Repository navigation
Description
Tier: 1
Project: cli
Type: fix
Objectives
O1. When a prior Developer launch ended (its process is gone) without ever binding a vendor session, the next run of that task dispatches a fresh Developer session instead of pausing. This covers a launch stopped by a signal before its session bound and a launch whose agent exited on a sign-in failure.
O2. A prior launch whose process is still alive is still never raced: it is reported as live, exactly as today.
O3. A prior launch that did bind a session still resumes that exact session, exactly as today.
O4. The fresh dispatch after such a launch is recorded in the run's Log with the prior attempt's outcome and failure reason.
Documentation
apps/cli/src/lib/dev-review-loop/developer-dispatch.ts—launchNeverSpawnedandreconcileLaunch, which pause with "ended … before its vendor session was ever bound" when no session was bound and the launch was not refused before spawn (O1, O2, O3, O4)apps/cli/specs/loop.md— the launch-recovery rules this changes (O1, O2, O3)
Planner's rationale
Boundary — One thing: a Developer launch that ended with no bound session never blocks its task. Pinned files: apps/cli/src/lib/dev-review-loop/developer-dispatch.ts, apps/cli/tests/lib/dev-review-loop/developer-dispatch.test.ts, apps/cli/specs/loop.md. Out: the Engine runner and its checkpoint; reviewer dispatch; how a session is bound.
Traps to avoid — Do NOT start a fresh session while the prior launch's process is alive (classifyChildLiveness live) or orphaned and not yet reaped. Do NOT drop the exact-session resume when a session was bound. Do NOT clear or rewrite the launch record by hand; decide from it.
blast-radius-ack: the Surface reaches packages/aeg-core and processes/vinaya only because they import the edited module or the loop spec; nothing there is edited, so one review lens is enough.
Suggested agent-class — medium: one pure reconciliation function and its tests.
Stop-and-escalate — Stop if a launch with no bound session can have produced turn state (commits, published effects) that a fresh session would lose or repeat.
Docs to keep coherent — apps/cli/specs/loop.md (the launch-recovery paragraph).
Premises
apps/cli/src/lib/dev-review-loop/developer-dispatch.tscontainsexport function launchNeverSpawned(record: LaunchRecord): boolean {apps/cli/src/lib/dev-review-loop/developer-dispatch.tscontainsif (launchNeverSpawned(record)) return { kind: 'fresh', record, outcome }apps/cli/src/lib/dev-review-loop/developer-dispatch.tscontainsbefore its vendor session was ever bound
Surface
in: apps/cli/src/lib, apps/cli/src/commands, apps/cli/tests/lib, apps/cli/tests/conformance, apps/cli/specs, processes/vinaya/bin, processes/vinaya/executing, processes/vinaya/tests, packages/aeg-core/src, .changeset
out: harness, apps/vinaya-portal, apps/log-server, aeg-root, .github/workflows
reader-resolvable-prose: code comments and spec prose describe the launch-recovery rule itself and carry no Issue or pull-request numbers and no tranche slugs.
doc-claims: every AEG:CLAIM line in a changed file, including apps/cli/specs/loop.md, is kept true in the same commit as the code it names.
Parts
Part 1 (O1, O2, O3, O4) — reconcileLaunch returns a fresh dispatch for a prior launch whose process is gone and that bound no session, keeping live and bound-session cases unchanged; the Log records the prior outcome.
Part 2 (O1, O2, O3) — apps/cli/specs/loop.md states the rule.
Test plan
bun test apps/cli/tests/lib/dev-review-loop/developer-dispatch.test.ts → passes, including cases: a launch stopped by a signal before binding (process gone, no session) dispatches fresh; a launch that exited on a sign-in failure before binding dispatches fresh; a live prior launch is reported live; a bound session resumes
bun apps/cli/src/index.ts check --all → every check passes
- [principal] A live Engine run of a real task whose Developer launch is stopped before its session binds, then started again, dispatches a fresh Developer and reaches its turn.
Stop conditions
- A launch with no bound session can leave turn state a fresh session would lose or repeat.
Origin
Principal-directed, 2026-10-26. Three times in one night a task stopped permanently with "the developer's prior launch (attempt 1) ended incomplete (no failure recorded) before its vendor session was ever bound — cannot resume the exact session, and starting a fresh one would lose worker continuity": once after the driver was stopped mid-launch, once after the Developer's sign-in expired, and once after a Codex token refresh failed. No ruling cleared it; only moving the task's runtime state aside did.
Activity
daniboomerang commentedon Oct 26, 2026
Brief hash: f3b32bf01e8799ae2c317869f5fd7b5a362af69034790201aba22b64c2366eef
For: [model] (coding-agent CLI on a dev machine, dispatched locally, unattended)
Reason: Suggested agent-class — medium: one pure reconciliation function and its tests.
Owner: the Principal
Goal: Fix(cli): a Developer launch that ended before binding a session never blocks its task
Project: ** cli
Tier: 1
Closes #1671
You are the AEG Developer. Run
bun apps/cli/src/index.ts doctrine --role developer --printand read its output first. Mandatory.Objectives
O1. When a prior Developer launch ended (its process is gone) without ever binding a vendor session, the next run of that task dispatches a fresh Developer session instead of pausing. This covers a launch stopped by a signal before its session bound and a launch whose agent exited on a sign-in failure.
O2. A prior launch whose process is still alive is still never raced: it is reported as live, exactly as today.
O3. A prior launch that did bind a session still resumes that exact session, exactly as today.
O4. The fresh dispatch after such a launch is recorded in the run's Log with the prior attempt's outcome and failure reason.
Documentation
apps/cli/src/lib/dev-review-loop/developer-dispatch.ts—launchNeverSpawnedandreconcileLaunch, which pause with "ended … before its vendor session was ever bound" when no session was bound and the launch was not refused before spawn (O1, O2, O3, O4)apps/cli/specs/loop.md— the launch-recovery rules this changes (O1, O2, O3)Premises
Checked against the code when this task Issue was cut, and again against the default branch at dispatch — facts, not claims.
apps/cli/src/lib/dev-review-loop/developer-dispatch.tscontainsexport function launchNeverSpawned(record: LaunchRecord): boolean {apps/cli/src/lib/dev-review-loop/developer-dispatch.tscontainsif (launchNeverSpawned(record)) return { kind: 'fresh', record, outcome }apps/cli/src/lib/dev-review-loop/developer-dispatch.tscontainsbefore its vendor session was ever bound2. Context — read before doing anything
task/issue-1671.Depends-on: —,Conflicts-with: —.89de2f65db8c421b5f935989bbc01ee0eea579cb— the checkout's HEAD equaled the remote default branch, and no pinned file below carried an uncommitted change, when these facts were read.apps/cli/src/lib/dev-review-loop/developer-dispatch.ts,apps/cli/tests/lib/dev-review-loop/developer-dispatch.test.ts,apps/cli/specs/loop.md. Out: the Engine runner and its checkpoint; reviewer dispatch; how a session is bound.classifyChildLivenesslive) or orphaned and not yet reaped. Do NOT drop the exact-session resume when a session was bound. Do NOT clear or rewrite the launch record by hand; decide from it.3. Technical dependencies
Dependency rationale — none: this single-fix Issue declares no
Depends-onorConflicts-withedge.4. Technical surface map
Create:
Modify:
apps/cli/src/lib
apps/cli/src/commands
apps/cli/tests/lib
apps/cli/tests/conformance
apps/cli/specs
processes/vinaya/bin
processes/vinaya/executing
processes/vinaya/tests
packages/aeg-core/src
.changeset
consumer-tests: none — no consumer test path named yet for @attalabs/aeg-core (apps/log-server), @attalabs/aeg-core (packages/sources), @attalabs/aeg-core (processes/vinaya); name one before dispatch, or confirm no consumer-facing behavior changed.
consumer-tests: apps/cli/tests (covers @attalabs/aeg-core)
Out of surface: harness, apps/vinaya-portal, apps/log-server, aeg-root, .github/workflows — 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 (O1, O2, O3, O4):
reconcileLaunchreturns a fresh dispatch for a prior launch whose process is gone and that bound no session, keeping live and bound-session cases unchanged; the Log records the prior outcome.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 (O1, O2, O3):
apps/cli/specs/loop.mdstates the rule.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
Stop-and-escalate — Stop if a launch with no bound session can have produced turn state (commits, published effects) that a fresh session would lose or repeat.
11. Constraints
12. Deliverable
Fix(cli): a Developer launch that ended before binding a session never blocks its taskopen_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 #1671at 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.