Skip to content

Fix(cli): a Developer launch that ended before binding a session never blocks its task #1671

Closed

Description

@daniboomerang

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 — launchNeverSpawned and reconcileLaunch, 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.ts contains export function launchNeverSpawned(record: LaunchRecord): boolean {
  • apps/cli/src/lib/dev-review-loop/developer-dispatch.ts contains if (launchNeverSpawned(record)) return { kind: 'fresh', record, outcome }
  • apps/cli/src/lib/dev-review-loop/developer-dispatch.ts contains before 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

  1. daniboomerang commented on Oct 26, 2026

    @daniboomerang
    MemberAuthor

    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 --print and 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 — launchNeverSpawned and reconcileLaunch, 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.ts contains export function launchNeverSpawned(record: LaunchRecord): boolean {
    • apps/cli/src/lib/dev-review-loop/developer-dispatch.ts contains if (launchNeverSpawned(record)) return { kind: 'fresh', record, outcome }
    • apps/cli/src/lib/dev-review-loop/developer-dispatch.ts contains before its vendor session was ever bound

    2. Context — read before doing anything

    • Backlog Issue: Fix(cli): a Developer launch that ended before binding a session never blocks its task #1671, no tranche. Branch task/issue-1671. Depends-on: —, Conflicts-with: —.
    • Read Issue Fix(cli): a Developer launch that ended before binding a session never blocks its task #1671 in full for the complete rationale — do not re-derive it.
    • Revision: rendered at 89de2f65db8c421b5f935989bbc01ee0eea579cb — the checkout's HEAD equaled the remote default branch, and no pinned file below carried an uncommitted change, when these facts were read.
    • 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.

    3. Technical dependencies

    Dependency rationale — none: this single-fix Issue declares no Depends-on or Conflicts-with edge.

    4. Technical surface map

    Create:

    • (none — every surface file already exists)

    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:

    • apps/cli/specs/loop.md sha256: 554798fa117d6c8f0eeb343bdc818937920ae0b42d1eb89d2174b1af9b97d194
    • apps/cli/src/lib/dev-review-loop/developer-dispatch.ts sha256: da5918ed9c56d1ddff411a9537f83841e66d25db2e8edd9bc0daa69b5c219a61
    • apps/cli/tests/lib/dev-review-loop/developer-dispatch.test.ts sha256: 535dcb9e64ebdfb9824c29d17e081540a370c09d17a022b7775062620b0af44e

    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, 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.

      Files (touches @attalabs/vinaya):

      • apps/cli/specs/loop.md
      • apps/cli/src/lib/dev-review-loop/developer-dispatch.ts
      • apps/cli/tests/lib/dev-review-loop/developer-dispatch.test.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.

    2. Part 2 (O1, O2, O3): apps/cli/specs/loop.md states the rule.

    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

    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.

    10. Stop conditions

    • A launch with no bound session can leave turn state a fresh session would lose or repeat.

    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

    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): Fix(cli): a Developer launch that ended before binding a session never blocks its task
    • 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 #1671 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

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions