- Fix MEDIUM: totalCount in dagNodes live-state path now counts only terminal nodes
(completed/failed/skipped), matching the semantics of the events fallback path.
Previously included pending/running nodes, producing a misleading denominator
during page-refresh mid-execution.
- Fix LOW: Duration badge background changed from bg-surface-elevated to bg-surface
for visual contrast against the bg-surface-elevated parent header.
- Fix LOW: Expanded staleTime: Infinity comment to explain the immutability invariant.
- Fix LOW: Expanded node count comment to describe that the events-path totalCount
is an approximation (nodes that reached terminal state, not workflow's full node count).
- Fix LOW: Added Workflow Result Card subsection to web adapter docs describing the
post-completion card's status icon, header, node count, duration, and artifacts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Loop nodes in DAG workflows appeared as flat nodes with no iteration visibility.
The backend already emitted loop_iteration_* events but the frontend dropped them.
Changes:
- Add nodeId to workflow_step SSE events in workflow-bridge
- Add LoopIterationEvent/LoopIterationInfo types and extend DagNodeState
- Handle workflow_step events in useSSE and route to store
- Add handleLoopIteration action to workflow store
- Extract loop_iteration_* events for historical REST view
- Add expandable iteration sub-list in DagNodeProgress sidebar
- Add iteration count badge on graph nodes (ExecutionDagNode)
- Add loop type color/label to graph node type maps
Fixes#1014
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The WorkflowResultCard showed a hardcoded green checkmark regardless of
actual outcome, with no duration, node count, or artifact links.
Changes:
- Fetch terminal run data via TanStack Query (staleTime: Infinity)
- Merge Zustand live state with API fallback for offline/CLI workflows
- Render StatusIcon for completed/failed/cancelled status awareness
- Display node count and duration pill in the header
- Show ArtifactSummary (PRs, commits, branches, files) above text content
- Derive node counts and artifacts from events when live state unavailable
Fixes#1015
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The reject command only read `--reason` flag but ignored positional
arguments. Running `bun run cli workflow reject <id> "feedback"` passed
undefined to rejectWorkflow(), defaulting to "Rejected". Now mirrors
the approve command pattern: reads both flag and positional args.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On Windows, path.join() produces backslashes which caused 3 test
failures in script-discovery on the Windows CI runner.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Investigate issues #1001, #1002, #1003: interactive-prd workflow bugs
* fix: replace Promise.any with Promise.all in validator script check (#1007)
Promise.any resolves with the first fulfilled promise regardless of value.
Since fileExists always fulfills (never rejects), Promise.any could return
false even when a later extension check would return true — a race condition
that flaked on Windows CI.
Fixes#1007
The inline comment stated ports 3100-3999 (implying basePort=3000) but the
actual basePort is 3090, making the true range 3190-4089, which already
matched the JSDoc. Update the inline comment to agree with both the JSDoc
and the arithmetic.
Closes#1008
* Investigate issues #1001, #1002, #1003: interactive-prd workflow bugs
* fix: interactive-prd workflow — capture responses, fix output path, reuse conversations (#1001, #1002, #1003)
The archon-interactive-prd workflow was completely non-functional due to three bugs:
approval gates didn't capture user responses (missing capture_response: true),
the generate node wrote to .claude/ which the Claude SDK blocks, and CLI
approve/reject created new conversations instead of reusing the original.
- Add capture_response: true to all three approval gates in the YAML
- Change output path from .claude/PRPs/prds/ to $ARTIFACTS_DIR/prds/
- Add conversationId to ApprovalOperationResult and RejectionOperationResult
- Look up and pass through original conversation ID in CLI approve/reject commands
- Add getConversationById mock to CLI workflow tests
Fixes#1001, #1002, #1003
* fix: guard conversation lookups, add tests, document approval node type
- Wrap getConversationById calls in try-catch in CLI approve/reject
commands to prevent crashes when DB errors occur after approval is
already recorded (falls back to new conversation ID)
- Log when conversation lookup fails or returns null for observability
- Add happy-path tests verifying platform conversation ID is passed
through in both approve and reject commands
- Add JSDoc comments clarifying conversationId is a DB UUID (not
platform ID) in operation result interfaces and WorkflowRunOptions
- Document approval: node type in CLAUDE.md DAG node types list
* feat: add ScriptNode schema and type guards (US-001)
Implements US-001 from the script-nodes PRD.
Changes:
- Add scriptNodeSchema with script, runtime (bun|uv), deps, and timeout fields
- Add ScriptNode type with never fields for mutual exclusivity
- Add isScriptNode type guard
- Add SCRIPT_NODE_AI_FIELDS constant (same as BASH_NODE_AI_FIELDS)
- Update dagNodeSchema superRefine and transform to handle script: nodes
- Update DagNode union type to include ScriptNode
- Add script node dispatch stub in dag-executor.ts (fails fast until US-003)
- Export all new types and values from schemas/index.ts
- Add comprehensive schema tests for ScriptNode parsing and validation
* feat: script discovery from .archon/scripts/
Implements US-002 from PRD.
Changes:
- Add ScriptDefinition type and discoverScripts() in script-discovery.ts
- Auto-detect runtime from file extension (.ts/.js->bun, .py->uv)
- Handle duplicate script name conflicts across extensions
- Add bundled defaults infrastructure (empty) for scripts
- Add tests for discovery, naming, and runtime detection
* feat: script execution engine (inline + named)
Implements US-003 from PRD.
Changes:
- Add executeScriptNode() in dag-executor.ts following executeBashNode pattern
- Support inline bun (-e) and uv (run python -c) execution
- Support named scripts via bun run / uv run
- Wire ScriptNode dispatch replacing 'not yet implemented' stub
- Capture stdout as node output, stderr as warning
- Handle timeout and non-zero exit
- Pass env vars for variable substitution
- Add tests for inline/named/timeout/failure cases
* feat: runtime availability validation at load time
Implements US-004 from PRD.
Changes:
- Add checkRuntimeAvailable() utility for bun/uv binary detection
- Extend validator.ts with script file and runtime validation
- Integrate script validation into parseWorkflow flow in loader.ts
- Add tests for runtime availability detection
* feat: dependency installation for script nodes
Implements US-005 from PRD.
Changes:
- Support deps field for uv nodes: uvx --with dep1... for inline
- Support uv run --with dep1... for named uv scripts
- Bun deps are auto-installed at runtime via bun's native mechanism
- Empty/omitted deps field produces no extra flags
- Add tests for dep injection into both runtimes
* test: integration tests and validation for script nodes
Implements US-006 from PRD.
Changes:
- Fill test coverage gaps for script node feature
- Add script + command mutual exclusivity schema test
- Add env var substitution tests ($WORKFLOW_ID, $ARTIFACTS_DIR in scripts)
- Add stderr handling test (stderr sent to user as platform message)
- Add missing named script file validation tests to validator.test.ts
- Full bun run validate passes
* fix: address review findings in script nodes
- Extract isInlineScript to executor-shared.ts (was duplicated in
dag-executor.ts and validator.ts)
- Remove dead warnMissingScriptRuntimes from loader.ts (validator
already covers runtime checks)
- Remove path traversal fallback in executeScriptNode — error when
named script not found instead of executing arbitrary file paths
- Memoize checkRuntimeAvailable to avoid repeated subprocess spawns
- Add min(1) to scriptNodeSchema.script field for consistency
- Replace dynamic import with static import in validator.ts
* fix(workflows): address review findings for script node implementation
Critical fixes:
- Wrap discoverScripts() in try-catch inside executeScriptNode to prevent
unhandled rejections when script discovery fails (e.g. duplicate names)
- Add isScriptNode to isNonAiNode check in loader.ts so AI-specific fields
on script nodes emit warnings (activates SCRIPT_NODE_AI_FIELDS)
Important fixes:
- Surface script stderr in user-facing error messages on non-zero exit
- Replace uvx with uv run --with for inline uv scripts with deps
- Add z.string().min(1) validation on deps array items
- Remove unused ScriptDefinition.content field and readFile I/O
- Add logging in discoverAvailableScripts catch block
- Warn when deps is specified with bun runtime (silently ignored)
Simplifications:
- Merge BASH_DEFAULT_TIMEOUT and SCRIPT_DEFAULT_TIMEOUT into single
SUBPROCESS_DEFAULT_TIMEOUT constant
- Use scriptDef.runtime instead of re-deriving from extname()
- Extract shared formatValidationResult helper, deduplicate section comments
Tests:
- Add isInlineScript unit tests to executor-shared.test.ts
- Add named-script-not-found executor test to dag-executor.test.ts
- Update deps tests to expect uv instead of uvx
Docs:
- Add script: node type to CLAUDE.md node types and directory structure
- Add script: to .claude/rules/workflows.md DAG Node Types section
On CI (Ubuntu), fetch to gitlab.example.com resolves to a real IP and the
TCP connection hangs, causing the "detects mention at end of string" test
to time out after 5s. Locally on Windows, DNS fails fast so it passes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The pre-spawn env-leak guard in ClaudeClient.sendQuery() and
CodexClient.sendQuery() used `if (!codebase?.allow_env_keys)`, which
evaluates truthy when `codebase` is null. Any sendQuery call with an
unregistered cwd (title generation, codebase-less orchestrator runs,
DAG executor paths) ran the sensitive-key scanner and threw
EnvLeakError, blocking every conversation creation on deployed servers
with a .env in scope.
The pre-spawn check is defense-in-depth for registered codebases
without explicit consent. Registration (registerRepoAtPath) is the
canonical gate; unregistered cwd paths are out of scope.
Changes:
- claude.ts: tighten predicate to `codebase && !codebase.allow_env_keys`
- codex.ts: same fix
- claude.test.ts: add regression test for unregistered cwd; update
existing tests that relied on the null-codebase path to use a
registered codebase with allow_env_keys: false
- codex.test.ts: same test updates
Fixes#991
The Claude Agent SDK falls back to resolving its cli.js via
import.meta.url when pathToClaudeCodeExecutable is not set. In
bun build --compile binaries, import.meta.url of a bundled module
is frozen at build time to the build host's absolute node_modules
path — so every binary shipped from CI carried a
/Users/runner/work/Archon/Archon/node_modules/.bun/... path that
only existed on the GitHub Actions runner, and every workflow run
failed with 'Module not found' after three retries.
The SDK ships a dedicated /embed entry point for exactly this case:
it uses 'import cli.js with { type: "file" }' so bun embeds cli.js
into the compiled binary's $bunfs virtual filesystem, then extracts
it to a real temp path at runtime so the subprocess can exec it.
Verified by building a local binary (scripts/build-binaries.sh) and
running 'workflow run assist' — the binary now spawns Claude
successfully where v0.3.1 fails before the first token.
This bug has been latent since bun build --compile was first wired
up; it surfaced in v0.3.1 because that was the first release where
the homebrew formula SHAs were correct and a user could actually
install the binary.
PR #983 added the allow_env_keys consent bit via PostgreSQL migrations
(migrations/000_combined.sql and migrations/021_*.sql) but did not
update packages/core/src/db/adapters/sqlite.ts, which has its own
independent schema bootstrap path. Result: every SQLite database has
been broken since #983 landed:
- Fresh installs: createSchema() creates remote_agent_codebases
without the column, and POST /api/codebases fails on every call
with "table remote_agent_codebases has no column named allow_env_keys".
- Existing installs upgraded from v0.2.x: CREATE TABLE IF NOT EXISTS
is a no-op on the existing table and migrateColumns() never adds
the column, same failure.
Cole's deployed server at archon-youtube.smartcode.diy hit this live —
every "add project" request returned 500 because the VPS runs
docker-compose with the SQLite default (no separate postgres service).
Two surgical changes to packages/core/src/db/adapters/sqlite.ts:
1. createSchema(): add `allow_env_keys INTEGER DEFAULT 0` to the
remote_agent_codebases CREATE TABLE block so fresh databases get
the column. SQLite has no true BOOLEAN — INTEGER with 0/1 matches
the existing pattern used for `hidden` on conversations.
2. migrateColumns(): add a new idempotent try/catch block that
PRAGMA-checks the codebases table for `allow_env_keys` and ALTERs
it in if missing. Pattern matches the existing migration blocks
for Conversations, Workflow runs, and Sessions columns.
The JavaScript read path in db/codebases.ts and the clients already
uses truthy checks (`if (!codebase?.allow_env_keys)`), which works
for both SQLite integer (0/1) and JS boolean (false/true) storage.
No other changes needed.
Fixes the live incident blocking Cole's demo and unblocks v0.3.1.
* Investigate issue #986: release workflow bypasses build-binaries.sh
* fix(release): wire release workflow to scripts/build-binaries.sh (#986)
The release workflow called `bun build --compile` inline, bypassing the
build-time-constants rewrite in scripts/build-binaries.sh. Released binaries
shipped with BUNDLED_IS_BINARY=false, causing `archon version` to crash with
"package.json not found (bad installation?)" on v0.2.13 and v0.3.0.
Changes:
- Refactor scripts/build-binaries.sh to support single-target mode via
TARGET/OUTFILE env vars; preserve multi-target local-dev mode unchanged.
Always --minify; skip --bytecode for Windows targets.
- Update .github/workflows/release.yml to call the script with the matrix
target/outfile, stripping the 'v' prefix and shortening the SHA.
- Add a post-build smoke test on bun-linux-x64 that asserts the binary
reports "Build: binary" and the tag version (would have caught both
broken releases).
- Document local CI-equivalent build invocation in the test-release skill.
Fixes#986
* chore: archive investigation for issue #986
* skill(release): document Homebrew formula SHA sync and verification
The release skill previously stopped at tag creation and GitHub release
creation. Formula updates were happening manually outside the skill and
consistently drifting — v0.3.0's homebrew/archon.rb had the correct
version string but SHAs from v0.2.13, because whoever updated it did so
before the release workflow had built the v0.3.0 binaries.
Add three new steps to close the gap:
- Step 10: wait for release workflow, fetch checksums.txt, update
homebrew/archon.rb atomically with new version AND new SHAs in a
single commit. The formula is regenerated from a template rather
than edited in place, eliminating the risk of partial updates.
- Step 11: sync the rewritten formula to coleam00/homebrew-archon
tap repo (the file users actually install from). Fails loudly if
push access is missing instead of silently skipping.
- Step 12: run /test-release brew and /test-release curl-mac to
verify the install path actually works end-to-end before announcing
the release. A release that installs but crashes is worse than no
release at all.
Also:
- Add a prominent warning at the top about the chicken-and-egg
relationship between version and SHAs (they must move atomically,
and SHAs can only be known after binaries exist).
- Add three new rules to "Important Rules":
* never update version without also updating SHAs
* never skip the tap sync (main repo formula is just a template)
* never announce a release that failed /test-release
Related to #986 (release workflow bypasses build-binaries.sh) — both
bugs block the next working release; fixing only one leaves the
install path broken.
* fix(release): address review feedback on smoke test and restore trap
- release.yml: use inputs.version on workflow_dispatch so the build step
doesn't embed the branch name as the binary version
- release.yml: compare smoke-test version against the stripped semver
instead of the raw ref, so the check doesn't rely on the CLI re-adding
a 'v' prefix
- release.yml: fail fast if the binary crashes on first invocation
instead of falling through to the 'wrong build type' branch
- release.yml: add a second smoke step that runs 'workflow list' in a
temp repo to catch the class of bug where bundled defaults fail to
embed in the binary
- build-binaries.sh: drop '2>/dev/null' on the EXIT trap so restore
failures surface in the log with a clear WARNING
- test-release skill: fix the single-target verification path
Automates release verification via three install paths: brew (Homebrew
tap), curl-mac (install.sh to a sandboxed tmp dir on macOS), and
curl-vps (install.sh on a remote Linux VPS). Runs a fixed suite
(version, workflow list, assist workflow, env-leak gate, isolation
list), captures SHA256 of the tested binary, and cleans up on exit
so the dev bun-link binary is never disturbed.
Use when cutting a new release or reproducing a user bug report on
the released version. NOT for testing dev changes — those use
bun run validate or direct source invocation.
See issue (to be filed) for the release workflow fix that unblocks
the brew and curl-mac paths end-to-end.
* feat(env-leak-gate): polish sweep — CLI flag, config bypass, retroactive consent (#973)
Closes the UX gaps left over from #1036:
- CLI: --allow-env-keys flag for `archon workflow run`
- Config: global `allow_target_repo_keys` in ~/.archon/config.yaml with
per-repo `.archon/config.yaml` override; warn log on every load when active
- API: PATCH /api/codebases/:id flips allow_env_keys with audit logs on grant
and revoke
- Web: Settings → Projects per-row "Allow/Revoke env keys" toggle + badge
- Errors: context-aware messages (register-ui / register-cli / spawn-existing)
- Startup: server scans registered codebases and warns for any that would be
blocked at next spawn
- Docs + CHANGELOG entries describing the four remediation paths
* fix(env-leak-gate): address review feedback from PR #983
Critical:
- Move loadConfig() before startup env-leak scan and skip the scan
entirely when allow_target_repo_keys is true. Previously the scan
spammed warnings on every boot for users who had set the global bypass.
Important:
- SettingsPage: add onError to toggleEnvKeysMutation so PATCH failures
surface to the user instead of being silently swallowed.
- env_leak_gate_disabled now logs at most once per process per source
(global vs repo) instead of firing on every loadConfig() call.
- PATCH /api/codebases/:id audit log includes scanStatus ('ok' | 'skipped')
so reviewers can distinguish "scanned and empty" from "scan failed".
- claude.ts / codex.ts pre-spawn loadConfig wrapped in try/catch — config
load failures now log and fail-closed instead of escaping as opaque errors.
- CLI --allow-env-keys grants now emit env_leak_consent_granted audit log
in registerRepoAtPath with actor: 'user-cli'. Previously only the PATCH
route logged grants, leaving CLI grants without an audit trail.
- printUsage() now lists --allow-env-keys.
- Outer startup scan catch promoted from warn to error level.
Polish:
- formatLeakError consent copy moved to an exhaustive switch with a never
default — adding a new LeakErrorContext variant is now a compile error.
- Comment / event-name normalization: 'migration_env_leak_gate_*' →
'startup_env_leak_*' to match the actual lifecycle (every boot, not
one-time migration).
Docs:
- security.md: corrected "at startup" wording, documented scanStatus
field, clarified scan-skipped-on-bypass behavior.
- api.md: added PATCH /api/codebases/{id} entry and example.
- cli.md: added --allow-env-keys row.
- configuration.md: added allow_target_repo_keys to global + repo examples.
- CLAUDE.md: added Codebases endpoint section and CLI example.
- CHANGELOG: corrected "at startup" wording, added scanStatus and CLI
audit-log notes.
Tests:
- claude.test.ts: added bypass test (allowTargetRepoKeys: true short-
circuits scanner) and fail-closed test (loadConfig throws → scanner
still runs).
- clone.test.ts: updated allowEnvKeys=true expectation — scanner is now
called for the audit-log payload but does not throw.
* deploy: harden cloud-init with archon user, swap, and fixes
- Create dedicated 'archon' user (sudo + docker groups, passwordless
sudo, locked password) and copy SSH authorized_keys from default
cloud user (with root fallback) so login works immediately.
- Run docker pulls and the image build as the archon user via sudo -u.
- Add 2GB swapfile to prevent OOM during docker build on small VPS
(<2GB RAM).
- Remove package_upgrade to speed up boot and avoid surprise kernel
updates.
- Drop redundant systemctl enable/start docker (get.docker.com handles
it).
- ufw allow 443/tcp for consistency with 22/80.
- set -e before clone for fail-fast on network errors.
- Update docs link to https://archon.diy/deployment/docker/.
- SETUP_COMPLETE now instructs ssh archon@<server-ip>.
- Header lists supported providers (incl. Hostinger) and notes the
archon user + swap behavior.
* deploy: address PR review feedback on cloud-init
- Fix set -e regression: merge clone/cp/chown into single shell block
so fail-fast actually applies (CodeRabbit).
- Drop passwordless sudo from archon user — docker group only. Removes
trivial privilege escalation path (Wirasm).
- Remove non-existent 'docker' group from initial users.groups list;
it is added via usermod later (CodeRabbit).
- Restore package_upgrade: true to patch CVEs in the base image before
anything else runs (Wirasm).
- Add ufw allow 443/udp for HTTP/3 QUIC — Caddy exposes 443:443/udp in
docker-compose (CodeRabbit).
- Update SETUP_COMPLETE and header comment to note archon user has no
sudo (use default cloud user / root for maintenance).
* deploy: harden cloud-init with archon user, swap, and fixes
- Create dedicated 'archon' user (sudo + docker groups, passwordless
sudo, locked password) and copy SSH authorized_keys from default
cloud user (with root fallback) so login works immediately.
- Run docker pulls and the image build as the archon user via sudo -u.
- Add 2GB swapfile to prevent OOM during docker build on small VPS
(<2GB RAM).
- Remove package_upgrade to speed up boot and avoid surprise kernel
updates.
- Drop redundant systemctl enable/start docker (get.docker.com handles
it).
- ufw allow 443/tcp for consistency with 22/80.
- set -e before clone for fail-fast on network errors.
- Update docs link to https://archon.diy/deployment/docker/.
- SETUP_COMPLETE now instructs ssh archon@<server-ip>.
- Header lists supported providers (incl. Hostinger) and notes the
archon user + swap behavior.
* deploy: address PR review feedback on cloud-init
- Fix set -e regression: merge clone/cp/chown into single shell block
so fail-fast actually applies (CodeRabbit).
- Drop passwordless sudo from archon user — docker group only. Removes
trivial privilege escalation path (Wirasm).
- Remove non-existent 'docker' group from initial users.groups list;
it is added via usermod later (CodeRabbit).
- Restore package_upgrade: true to patch CVEs in the base image before
anything else runs (Wirasm).
- Add ufw allow 443/udp for HTTP/3 QUIC — Caddy exposes 443:443/udp in
docker-compose (CodeRabbit).
- Update SETUP_COMPLETE and header comment to note archon user has no
sudo (use default cloud user / root for maintenance).
* fix(build): use build-time constants for binary detection and pretty stream logger
Replaces runtime detection of compiled binaries (env sniffing via
import.meta.dir / process.execPath) with a build-time BUNDLED_IS_BINARY
constant in @archon/paths/bundled-build.ts, rewritten by
scripts/build-binaries.sh and restored on EXIT via a trap.
Also rewrites @archon/paths/logger.ts to use pino-pretty as a destination
stream instead of a worker-thread transport. The formatter now runs on
the main thread, eliminating the require.resolve('pino-pretty') lookup
that crashes inside Bun's /\$bunfs/ virtual filesystem in compiled
binaries. The same code path runs in dev and binaries — no environment
detection in the logger at all.
isBinaryBuild() in @archon/workflows is kept as a one-line wrapper
around BUNDLED_IS_BINARY so existing spyOn-based test mocking in
loader.test.ts continues to work without modification.
Closes#960Closes#961Closes#979
Supersedes #962
Supersedes #963
Co-Authored-By: leex279 <leex279@users.noreply.github.com>
* style(workflows): hoist BUNDLED_IS_BINARY import to top of file
* fix(build,logger): harden pretty init and trap restore
- logger: wrap pino-pretty init in try/catch and fall back to JSON so a
broken TTY or missing peer can't crash module load.
- build-binaries.sh: drop '2>/dev/null || true' from the EXIT trap so a
failed bundled-build.ts restore is visible instead of silently leaving
the dev tree with BUNDLED_IS_BINARY=true.
- bundled-defaults: unmark isBinaryBuild() @deprecated and document why
the wrapper is the intentional test seam (mock.module pollution in Bun).
---------
Co-authored-by: leex279 <leex279@users.noreply.github.com>
* Fix: detect squash-merged and PR-merged branches in isolation cleanup (#1026)
`isolation cleanup --merged` only used `git branch --merged`, which misses
squash-merged branches because the resulting commit has a different SHA.
Bulk cleanup of task worktrees required a manual `gh pr list` per branch.
Changes:
- Add `isPatchEquivalent()` to `@archon/git` using `git cherry` to detect
squash-merged branches
- Add `getPrState()` to `@archon/isolation` for `gh`-based PR state lookup
with per-invocation caching; soft-fails on missing gh / non-GitHub remotes
- `cleanupMergedWorktrees()` now unions three signals (ancestry, patch
equivalence, PR state); skips with a clear reason when PR is OPEN
- Add `--include-closed` flag to `archon isolation cleanup --merged` to
also remove worktrees whose PRs were closed without merging
- Tests for all new code paths
Fixes#1026
* fix: address review findings for squash-merge cleanup PR
- branch.ts: add 'bad revision' to isPatchEquivalent expected errors
so manually-deleted branches return false instead of throwing
- pr-state.ts: add repoPath context to warn/debug log calls;
capture ghStdout before try block to include in warn log for
parse failures
- pr-state.test.ts: remove redundant beforeEach reset (setupGhResponse
already resets); add tests for non-ENOENT gh error and malformed JSON
- cleanup-service.test.ts: add test for isPatchEquivalent unexpected
throw → skipped with 'merge check failed' reason
- isolation-operations.test.ts: add test for includeClosed: true
forwarding through cleanupMergedEnvironments
- docs: add --include-closed to all five affected docs (CLAUDE.md,
reference/cli.md, book/isolation.md, book/quick-reference.md,
getting-started/overview.md)
- cli-internals.md: add isolation cleanup --merged flow diagram
* simplify: remove redundant assignments and verbose filter in new code
* feat(cli): embed git commit hash in version output
- Add BUNDLED_GIT_COMMIT constant to bundled-version.ts for binary builds
- Read commit at runtime via git rev-parse (dev) or from bundled constant (binary)
- Display Git commit: line in archon version output; falls back to "unknown"
- Update build-binaries.sh to capture and embed short SHA at compile time
- Update version tests to assert new Git commit: output line
* fix: use @archon/git execFileAsync and add error logging in version command
- Replace local child_process/promisify wrapper with execFileAsync from @archon/git
(fixes CLAUDE.md violation; enables test mockability via spyOn)
- Add 5s timeout to git rev-parse subprocess call to prevent indefinite hang
- Log debug trace in catch block with comment explaining intentional fallback
- Import createLogger from @archon/paths for structured logging
- Update version.test.ts to spy on @archon/git execFileAsync for deterministic SHA
- Add test case covering the git-unavailable fallback path (returns 'unknown')
- Tighten git commit assertion to match exact value instead of label presence only
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Archon <archon@dynamous.ai>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(sse): extend buffer TTL beyond reconnect grace to prevent dropped tool_results
The SSE event buffer held events for only 3s, but the conversation
reconnect grace period is 5s — meaning events emitted during a
reconnect window could expire *before* the client even had a chance
to reconnect. When a tool_result happened to land in that gap, the
UI would show a perpetually spinning tool card with no recovery path.
This is one of the remaining causes from the 'tool cards stuck
running' investigation. The two biggest causes (Claude hook coverage
and Codex tool_result emission) were already fixed in #1031 and #1032.
This closes the last high-impact backend gap.
Changes:
- EVENT_BUFFER_TTL_MS: 3_000 → 60_000. Covers typical EventSource
auto-reconnect delays on flaky networks (mobile, VPN, laptop sleep).
- EVENT_BUFFER_MAX: 50 → 500. Events are small JSON strings; 500
bounds worst-case memory while giving real headroom for bursts.
- Warn when buffer cap evicts oldest (previously silent).
- Warn when events expire on TTL at replay time (previously silent).
Both warnings give us observability if the new bounds are still
ever insufficient.
Note: a full Last-Event-ID resume protocol would be more principled
but requires monotonic event IDs and client-side offset tracking —
a larger change with its own risks. The TTL bump alone closes the
vast majority of the window at near-zero cost.
* fix(sse): throttle eviction warns, reset cleanup timer, enforce TTL invariant
Address review feedback on the SSE buffer TTL bump:
- Reset the buffer cleanup timer on each new event so the buffer is held
for TTL past the most recent event, not the first one. With the 20x TTL
bump this gap became meaningful — a fresh event could be wiped by a
cleanup timer scheduled when the first (now-stale) event was buffered.
- Throttle 'transport.buffer_evicted_oldest' warns to one per conversation
per 5s. A runaway producer overflowing the cap by hundreds would
otherwise flood logs.
- Fail-fast at module load if EVENT_BUFFER_TTL_MS < RECONNECT_GRACE_MS.
Locks in the invariant the comment already documents.
- Add test covering the eviction-warn throttle.
* fix(env): detect and refuse target-repo .env with sensitive keys (#1034)
Bun auto-loads .env from subprocess CWD regardless of the clean env
passed to Bun.spawn, silently overriding OAuth auth and billing the
wrong API account. This adds a consent-based gate at registration time
and a pre-spawn safety net in both Claude and Codex clients.
Changes:
- Add env-leak-scanner utility that checks 6 auto-loaded .env filenames
for 7 sensitive keys (ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.)
- Add allow_env_keys boolean column to codebases table (migration 021)
- Gate registerRepoAtPath to reject codebases with sensitive .env keys
unless explicitly consented via allowEnvKeys flag
- Add pre-spawn check in ClaudeClient and CodexClient sendQuery methods
- Return 422 from POST /api/codebases on env leak detection
- Surface env leak error in web UI with "Allow env keys" checkbox
- Classify EnvLeakError as FATAL in workflow executor
Fixes#1034
* fix: address review findings for env leak scanner PR
- Fix FATAL_PATTERNS 'env leak' pattern that never matched EnvLeakError.message;
now checks error.name === 'EnvLeakError' directly (immune to message rewording)
- Fix pre-spawn consent lookup for worktree paths: add findCodebaseByPathPrefix()
and use it as fallback when exact match returns null; prevents opt-in from
being silently ineffective for workflow-based runs
- Add allow_env_keys column to 000_combined.sql CREATE TABLE and idempotent ALTER
section to fix fresh PostgreSQL installs
- Remove non-existent --allow-env-keys CLI flag from error message; replace with
web UI-only instruction
- Narrow isEnvLeakError check from error.message.includes('env') to startsWith('Cannot add codebase')
- Distinguish ENOENT (skip) from EACCES/other errors in scanner catch block;
unreadable files now surface as findings to avoid silently bypassing the gate
- Use cross-platform grep command instead of macOS-specific sed -i '' syntax
- Add audit log (log.warn) when 422 EnvLeakError is returned from API
- Add pre-spawn gate tests to claude.test.ts and codex.test.ts (4 tests each)
- Add env leak gate tests to clone.test.ts (2 tests)
- Add 422 and allowEnvKeys passthrough tests to api.codebases.test.ts
* simplify: reduce complexity in changed files
* fix(web): allow workflow graph view to load without a codebase (#958)
The useQuery for workflow definitions required both workflowName and
codebaseCwd to be truthy. For CLI-triggered runs or "No project" web
runs where codebase_id is null, codebaseCwd stays undefined and the
query never fires — showing "Loading graph..." forever. The server
already handles missing cwd by falling back to bundled defaults, so
the client gate only needs workflowName.
Fixes#958
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(web): clarify server fallback chain comment for absent codebaseCwd
Update inline comment to accurately describe the two-step server fallback:
when cwd is absent the server first tries the first registered codebase
before falling back to bundled defaults. The previous comment skipped the
intermediate step, which could confuse developers debugging unexpected
workflow resolution for "No project" runs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update inline comment to accurately describe the two-step server fallback:
when cwd is absent the server first tries the first registered codebase
before falling back to bundled defaults. The previous comment skipped the
intermediate step, which could confuse developers debugging unexpected
workflow resolution for "No project" runs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The useQuery for workflow definitions required both workflowName and
codebaseCwd to be truthy. For CLI-triggered runs or "No project" web
runs where codebase_id is null, codebaseCwd stays undefined and the
query never fires — showing "Loading graph..." forever. The server
already handles missing cwd by falling back to bundled defaults, so
the client gate only needs workflowName.
Fixes#958
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Windows symlinks require Developer Mode or admin rights. Junctions
work without special permissions and behave identically for directories.
Use 'junction' type on win32, 'dir' on other platforms.
Also bump @archon/cli version from 0.2.0 to 0.2.13 to match root.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Shared "Start Using Archon" section after both setup paths
- Remove duplicated usage instructions from each path
- Full Setup ends at the wizard, Quick Install ends at the binary
- Both converge to "go to your project, open Claude Code, start working"
- Better intro line for the workflow example
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
What It Looks Like: Replace flat command-list workflow with one that
shows all differentiators - AI loops, deterministic bash nodes, human
approval gates, fresh context. Show usage as a Claude Code conversation
instead of a CLI command, since that's how most people will use Archon.
Getting Started: Lead with Full Setup (guided wizard) as the recommended
path. Move Quick Install (CLI binary) below as the power-user shortcut.
Add decision-helper callout so users know which path to take.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add favicon.png to docs public dir and configure in Starlight.
Also add logo to src/assets for future sidebar use.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reorganize README flow: intro → why → what it looks like → previous
version → quickstart with two options.
Option A: CLI binary install (30 seconds, for terminal-first users).
Option B: Full setup with guided wizard (5 min, for web UI and
platform integrations).
Removes the separate Install section that overlapped with Quickstart.
Moves Web UI, workflows, architecture, and platform sections after
the quickstart so users get running before reading reference material.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Archon was #1 trending repo on GitHub. Add the Trendshift badge
between the tagline and the status badges.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add logo to assets/ and update README with centered header,
tagline, and badges for a professional open-source presentation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>