Skip to content

[developer-review-engine-proof-v1] 6 — Run-control and governed-effects faults end in their declared outcome on the Engine runner under both vendors #1647

Closed

Description

@daniboomerang

Tier: 1
Project: cli
Type: test

Objectives

O1. Every run-control fault-and-attack row this tranche owns (controller process death, provider rate or usage limit, version incompatibility on resume, checkpoint corruption, cancellation races) and every governed-effects row (forge latency, duplicate delivery, remote success with the local acknowledgement lost, a forged or stale beyond-Surface record) has a fault-injection scenario that runs the Engine runner with stub Claude Code and stub Codex vendors and asserts the row's declared outcome.
O2. A controller killed by signal at each checkpoint boundary and at each effect boundary of an Engine run, then restarted, resumes from its last checkpoint, repeats no completed step and produces each effect at most once; the scenario runs a real runner subprocess and kills it, once per vendor stub.
O3. Every Must-pass row in O1 holds on the Engine runner under both vendor stubs, with no known-failure entry; a row that fails names the row in the failure message.
O4. Every checkpoint an Engine run writes records the checkpoint format version and the Engine runtime version beside the code revision and model it records today; resuming a checkpoint whose recorded format or runtime version is incompatible with the running one, or that records neither, is refused with a typed reason and runs no step; resuming after only the code revision moved still continues, as it does today.
O5. An architecture test lists these rows and fails while any of them has no recorded Engine evidence; its list of rows still owed shrinks only, and the count it prints is zero when this task merges. The consumed-ruling and human-wait rows stay on the Operator project's exclusion list, unchanged.

Documentation

  • apps/cli/specs/dev-review-evaluation-program.md — section 6.13 (governed effects) and 6.14 (run control), and section 13 rows for controller death, rate limit, version incompatibility, checkpoint corruption, cancellation races, forge latency, duplicate delivery, lost acknowledgement and the beyond-Surface record (O1, O2, O3, O4)
  • apps/cli/specs/dev-review-engine-state-machine.md — the Engine run's legal transitions and terminal states the scenarios assert (O1, O4)
  • processes/vinaya/tests/run-lifecycle.test.ts — the existing Engine recovery, lock and corrupt-launch-record tests the scenarios extend (O1, O2)
  • processes/vinaya/tests/resume.test.ts — the existing resume-after-revision-move behaviour that must keep working (O4)
  • harness/dev-process/engine-runtime/sqlite-checkpointer.ts — what each checkpoint records (revision and model) and how a run resumes on it (O2, O4)
  • apps/cli/src/lib/dev-review-eval/exit-check.ts — OPERATOR_EXIT_EXCLUSIONS, which keeps the consumed-ruling and human-wait rows out of this task (O5)
  • apps/cli/tests/eval/registry/shards/fault-attack.json — each row's id, expected outcome and where its Engine evidence is recorded (O3, O5)

Planner's rationale

Boundary — Prove the run-control and governed-effects rows of the fault-and-attack matrix on the Engine runner, by fault injection with stub vendors, and fix the Engine runner where a row does not hold. It is NOT the consumed-ruling or human-wait rows (the Operator project owns them), NOT shadow mode (its own task), NOT the developer, reviewer, provider-timeout or stream rows (the remaining-rows task), and NOT the backend-selection and rollback suite. Pinned files: processes/vinaya/tests/fault-injection.test.ts, processes/vinaya/tests/replay-world.ts, processes/vinaya/tests/round-fakes.ts, processes/vinaya/bin/run-lifecycle.ts, processes/vinaya/bin/run-engine.ts, harness/dev-process/engine-runtime/sqlite-checkpointer.ts, apps/cli/tests/eval/registry/shards/fault-attack.json, apps/cli/tests/eval/registry-fault-attack.test.ts. Out: the CLI library and the CLI conformance and task-tool tests are in the Surface only because they import a pinned file, and none is edited; the standalone loop and its tests (read only: the standalone outcome is the oracle, not this task's evidence); the Engine Flow file; the Operator task tools.

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.

Sizing — One verification story: each listed fault, injected into an Engine run under both vendor stubs, ends in the row's declared outcome. Nine rows, eight pinned files, one failure mode (an Engine run that does not recover or repeats an effect under a fault). The isolation rows and the remaining rows were kept out so this stays one agent's work. Size M.

Project(s) + blast radius — Project: cli. The Engine runner files change only where a row does not hold; every Engine run uses them, so the Engine corpus, the architecture exit suite and the Log-parity suite re-verify. No shared package changes.

Dependency rationale — Depends-on: #1643 — the registry gains a per-backend evidence field there; O5's architecture test reads it. Conflicts-with: #1645 — the per-dispatch capability task also pins processes/vinaya/bin/run-engine.ts, apps/cli/tests/eval/registry/shards/fault-attack.json and apps/cli/tests/eval/registry-fault-attack.test.ts, three shared files, so the two run one after the other. Otherwise it shares apps/cli/tests/eval/registry/shards/fault-attack.json and apps/cli/tests/eval/registry-fault-attack.test.ts with #1643, #1644 and #1646, two files each; processes/vinaya/bin/run-engine.ts and fault-attack.json with the shadow-mode task #1648, two files; and fault-attack.json alone with #1649 — all below the three-file threshold, so no further Conflicts-with.

Traps to avoid — Do NOT treat a code-revision move as a version incompatibility: resuming after the revision moved continues today and must keep doing so; the refusal is for a checkpoint whose schema or runtime version the runner cannot read. Do NOT kill the controller with a mocked signal for O2; start a real runner subprocess with stub vendors and send it a real signal. Do NOT reach the forge or a real agent: use the replay world and the round fakes. Do NOT remove the consumed-ruling or human-wait rows from the Operator exclusion list. Do NOT write Issue or pull-request numbers or tranche slugs into code comments. Do NOT use sleep 60 in any test or probe.

Suggested agent-class — high: crash, resume and effect-identity reasoning across real subprocess kills.

Stop-and-escalate — Stop if making a row hold needs a change to the Engine Flow file or the packed Atta runtime; name the row and the change. Stop if an effect cannot be identified across a restart without a new record outside the Engine runner's state; name the effect. Stop if the rate-limit row needs a real provider reset time to observe; name what the stub cannot provide.

Docs to keep coherent — apps/cli/specs/dev-review-engine-state-machine.md (if a refusal or recovery transition is added); apps/cli/specs/loop.md (read only: the standalone oracle).

Premises

Surface

in: processes/vinaya/tests, processes/vinaya/bin, harness/dev-process/engine-runtime, apps/cli/tests, apps/cli/src/lib, apps/cli/tests/fixtures
out: apps/log-server, aeg-root, apps/vinaya-portal

atta-labs/process-version-bump: the pinned process files are runner scripts and tests, none of the governed files (process.yaml, the flow file, a role's doctrine, grants or output schema, the action bindings), so the check skips; a fix that reaches a governed file raises the process version in the same commit.

Parts

Part 1 (O1, O3) — Fault-injection scenarios for each listed row on the Engine runner under both vendor stubs, each asserting its declared outcome.
Part 2 (O2) — A real runner subprocess killed at each checkpoint and effect boundary, restarted, with no repeated step or effect.
Part 3 (O4) — Checkpoints record their format and runtime version; typed refusal of an incompatible checkpoint; resume after a revision move unchanged.
Part 4 (O5) — The architecture test over these rows with its shrink-only owed list, printing zero owed.

Test plan

cd processes/vinaya && bun test --timeout=120000 tests/fault-injection.test.ts → 0 fail; every listed row passes under the claude and codex stubs, and the controller-kill scenario restarts with no repeated effect
cd processes/vinaya && bun test --timeout=30000 tests/run-lifecycle.test.ts tests/resume.test.ts → 0 fail
cd apps/cli && bun test --timeout=30000 tests/eval/registry-fault-attack.test.ts tests/eval/exit-check.test.ts → 0 fail; these rows' owed count prints 0
bunx turbo run typecheck --affected → exits 0

Stop conditions

  • Making a row hold needs a change to the Engine Flow file or the packed Atta runtime.
  • An effect cannot be identified across a restart without a new record outside the Engine runner's state.
  • The rate-limit row needs a real provider reset time to observe.

Origin

Principal-directed, 2026-10-25: prove the Engine path against the target contract, every fault-and-attack row at its declared cutover outcome, on Claude Code and Codex. The consumed-ruling and human-wait rows stay owed to the Operator project.

Activity

  1. daniboomerang commented on Oct 25, 2026

    @daniboomerang
    MemberAuthor

    Brief hash: 55402142febc18cb79c6329dac4cdb637d706fd6ec832b3732bada5f1623e63a
    For: [model] (coding-agent CLI on a dev machine, dispatched locally, unattended)
    Reason: Suggested agent-class — high: crash, resume and effect-identity reasoning across real subprocess kills.
    Owner: the Principal
    Goal: Run-control and governed-effects faults end in their declared outcome on the Engine runner under both vendors
    Project: cli
    Tier: 1

    Closes #1647

    You are the AEG Developer. Run bun apps/cli/src/index.ts doctrine --role developer --print and read its output first. Mandatory.

    Objectives

    O1. Every run-control fault-and-attack row this tranche owns (controller process death, provider rate or usage limit, version incompatibility on resume, checkpoint corruption, cancellation races) and every governed-effects row (forge latency, duplicate delivery, remote success with the local acknowledgement lost, a forged or stale beyond-Surface record) has a fault-injection scenario that runs the Engine runner with stub Claude Code and stub Codex vendors and asserts the row's declared outcome.
    O2. A controller killed by signal at each checkpoint boundary and at each effect boundary of an Engine run, then restarted, resumes from its last checkpoint, repeats no completed step and produces each effect at most once; the scenario runs a real runner subprocess and kills it, once per vendor stub.
    O3. Every Must-pass row in O1 holds on the Engine runner under both vendor stubs, with no known-failure entry; a row that fails names the row in the failure message.
    O4. Every checkpoint an Engine run writes records the checkpoint format version and the Engine runtime version beside the code revision and model it records today; resuming a checkpoint whose recorded format or runtime version is incompatible with the running one, or that records neither, is refused with a typed reason and runs no step; resuming after only the code revision moved still continues, as it does today.
    O5. An architecture test lists these rows and fails while any of them has no recorded Engine evidence; its list of rows still owed shrinks only, and the count it prints is zero when this task merges. The consumed-ruling and human-wait rows stay on the Operator project's exclusion list, unchanged.

    Documentation

    • apps/cli/specs/dev-review-evaluation-program.md — section 6.13 (governed effects) and 6.14 (run control), and section 13 rows for controller death, rate limit, version incompatibility, checkpoint corruption, cancellation races, forge latency, duplicate delivery, lost acknowledgement and the beyond-Surface record (O1, O2, O3, O4)
    • apps/cli/specs/dev-review-engine-state-machine.md — the Engine run's legal transitions and terminal states the scenarios assert (O1, O4)
    • processes/vinaya/tests/run-lifecycle.test.ts — the existing Engine recovery, lock and corrupt-launch-record tests the scenarios extend (O1, O2)
    • processes/vinaya/tests/resume.test.ts — the existing resume-after-revision-move behaviour that must keep working (O4)
    • harness/dev-process/engine-runtime/sqlite-checkpointer.ts — what each checkpoint records (revision and model) and how a run resumes on it (O2, O4)
    • apps/cli/src/lib/dev-review-eval/exit-check.ts — OPERATOR_EXIT_EXCLUSIONS, which keeps the consumed-ruling and human-wait rows out of this task (O5)
    • apps/cli/tests/eval/registry/shards/fault-attack.json — each row's id, expected outcome and where its Engine evidence is recorded (O3, O5)

    Premises

    Checked against the code when this task Issue was cut, and again against the default branch at dispatch — facts, not claims.

    2. Context — read before doing anything

    • Tranche: developer-review-engine-proof-v1, task 6, Issue [developer-review-engine-proof-v1] 6 — Run-control and governed-effects faults end in their declared outcome on the Engine runner under both vendors #1647. Branch task/developer-review-engine-proof-v1/6. Depends-on: #1643, Conflicts-with: #1645.
    • Read Issue [developer-review-engine-proof-v1] 6 — Run-control and governed-effects faults end in their declared outcome on the Engine runner under both vendors #1647 in full for the complete rationale — do not re-derive it.
    • Revision: rendered at 8d5fc58a61e2d1daeed5aa58f48e7212bbedbb76 — the checkout's HEAD equaled the remote default branch, and no pinned file below carried an uncommitted change, when these facts were read.
    • Boundary — Prove the run-control and governed-effects rows of the fault-and-attack matrix on the Engine runner, by fault injection with stub vendors, and fix the Engine runner where a row does not hold. It is NOT the consumed-ruling or human-wait rows (the Operator project owns them), NOT shadow mode (its own task), NOT the developer, reviewer, provider-timeout or stream rows (the remaining-rows task), and NOT the backend-selection and rollback suite. Pinned files: processes/vinaya/tests/fault-injection.test.ts, processes/vinaya/tests/replay-world.ts, processes/vinaya/tests/round-fakes.ts, processes/vinaya/bin/run-lifecycle.ts, processes/vinaya/bin/run-engine.ts, harness/dev-process/engine-runtime/sqlite-checkpointer.ts, apps/cli/tests/eval/registry/shards/fault-attack.json, apps/cli/tests/eval/registry-fault-attack.test.ts. Out: the CLI library and the CLI conformance and task-tool tests are in the Surface only because they import a pinned file, and none is edited; the standalone loop and its tests (read only: the standalone outcome is the oracle, not this task's evidence); the Engine Flow file; the Operator task tools.

    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.

    • Traps to avoid — Do NOT treat a code-revision move as a version incompatibility: resuming after the revision moved continues today and must keep doing so; the refusal is for a checkpoint whose schema or runtime version the runner cannot read. Do NOT kill the controller with a mocked signal for O2; start a real runner subprocess with stub vendors and send it a real signal. Do NOT reach the forge or a real agent: use the replay world and the round fakes. Do NOT remove the consumed-ruling or human-wait rows from the Operator exclusion list. Do NOT write Issue or pull-request numbers or tranche slugs into code comments. Do NOT use sleep 60 in any test or probe.

    3. Technical dependencies

    Dependency rationale — Depends-on: #1643 — the registry gains a per-backend evidence field there; O5's architecture test reads it. Conflicts-with: #1645 — the per-dispatch capability task also pins processes/vinaya/bin/run-engine.ts, apps/cli/tests/eval/registry/shards/fault-attack.json and apps/cli/tests/eval/registry-fault-attack.test.ts, three shared files, so the two run one after the other. Otherwise it shares apps/cli/tests/eval/registry/shards/fault-attack.json and apps/cli/tests/eval/registry-fault-attack.test.ts with #1643, #1644 and #1646, two files each; processes/vinaya/bin/run-engine.ts and fault-attack.json with the shadow-mode task #1648, two files; and fault-attack.json alone with #1649 — all below the three-file threshold, so no further Conflicts-with.

    4. Technical surface map

    Create:

    • (none — every surface file already exists)

    Modify:

    • processes/vinaya/tests
    • processes/vinaya/bin
    • harness/dev-process/engine-runtime
    • apps/cli/tests
    • apps/cli/src/lib

    Out of surface: apps/log-server, aeg-root, apps/vinaya-portal — 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:

    • apps/cli/tests/eval/registry-fault-attack.test.ts sha256: 555927604c8d3663ccbac7c3d879bd62de0ad32b4d7cb1479dca7b5f4d1bff0d
    • apps/cli/tests/eval/registry/shards/fault-attack.json sha256: 61e80e09fad63fb1cf990f6748fbc286fd563cf8ab4a4bb1d1b6640fec376be6
    • harness/dev-process/engine-runtime/sqlite-checkpointer.ts sha256: 49f67b0ae3f1bc5722a7bafe33ddc6f5db1b0dc4c77bee950d7b9e72ac1326e7
    • processes/vinaya/bin/run-engine.ts sha256: 5e918f14b6f473e114dec98da5c94e4c8b8bf6576149b452e947c7bd9a4d773e
    • processes/vinaya/bin/run-lifecycle.ts sha256: 26a86e2474fb9d61c924f854cb738e492b52250d19b541ea20c59c09a445962e
    • processes/vinaya/tests/replay-world.ts sha256: e836e97328a8f661cc41cbf5b1790ef0c4a6712fe57e8c3491f94aebdcc6337c
    • processes/vinaya/tests/round-fakes.ts sha256: 938469794483001018b7092f04a26f885c60e95ff505ac114db2be3a5d514ecc

    5. Pre-flight checks

    Step 0 (mandatory, verbatim):

    bun install --frozen-lockfile --silent
    
    1. Clean status; parent origin/main; branch suffix literal-matches the task id.
    2. Under the review loop (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 own gh call fails inside your sandbox. Working manually, with no driver behind you, fall back to roles/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_changes when every Part is done, then open_pull_request

    1. Part 1 (O1, O3): Fault-injection scenarios for each listed row on the Engine runner under both vendor stubs, each asserting its declared outcome.

      Files (touches @attalabs/vinaya):

      • apps/cli/tests/eval/registry-fault-attack.test.ts
      • apps/cli/tests/eval/registry/shards/fault-attack.json

      The pre-push hook runs the affected suite on your one push and refuses it on failure — do not run it yourself per Part.

    2. Part 2 (O2): A real runner subprocess killed at each checkpoint and effect boundary, restarted, with no repeated step or effect.

      Files (touches the repo root):

      • harness/dev-process/engine-runtime/sqlite-checkpointer.ts
      • processes/vinaya/bin/run-engine.ts
      • processes/vinaya/bin/run-lifecycle.ts
      • processes/vinaya/tests/replay-world.ts
      • processes/vinaya/tests/round-fakes.ts

      The pre-push hook runs the affected suite on your one push and refuses it on failure — do not run it yourself per Part.

    3. Part 3 (O4): Checkpoints record their format and runtime version; typed refusal of an incompatible checkpoint; resume after a revision move unchanged.

    4. Part 4 (O5): The architecture test over these rows with its shrink-only owed list, printing zero owed.

    7. Documentation-update list

    .vinaya/doc-owners derivation 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

    • Under the review loop, the controller runs the static gates: the commit hook applies safe format and lint fixes to staged files, typechecks affected packages, and runs the Vinaya checks; the pre-push hook runs the affected suite; run_checks and 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.
    • The pre-push hook runs the affected suite once, on your one push, and refuses it on failure; CI runs the full suite.
    • Every blast-radius consumer named in §4, re-verified by name.
    • The tier checklist in bun apps/cli/src/index.ts doctrine --role developer --print genuinely satisfied; use the controller's run_checks result, hooks, and CI as gate evidence.

    9. Test Plan

    cd processes/vinaya && bun test --timeout=120000 tests/fault-injection.test.ts → 0 fail; every listed row passes under the claude and codex stubs, and the controller-kill scenario restarts with no repeated effect
    cd processes/vinaya && bun test --timeout=30000 tests/run-lifecycle.test.ts tests/resume.test.ts → 0 fail
    cd apps/cli && bun test --timeout=30000 tests/eval/registry-fault-attack.test.ts tests/eval/exit-check.test.ts → 0 fail; these rows' owed count prints 0
    bunx turbo run typecheck --affected → exits 0
    

    10. Stop conditions

    • Making a row hold needs a change to the Engine Flow file or the packed Atta runtime.
    • An effect cannot be identified across a restart without a new record outside the Engine runner's state.
    • The rate-limit row needs a real provider reset time to observe.

    Stop-and-escalate — Stop if making a row hold needs a change to the Engine Flow file or the packed Atta runtime; name the row and the change. Stop if an effect cannot be identified across a restart without a new record outside the Engine runner's state; name the effect. Stop if the rate-limit row needs a real provider reset time to observe; name what the stub cannot provide.

    11. Constraints

    Autonomy: Do not stop to ask clarifying questions. For any ambiguity not covered by a Section 10 stop condition, choose the most reasonable option consistent with this brief, record the choice in the PR body at open, or in a PR comment after open, and continue. Halt only for the explicit Section 10 stop conditions — and when you halt, record the blocker in a PR comment or an Issue comment rather than waiting interactively for input.

    12. Deliverable

    • PR title (exact): [developer-review-engine-proof-v1] 6 — Run-control and governed-effects faults end in their declared outcome on the Engine runner under both vendors
    • Open the PR only via the open_pull_request tool (title and body); publish your commits with publish_changes and update the body with update_pull_request_body/refresh_evidence — you hold no gh/git push credential.
    • Publish once, with publish_changes, when every Part is done; then open the pull request with open_pull_request. Publish again only to answer review findings or a red gate — a refused hook, a failing check, a merge conflict.
    • PR body = the Developer's PR report (print it with bun apps/cli/src/index.ts doctrine --template pr-report --print), with Closes #1647 at the top of the header block. This brief stays frozen on the task Issue and never rides in the body.
    • Pre-open: tier checklist satisfied. The documentation gate is the controller's — run_checks and CI run it.
    • Include git diff origin/main...HEAD --stat as context only; Surface is judged by the driver's surface-scope check, never by this hand-run diff. Token use needs no report — the Vinaya log records it.
    • Then STOP. Review and Verification are separate invocations.
  2. daniboomerang commented on Oct 25, 2026

    @daniboomerang
    MemberAuthor

    The dev-review-loop paused: escalation — the Developer asks for a ruling: Rows 08 and 09 need the refused checkpoint to end in a typed Operator-remediation pause. Two options. (1) Widen the Surface: allow the one-reason addition checkpoint_refused to the shared PauseReason union and to its PAUSE_REASON_PROFILE / isHostRepairPause ent….

    No pull request exists yet for this task, so the pause is recorded on this Issue instead.
    A Principal ruling is needed before this can continue. Post it as a comment on this Issue whose FIRST line is exactly this marker — the second number is the ordinal, and it must exceed the one this pause recorded:

    <!-- aeg:principal:ruling:1647-1 -->
    Your ruling text here.
    

    Then continue with:

    vinaya task run developer-review-engine-proof-v1 6 --agent claude --model opus --runner atta-engine
    

    The Operator's own task_resume clears this pause too, reading that same ruling from this Issue; task_cancel ends it instead.

  3. daniboomerang commented on Oct 25, 2026

    @daniboomerang
    MemberAuthor

    Decision: option 2, proceed as briefed.

    Revert the edits to the shared PauseReason union and pause-resume.ts, and do not widen the Surface. Raise the refused-checkpoint case as the existing host-repair reason sandbox_refused, with the typed code (checkpoint_corrupt, checkpoint_version_unrecorded, checkpoint_format_incompatible, runtime_version_incompatible) first in its detail so the Operator and the Log name the real cause. Do not re-pin the standalone baseline.

  4. daniboomerang commented on Oct 25, 2026

    @daniboomerang
    MemberAuthor

    Decision: widen_surface — apps/cli/tests/fixtures/dev-review-engine-scenarios.json only.

    Make the minimal edit to the scenario corpus that records the standalone baseline as still current, so the history rule sees a corpus update after the loop-touching commits; the pull request's net diff leaves the loop byte-identical to main, so the baseline genuinely did not change. Do not rewrite branch history, do not force-push, and do not change the history rule in the architecture-exit test. Keep the two widened waits in the orphan scenario only if they pass under the pre-push hook's load; note them in the pull request body.

  5. daniboomerang commented on Oct 25, 2026

    @daniboomerang
    MemberAuthor

    Brief hash: 27022dda2dd62d848bd4781697946015b130806506eb20786f631e7090c11838
    Supersedes: #1647 (comment) — Principal ruling: record the unchanged standalone baseline in the scenario corpus so the architecture-exit history rule passes after the ruled revert of the shared loop edits
    For: [model] (coding-agent CLI on a dev machine, dispatched locally, unattended)
    Reason: Suggested agent-class — high: crash, resume and effect-identity reasoning across real subprocess kills.
    Owner: the Principal
    Goal: Run-control and governed-effects faults end in their declared outcome on the Engine runner under both vendors
    Project: cli
    Tier: 1

    Closes #1647

    You are the AEG Developer. Run bun apps/cli/src/index.ts doctrine --role developer --print and read its output first. Mandatory.

    Objectives

    O1. Every run-control fault-and-attack row this tranche owns (controller process death, provider rate or usage limit, version incompatibility on resume, checkpoint corruption, cancellation races) and every governed-effects row (forge latency, duplicate delivery, remote success with the local acknowledgement lost, a forged or stale beyond-Surface record) has a fault-injection scenario that runs the Engine runner with stub Claude Code and stub Codex vendors and asserts the row's declared outcome.
    O2. A controller killed by signal at each checkpoint boundary and at each effect boundary of an Engine run, then restarted, resumes from its last checkpoint, repeats no completed step and produces each effect at most once; the scenario runs a real runner subprocess and kills it, once per vendor stub.
    O3. Every Must-pass row in O1 holds on the Engine runner under both vendor stubs, with no known-failure entry; a row that fails names the row in the failure message.
    O4. Every checkpoint an Engine run writes records the checkpoint format version and the Engine runtime version beside the code revision and model it records today; resuming a checkpoint whose recorded format or runtime version is incompatible with the running one, or that records neither, is refused with a typed reason and runs no step; resuming after only the code revision moved still continues, as it does today.
    O5. An architecture test lists these rows and fails while any of them has no recorded Engine evidence; its list of rows still owed shrinks only, and the count it prints is zero when this task merges. The consumed-ruling and human-wait rows stay on the Operator project's exclusion list, unchanged.

    Documentation

    • apps/cli/specs/dev-review-evaluation-program.md — section 6.13 (governed effects) and 6.14 (run control), and section 13 rows for controller death, rate limit, version incompatibility, checkpoint corruption, cancellation races, forge latency, duplicate delivery, lost acknowledgement and the beyond-Surface record (O1, O2, O3, O4)
    • apps/cli/specs/dev-review-engine-state-machine.md — the Engine run's legal transitions and terminal states the scenarios assert (O1, O4)
    • processes/vinaya/tests/run-lifecycle.test.ts — the existing Engine recovery, lock and corrupt-launch-record tests the scenarios extend (O1, O2)
    • processes/vinaya/tests/resume.test.ts — the existing resume-after-revision-move behaviour that must keep working (O4)
    • harness/dev-process/engine-runtime/sqlite-checkpointer.ts — what each checkpoint records (revision and model) and how a run resumes on it (O2, O4)
    • apps/cli/src/lib/dev-review-eval/exit-check.ts — OPERATOR_EXIT_EXCLUSIONS, which keeps the consumed-ruling and human-wait rows out of this task (O5)
    • apps/cli/tests/eval/registry/shards/fault-attack.json — each row's id, expected outcome and where its Engine evidence is recorded (O3, O5)

    Premises

    Checked against the code when this task Issue was cut, and again against the default branch at dispatch — facts, not claims.

    2. Context — read before doing anything

    • Tranche: developer-review-engine-proof-v1, task 6, Issue [developer-review-engine-proof-v1] 6 — Run-control and governed-effects faults end in their declared outcome on the Engine runner under both vendors #1647. Branch task/developer-review-engine-proof-v1/6. Depends-on: #1643, Conflicts-with: #1645.
    • Read Issue [developer-review-engine-proof-v1] 6 — Run-control and governed-effects faults end in their declared outcome on the Engine runner under both vendors #1647 in full for the complete rationale — do not re-derive it.
    • Revision: rendered at fcc11efee68dd3331887047ee80866ddebd26d07 — the checkout's HEAD equaled the remote default branch, and no pinned file below carried an uncommitted change, when these facts were read.
    • Boundary — Prove the run-control and governed-effects rows of the fault-and-attack matrix on the Engine runner, by fault injection with stub vendors, and fix the Engine runner where a row does not hold. It is NOT the consumed-ruling or human-wait rows (the Operator project owns them), NOT shadow mode (its own task), NOT the developer, reviewer, provider-timeout or stream rows (the remaining-rows task), and NOT the backend-selection and rollback suite. Pinned files: processes/vinaya/tests/fault-injection.test.ts, processes/vinaya/tests/replay-world.ts, processes/vinaya/tests/round-fakes.ts, processes/vinaya/bin/run-lifecycle.ts, processes/vinaya/bin/run-engine.ts, harness/dev-process/engine-runtime/sqlite-checkpointer.ts, apps/cli/tests/eval/registry/shards/fault-attack.json, apps/cli/tests/eval/registry-fault-attack.test.ts. Out: the CLI library and the CLI conformance and task-tool tests are in the Surface only because they import a pinned file, and none is edited; the standalone loop and its tests (read only: the standalone outcome is the oracle, not this task's evidence); the Engine Flow file; the Operator task tools.

    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.

    • Traps to avoid — Do NOT treat a code-revision move as a version incompatibility: resuming after the revision moved continues today and must keep doing so; the refusal is for a checkpoint whose schema or runtime version the runner cannot read. Do NOT kill the controller with a mocked signal for O2; start a real runner subprocess with stub vendors and send it a real signal. Do NOT reach the forge or a real agent: use the replay world and the round fakes. Do NOT remove the consumed-ruling or human-wait rows from the Operator exclusion list. Do NOT write Issue or pull-request numbers or tranche slugs into code comments. Do NOT use sleep 60 in any test or probe.

    3. Technical dependencies

    Dependency rationale — Depends-on: #1643 — the registry gains a per-backend evidence field there; O5's architecture test reads it. Conflicts-with: #1645 — the per-dispatch capability task also pins processes/vinaya/bin/run-engine.ts, apps/cli/tests/eval/registry/shards/fault-attack.json and apps/cli/tests/eval/registry-fault-attack.test.ts, three shared files, so the two run one after the other. Otherwise it shares apps/cli/tests/eval/registry/shards/fault-attack.json and apps/cli/tests/eval/registry-fault-attack.test.ts with #1643, #1644 and #1646, two files each; processes/vinaya/bin/run-engine.ts and fault-attack.json with the shadow-mode task #1648, two files; and fault-attack.json alone with #1649 — all below the three-file threshold, so no further Conflicts-with.

    4. Technical surface map

    Create:

    • (none — every surface file already exists)

    Modify:

    • processes/vinaya/tests
    • processes/vinaya/bin
    • harness/dev-process/engine-runtime
    • apps/cli/tests
    • apps/cli/src/lib
    • apps/cli/tests/fixtures

    Out of surface: apps/log-server, aeg-root, apps/vinaya-portal — 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:

    • apps/cli/tests/eval/registry-fault-attack.test.ts sha256: 555927604c8d3663ccbac7c3d879bd62de0ad32b4d7cb1479dca7b5f4d1bff0d
    • apps/cli/tests/eval/registry/shards/fault-attack.json sha256: 4489e60b02d34fb69b052a90514e29138a0a86f99a0a05d50a6c2b2ed40c0d14
    • harness/dev-process/engine-runtime/sqlite-checkpointer.ts sha256: 49f67b0ae3f1bc5722a7bafe33ddc6f5db1b0dc4c77bee950d7b9e72ac1326e7
    • processes/vinaya/bin/run-engine.ts sha256: 3106f971867d4af91f85c9d87b1ebbfb5a300e1f8a5819809ee51577ec58d30c
    • processes/vinaya/bin/run-lifecycle.ts sha256: 26a86e2474fb9d61c924f854cb738e492b52250d19b541ea20c59c09a445962e
    • processes/vinaya/tests/replay-world.ts sha256: e836e97328a8f661cc41cbf5b1790ef0c4a6712fe57e8c3491f94aebdcc6337c
    • processes/vinaya/tests/round-fakes.ts sha256: 938469794483001018b7092f04a26f885c60e95ff505ac114db2be3a5d514ecc

    5. Pre-flight checks

    Step 0 (mandatory, verbatim):

    bun install --frozen-lockfile --silent
    
    1. Clean status; parent origin/main; branch suffix literal-matches the task id.
    2. Under the review loop (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 own gh call fails inside your sandbox. Working manually, with no driver behind you, fall back to roles/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_changes when every Part is done, then open_pull_request

    1. Part 1 (O1, O3): Fault-injection scenarios for each listed row on the Engine runner under both vendor stubs, each asserting its declared outcome.

      Files (touches @attalabs/vinaya):

      • apps/cli/tests/eval/registry-fault-attack.test.ts
      • apps/cli/tests/eval/registry/shards/fault-attack.json

      The pre-push hook runs the affected suite on your one push and refuses it on failure — do not run it yourself per Part.

    2. Part 2 (O2): A real runner subprocess killed at each checkpoint and effect boundary, restarted, with no repeated step or effect.

      Files (touches the repo root):

      • harness/dev-process/engine-runtime/sqlite-checkpointer.ts
      • processes/vinaya/bin/run-engine.ts
      • processes/vinaya/bin/run-lifecycle.ts
      • processes/vinaya/tests/replay-world.ts
      • processes/vinaya/tests/round-fakes.ts

      The pre-push hook runs the affected suite on your one push and refuses it on failure — do not run it yourself per Part.

    3. Part 3 (O4): Checkpoints record their format and runtime version; typed refusal of an incompatible checkpoint; resume after a revision move unchanged.

    4. Part 4 (O5): The architecture test over these rows with its shrink-only owed list, printing zero owed.

    7. Documentation-update list

    .vinaya/doc-owners derivation 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

    • Under the review loop, the controller runs the static gates: the commit hook applies safe format and lint fixes to staged files, typechecks affected packages, and runs the Vinaya checks; the pre-push hook runs the affected suite; run_checks and 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.
    • The pre-push hook runs the affected suite once, on your one push, and refuses it on failure; CI runs the full suite.
    • Every blast-radius consumer named in §4, re-verified by name.
    • The tier checklist in bun apps/cli/src/index.ts doctrine --role developer --print genuinely satisfied; use the controller's run_checks result, hooks, and CI as gate evidence.

    9. Test Plan

    cd processes/vinaya && bun test --timeout=120000 tests/fault-injection.test.ts → 0 fail; every listed row passes under the claude and codex stubs, and the controller-kill scenario restarts with no repeated effect
    cd processes/vinaya && bun test --timeout=30000 tests/run-lifecycle.test.ts tests/resume.test.ts → 0 fail
    cd apps/cli && bun test --timeout=30000 tests/eval/registry-fault-attack.test.ts tests/eval/exit-check.test.ts → 0 fail; these rows' owed count prints 0
    bunx turbo run typecheck --affected → exits 0
    

    10. Stop conditions

    • Making a row hold needs a change to the Engine Flow file or the packed Atta runtime.
    • An effect cannot be identified across a restart without a new record outside the Engine runner's state.
    • The rate-limit row needs a real provider reset time to observe.

    Stop-and-escalate — Stop if making a row hold needs a change to the Engine Flow file or the packed Atta runtime; name the row and the change. Stop if an effect cannot be identified across a restart without a new record outside the Engine runner's state; name the effect. Stop if the rate-limit row needs a real provider reset time to observe; name what the stub cannot provide.

    11. Constraints

    Autonomy: Do not stop to ask clarifying questions. For any ambiguity not covered by a Section 10 stop condition, choose the most reasonable option consistent with this brief, record the choice in the PR body at open, or in a PR comment after open, and continue. Halt only for the explicit Section 10 stop conditions — and when you halt, record the blocker in a PR comment or an Issue comment rather than waiting interactively for input.

    12. Deliverable

    • PR title (exact): [developer-review-engine-proof-v1] 6 — Run-control and governed-effects faults end in their declared outcome on the Engine runner under both vendors
    • Open the PR only via the open_pull_request tool (title and body); publish your commits with publish_changes and update the body with update_pull_request_body/refresh_evidence — you hold no gh/git push credential.
    • Publish once, with publish_changes, when every Part is done; then open the pull request with open_pull_request. Publish again only to answer review findings or a red gate — a refused hook, a failing check, a merge conflict.
    • PR body = the Developer's PR report (print it with bun apps/cli/src/index.ts doctrine --template pr-report --print), with Closes #1647 at the top of the header block. This brief stays frozen on the task Issue and never rides in the body.
    • Pre-open: tier checklist satisfied. The documentation gate is the controller's — run_checks and CI run it.
    • Include git diff origin/main...HEAD --stat as context only; Surface is judged by the driver's surface-scope check, never by this hand-run diff. Token use needs no report — the Vinaya log records it.
    • Then STOP. Review and Verification are separate invocations.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions