Repository navigation
Description
Tier: 1
Project: cli
Type: feat
Objectives
O1. An Engine run started in shadow mode makes zero forge effects: across every scenario of the behavioural corpus, run in shadow mode with the Claude stub and with the Codex stub, the forge-fake ledger records no commit, push, pull-request open, published round or marked comment, while the same scenarios run without shadow mode still record their expected effects.
O2. Shadow mode is mechanically unable to mutate: every forge-writing function the Engine actions can call and every Developer publication tool is replaced by a recorder that refuses and records the attempt, and a test fails when a forge-writing function or a Developer tool exists that the shadow replacement does not classify.
O3. A shadow run still runs the real work around the effects: the Developer and both reviewers are dispatched, the gates run, and the run's recorded decision and its refused-attempt list are written to the shadow run's own runtime directory, never to the task's live runtime directory.
O4. The Engine runner script accepts a shadow flag that starts a shadow run of a real task beside its live run, with its own runtime directory and its own copy of the worktree, and refuses to start when that directory is the task's live runtime directory.
O5. The shadow-mode fault-and-attack row and cutover gate 9 name this evidence, and the exit check reports no owed Engine evidence for them.
Documentation
apps/cli/specs/dev-review-evaluation-program.md— §6.13 governed effects (shadow mode is mechanically unable to mutate; zero effects in shadow mode), §13 row "Shadow-mode mutation", §12 gate 9 (O1, O2, O5)processes/vinaya/executing/actions.ts—BoundFunctionsandACTION_BINDINGS, the forge-writing functions every Engine action reaches throughtask.bound(O2)apps/cli/src/lib/task-tools/dev-tools-server.ts—DevToolContext, the Developer's publication tools (O2)processes/vinaya/bin/run-engine.ts—EngineRunOptions(actions,handoffDeps,devToolsContext) andparseArgs(O3, O4)apps/cli/tests/conformance/dev-review-engine-loop-adapter.ts— the Engine scenario drivers and theforge.jsonleffect ledger they read (O1)
Planner's rationale
Boundary — Add a shadow mode to the Engine runner: one module classifies every forge-writing bound function and every Developer publication tool and builds refusing recorders for them; the runner script gains a shadow flag; a conformance test runs the whole corpus in shadow mode on both stub vendors and asserts a zero-effect ledger. It does NOT compare shadow results with standalone results beyond effects (difference classification is later canary work), does NOT add shadow mode to the standalone loop, and does NOT change any live effect path. Pinned files: processes/vinaya/executing/shadow.ts, processes/vinaya/bin/run-engine.ts, processes/vinaya/tests/shadow.test.ts, apps/cli/tests/conformance/dev-review-engine-loop-adapter.ts, apps/cli/tests/conformance/dev-review-engine-shadow.test.ts, apps/cli/tests/ci-shards/shard-3.txt, apps/cli/tests/eval/registry/shards/fault-attack.json, apps/cli/tests/eval/registry/shards/cutover-gates.json. Out: the CLI library is in the Surface only because the task-run Engine module imports the runner script, and it is not edited; the standalone loop, the effect executor, the Operator tools, every live forge writer.
Sizing — One verification story: in shadow mode the ledger stays empty and every attempted write is a recorded refusal. Passes the four too-big tests: one story, one agent can hold the classifier and one test, eight pinned files, one failure mode (an effect reached the ledger). Size M.
Project(s) + blast radius — Project: cli. The Vinaya process directory and the CLI's conformance tests change; no shared package changes. processes/vinaya has no registry row of its own and is covered by the repository-root row's surface.
Dependency rationale — Depends-on: #1643 — task 2 adds the per-backend evidence field to the registry rows and the exit check's owed-evidence list, which O5 fills for the shadow-mode row and gate 9. Shares apps/cli/tests/eval/registry/shards/fault-attack.json with #1643–#1647 and #1649; processes/vinaya/bin/run-engine.ts with #1645 and #1647; apps/cli/tests/eval/registry/shards/cutover-gates.json with #1643 and #1650; apps/cli/tests/conformance/dev-review-engine-loop-adapter.ts with #1649 and #1650; and apps/cli/tests/ci-shards/shard-3.txt with #1642 and #1649. Only #1649 reaches three shared files (fault-attack.json, the loop adapter, shard-3.txt), and #1649 declares Conflicts-with on this task; every other sibling shares two files at most, under the collision threshold.
Traps to avoid — Do NOT implement shadow mode as a flag the live actions check before writing; that leaves the live writer reachable. Build the shadow run from refusing recorders so no live forge writer is in the registry at all. Do NOT let an unclassified BoundFunctions key or DevToolContext method pass silently; the classification test must fail on a new key. Do NOT share the live run's runtime directory, lock or worktree; a shadow run beside a live run must not take its driver lock. Do NOT write Issue or pull-request numbers or tranche slugs into code comments. A new test file under apps/cli/tests needs its line in one shard file in the same commit.
reader-resolvable-prose: code comments, READMEs and specs carry no Issue or pull-request numbers and no tranche slugs.
doc-claims: every AEG:CLAIM line in a changed file is kept true in the same commit.
atta-labs/process-version-bump: the task leaves processes/vinaya/process.yaml, the flow file, the role files and processes/vinaya/executing/actions.ts unchanged, so the check passes as skipped; if actions.ts must change, process.yaml's version is raised in the same commit.
Suggested agent-class — high: the classifier must cover every write path, which needs judgment over many functions.
Stop-and-escalate — Stop if an Engine action writes to the forge through a function not reachable from task.bound, the handoff dependencies or the Developer tool context; name the action and the function. Stop if the corpus driver cannot inject the shadow task input without changing the scenario fixture; name the scenario.
Docs to keep coherent — apps/cli/specs/dev-review-evaluation-program.md (read only: §6.13); apps/cli/specs/loop.md (read only); no doc-owners binding fires for these paths (checked against .vinaya/doc-owners lines 48–53).
Premises
processes/vinaya/executing/actions.tscontainsexport interface BoundFunctions {processes/vinaya/executing/actions.tscontainsbound?: Partial<BoundFunctions>apps/cli/src/lib/task-tools/dev-tools-server.tscontainsexport type DevToolContext = {processes/vinaya/bin/run-engine.tscontainshandoffDeps?: HandoffDepsapps/cli/tests/conformance/dev-review-engine-loop-adapter.tscontainsforge.jsonlapps/cli/tests/eval/registry/shards/fault-attack.jsoncontainsfault-attack:29-shadow-mode-mutation
Surface
in: processes/vinaya, apps/cli/tests, apps/cli/src/lib
out: harness, packages, aeg-root
Parts
Part 1 (O2) — The shadow classification of every forge-writing bound function and Developer tool, and the refusing recorders built from it.
Part 2 (O3, O4) — The runner's shadow flag, its own runtime directory and worktree copy, and the refusal when it would reuse the live directory.
Part 3 (O1) — The corpus run in shadow mode on both stub vendors with a zero-effect ledger, beside the non-shadow run with its expected effects.
Part 4 (O5) — The shadow-mode row and gate 9 name this evidence.
Test plan
cd processes/vinaya && bun test --timeout=30000 tests/shadow.test.ts → 0 fail; every bound function and Developer tool is classified; an unclassified key fails
cd apps/cli && bun test --timeout=60000 tests/conformance/dev-review-engine-shadow.test.ts → 0 fail; every corpus scenario on claude and codex records zero ledger effects in shadow mode
cd apps/cli && bun test --timeout=30000 tests/eval/exit-check.test.ts tests/ci-shards.test.ts → 0 fail
bunx turbo run typecheck --affected → exits 0
[principal] On one real task in this repository, once with --agent claude and once with --agent codex, run the Engine runner with the shadow flag beside the live run: the forge shows no commit, push, comment or pull-request change from the shadow run, and its refused-attempt list is pasted in the pull request.
Stop conditions
- An Engine action writes to the forge through a function outside
task.bound, the handoff dependencies and the Developer tool context. - The corpus driver cannot inject the shadow task input without changing the scenario fixture.
Origin
Principal-directed, 2026-10-25: the Engine proof tranche under the Productionize developer-review on Atta Engine milestone proves mutation-safe shadow execution; no shadow code exists today.
Activity
daniboomerang commentedon Oct 25, 2026
Brief hash: 4db24833b3aac3a15c076f166797c1d204cce44ab5885bf799a00c393d61b91b
For: [model] (coding-agent CLI on a dev machine, dispatched locally, unattended)
Reason: Suggested agent-class — high: the classifier must cover every write path, which needs judgment over many functions.
Owner: the Principal
Goal: An Engine run in shadow mode does the real work and makes zero forge effects
Project: cli
Tier: 1
Closes #1648
You are the AEG Developer. Run
bun apps/cli/src/index.ts doctrine --role developer --printand read its output first. Mandatory.Objectives
O1. An Engine run started in shadow mode makes zero forge effects: across every scenario of the behavioural corpus, run in shadow mode with the Claude stub and with the Codex stub, the forge-fake ledger records no commit, push, pull-request open, published round or marked comment, while the same scenarios run without shadow mode still record their expected effects.
O2. Shadow mode is mechanically unable to mutate: every forge-writing function the Engine actions can call and every Developer publication tool is replaced by a recorder that refuses and records the attempt, and a test fails when a forge-writing function or a Developer tool exists that the shadow replacement does not classify.
O3. A shadow run still runs the real work around the effects: the Developer and both reviewers are dispatched, the gates run, and the run's recorded decision and its refused-attempt list are written to the shadow run's own runtime directory, never to the task's live runtime directory.
O4. The Engine runner script accepts a shadow flag that starts a shadow run of a real task beside its live run, with its own runtime directory and its own copy of the worktree, and refuses to start when that directory is the task's live runtime directory.
O5. The shadow-mode fault-and-attack row and cutover gate 9 name this evidence, and the exit check reports no owed Engine evidence for them.
Documentation
apps/cli/specs/dev-review-evaluation-program.md— §6.13 governed effects (shadow mode is mechanically unable to mutate; zero effects in shadow mode), §13 row "Shadow-mode mutation", §12 gate 9 (O1, O2, O5)processes/vinaya/executing/actions.ts—BoundFunctionsandACTION_BINDINGS, the forge-writing functions every Engine action reaches throughtask.bound(O2)apps/cli/src/lib/task-tools/dev-tools-server.ts—DevToolContext, the Developer's publication tools (O2)processes/vinaya/bin/run-engine.ts—EngineRunOptions(actions,handoffDeps,devToolsContext) andparseArgs(O3, O4)apps/cli/tests/conformance/dev-review-engine-loop-adapter.ts— the Engine scenario drivers and theforge.jsonleffect ledger they read (O1)Premises
Checked against the code when this task Issue was cut, and again against the default branch at dispatch — facts, not claims.
processes/vinaya/executing/actions.tscontainsexport interface BoundFunctions {processes/vinaya/executing/actions.tscontainsbound?: Partial<BoundFunctions>apps/cli/src/lib/task-tools/dev-tools-server.tscontainsexport type DevToolContext = {processes/vinaya/bin/run-engine.tscontainshandoffDeps?: HandoffDepsapps/cli/tests/conformance/dev-review-engine-loop-adapter.tscontainsforge.jsonlapps/cli/tests/eval/registry/shards/fault-attack.jsoncontainsfault-attack:29-shadow-mode-mutation2. Context — read before doing anything
developer-review-engine-proof-v1, task 7, Issue [developer-review-engine-proof-v1] 7 — An Engine run in shadow mode does the real work and makes zero forge effects #1648. Branchtask/developer-review-engine-proof-v1/7.Depends-on: #1643,Conflicts-with: —.8d5fc58a61e2d1daeed5aa58f48e7212bbedbb76— the checkout's HEAD equaled the remote default branch, and no pinned file below carried an uncommitted change, when these facts were read.processes/vinaya/executing/shadow.ts,processes/vinaya/bin/run-engine.ts,processes/vinaya/tests/shadow.test.ts,apps/cli/tests/conformance/dev-review-engine-loop-adapter.ts,apps/cli/tests/conformance/dev-review-engine-shadow.test.ts,apps/cli/tests/ci-shards/shard-3.txt,apps/cli/tests/eval/registry/shards/fault-attack.json,apps/cli/tests/eval/registry/shards/cutover-gates.json. Out: the CLI library is in the Surface only because the task-run Engine module imports the runner script, and it is not edited; the standalone loop, the effect executor, the Operator tools, every live forge writer.BoundFunctionskey orDevToolContextmethod pass silently; the classification test must fail on a new key. Do NOT share the live run's runtime directory, lock or worktree; a shadow run beside a live run must not take its driver lock. Do NOT write Issue or pull-request numbers or tranche slugs into code comments. A new test file underapps/cli/testsneeds its line in one shard file in the same commit.reader-resolvable-prose: code comments, READMEs and specs carry no Issue or pull-request numbers and no tranche slugs.doc-claims: every AEG:CLAIM line in a changed file is kept true in the same commit.atta-labs/process-version-bump: the task leavesprocesses/vinaya/process.yaml, the flow file, the role files andprocesses/vinaya/executing/actions.tsunchanged, so the check passes as skipped; ifactions.tsmust change,process.yaml'sversionis raised in the same commit.3. Technical dependencies
Dependency rationale —
Depends-on: #1643— task 2 adds the per-backend evidence field to the registry rows and the exit check's owed-evidence list, which O5 fills for the shadow-mode row and gate 9. Sharesapps/cli/tests/eval/registry/shards/fault-attack.jsonwith #1643–#1647 and #1649;processes/vinaya/bin/run-engine.tswith #1645 and #1647;apps/cli/tests/eval/registry/shards/cutover-gates.jsonwith #1643 and #1650;apps/cli/tests/conformance/dev-review-engine-loop-adapter.tswith #1649 and #1650; andapps/cli/tests/ci-shards/shard-3.txtwith #1642 and #1649. Only #1649 reaches three shared files (fault-attack.json, the loop adapter,shard-3.txt), and #1649 declaresConflicts-withon this task; every other sibling shares two files at most, under the collision threshold.4. Technical surface map
Create:
Modify:
Out of surface: harness, packages, aeg-root — a change there, or to any path no Surface
in:glob covers, is still published: the review-loop driver records each such path in the pull request body's beyond-Surface block, and the reviewers judge it like any change.Premise pins
Premise:
5. Pre-flight checks
Step 0 (mandatory, verbatim):
origin/main; branch suffix literal-matches the task id.bun apps/cli/src/index.ts task run), the driver has already run the dispatch-readiness gate plus, on this repository toolchain, its own fuller derivation from its own unsandboxed process before dispatching you (re-derived at render time: it was ready), and dispatches you only when that verdict is READY — a NOT READY verdict refuses the turn before it starts, so being dispatched is the verdict: look for no file and run neither script, whose ownghcall fails inside your sandbox. Working manually, with no driver behind you, fall back toroles/developer.md's own items 3/5/7 prose — each a complete, hand-run forge check in its own right.On any failure: STOP and report.
6. Numbered parts — publish once with
publish_changeswhen every Part is done, thenopen_pull_requestPart 1 (O2): The shadow classification of every forge-writing bound function and Developer tool, and the refusing recorders built from it.
Files (touches @attalabs/vinaya):
The pre-push hook runs the affected suite on your one push and refuses it on failure — do not run it yourself per Part.
Part 2 (O3, O4): The runner's shadow flag, its own runtime directory and worktree copy, and the refusal when it would reuse the live directory.
Files (touches the repo root):
The pre-push hook runs the affected suite on your one push and refuses it on failure — do not run it yourself per Part.
Part 3 (O1): The corpus run in shadow mode on both stub vendors with a zero-effect ledger, beside the non-shadow run with its expected effects.
Part 4 (O5): The shadow-mode row and gate 9 name this evidence.
7. Documentation-update list
.vinaya/doc-ownersderivation matched zero bindings against this task's surface — no mechanically-derived doc updates. Confirm no additional doc artifact applies before treating this list as final.8. Verification before claiming done
run_checksand CI provide the authoritative results. Do not hand-run or paste static-gate commands. Working manually, run this repository's declared typecheck, lint/format, build, and documentation-gate commands yourself.bun apps/cli/src/index.ts doctrine --role developer --printgenuinely satisfied; use the controller'srun_checksresult, hooks, and CI as gate evidence.9. Test Plan
10. Stop conditions
task.bound, the handoff dependencies and the Developer tool context.Stop-and-escalate — Stop if an Engine action writes to the forge through a function not reachable from
task.bound, the handoff dependencies or the Developer tool context; name the action and the function. Stop if the corpus driver cannot inject the shadow task input without changing the scenario fixture; name the scenario.11. Constraints
12. Deliverable
[developer-review-engine-proof-v1] 7 — An Engine run in shadow mode does the real work and makes zero forge effectsopen_pull_requesttool (title and body); publish your commits withpublish_changesand update the body withupdate_pull_request_body/refresh_evidence— you hold nogh/git pushcredential.publish_changes, when every Part is done; then open the pull request withopen_pull_request. Publish again only to answer review findings or a red gate — a refused hook, a failing check, a merge conflict.bun apps/cli/src/index.ts doctrine --template pr-report --print), withCloses #1648at the top of the header block. This brief stays frozen on the task Issue and never rides in the body.run_checksand CI run it.git diff origin/main...HEAD --statas context only; Surface is judged by the driver'ssurface-scopecheck, never by this hand-run diff. Token use needs no report — the Vinaya log records it.