feat(907): orchestrated sessions — mechanics page, standing prompt, pick/build/resume workflows #908

Merged
timothy merged 5 commits from 907-orchestrated-sessions into main 2026-09-05 00:09:16 +02:00
Owner

fixes #907

What this adds

The single-issue kickoff stays as it is. This adds the layer that runs several of those under one referee: docs/handoffs/orchestration.md (roles and sizing, one worktree per issue under ~/orca/workspaces/ersatztv/, the landing order with the review loop inside the worktree before the single push, the merge through the consent hook, resume, incidents), docs/handoffs/orchestrator-prompt.md (the standing prompt), and three Workflow scripts under .claude/workflows/: a picker over scripts/select-queue.sh with two refuters, an issue-build pipeline (claim → recon → implement → gate → cold review with a Codex runner for the rubric's risk classes and a cold same-family fallback → fix loop → finisher), and a resume pipeline for a paused branch.

New decision record process.orchestrated-session scopes the two rules the harness needed stated: a rebase onto origin/main pushed with --force-with-lease is the one sanctioned rewrite of a pushed branch (commits unchanged, base moved; H11 refuses the alternatives) and it voids the verdict; and the referee, never the agent whose work it certifies, ticks the ## Done-when boxes.

Tested and rejected

A mkdir lock around scripts/e2e-local.sh to serialise live-E2E across worktrees. The launcher returns with the server still running, so the lock released before the assertions ran, and its stale-holder path double-acquired in 4 of 91 measured races (2026-09-04). The launcher's documented conflict is its wwwroot under the repo root, which is per worktree, so slots run on their own port and the launcher's pre-flight refuses a busy one. Recorded in the decision record.

Review

Four rounds, cold worktree-isolated reviewer (Opus, high; same-family — this diff is docs, JS prompts and shell, outside the cross-family rubric). Round one found three blockers (the E2E lock's two defects and the unrecorded force-with-lease rule) and fourteen should-fix; each subsequent round found an adjacent defect introduced by the previous fix (a marker-overwrite instruction that could only fire in a foreign worktree; branch keys not under required; a substring port substitution that could rewrite a path; a guard that made a legitimate resume outcome an error). Round four: no findings, VERDICT: merge. Every finding was verified by execution by the reviewer, not taken on assertion.

Verification

Gate Result
PYTHONPATH=. python3 -m pytest scripts/tests -q 1569 passed, 3 skipped on the first commit; the subset covering inventory/readme/handoffs/narrative/decisions/catalog/workflow re-run green on every round (324 passed on the last)
scripts/check-doc-narrative.py --diff origin/main 0 advisory warnings
scripts/decisions_validate.py + catalog regeneration OK, catalog byte-unchanged after regeneration
Workflow scripts node --check clean; templates rendered with node to verify the slot and reviewer gates name their own worktree and port
Live use the three scripts ran this session as the harness for #830 (resume), #881 and #885 (build)

Deferred

none

🤖 Generated with Claude Code

https://claude.ai/code/session_015QqCpYFsKgnAnx6jVwrKiV

fixes #907 ## What this adds The single-issue kickoff stays as it is. This adds the layer that runs several of those under one referee: `docs/handoffs/orchestration.md` (roles and sizing, one worktree per issue under `~/orca/workspaces/ersatztv/`, the landing order with the review loop inside the worktree before the single push, the merge through the consent hook, resume, incidents), `docs/handoffs/orchestrator-prompt.md` (the standing prompt), and three Workflow scripts under `.claude/workflows/`: a picker over `scripts/select-queue.sh` with two refuters, an issue-build pipeline (claim → recon → implement → gate → cold review with a Codex runner for the rubric's risk classes and a cold same-family fallback → fix loop → finisher), and a resume pipeline for a paused branch. New decision record `process.orchestrated-session` scopes the two rules the harness needed stated: a rebase onto `origin/main` pushed with `--force-with-lease` is the one sanctioned rewrite of a pushed branch (commits unchanged, base moved; H11 refuses the alternatives) and it voids the verdict; and the referee, never the agent whose work it certifies, ticks the `## Done-when` boxes. ## Tested and rejected A `mkdir` lock around `scripts/e2e-local.sh` to serialise live-E2E across worktrees. The launcher returns with the server still running, so the lock released before the assertions ran, and its stale-holder path double-acquired in 4 of 91 measured races (2026-09-04). The launcher's documented conflict is its `wwwroot` under the repo root, which is per worktree, so slots run on their own port and the launcher's pre-flight refuses a busy one. Recorded in the decision record. ## Review Four rounds, cold worktree-isolated reviewer (Opus, high; same-family — this diff is docs, JS prompts and shell, outside the cross-family rubric). Round one found three blockers (the E2E lock's two defects and the unrecorded force-with-lease rule) and fourteen should-fix; each subsequent round found an adjacent defect introduced by the previous fix (a marker-overwrite instruction that could only fire in a foreign worktree; branch keys not under `required`; a substring port substitution that could rewrite a path; a guard that made a legitimate resume outcome an error). Round four: no findings, `VERDICT: merge`. Every finding was verified by execution by the reviewer, not taken on assertion. ## Verification | Gate | Result | |---|---| | `PYTHONPATH=. python3 -m pytest scripts/tests -q` | 1569 passed, 3 skipped on the first commit; the subset covering inventory/readme/handoffs/narrative/decisions/catalog/workflow re-run green on every round (324 passed on the last) | | `scripts/check-doc-narrative.py --diff origin/main` | 0 advisory warnings | | `scripts/decisions_validate.py` + catalog regeneration | OK, catalog byte-unchanged after regeneration | | Workflow scripts | `node --check` clean; templates rendered with node to verify the slot and reviewer gates name their own worktree and port | | Live use | the three scripts ran this session as the harness for #830 (resume), #881 and #885 (build) | ## Deferred none 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_015QqCpYFsKgnAnx6jVwrKiV
timothy added 5 commits 2026-09-04 23:44:19 +02:00
The single-issue kickoff stays as it is; this adds the layer that runs several of
those under one referee. docs/handoffs/orchestration.md owns roles and sizing,
one worktree per issue under ~/orca/workspaces/ersatztv/, the landing order with
the review loop inside the worktree before the single push, and the merge through
the consent hook. Three Workflow scripts encode it: a picker over
scripts/select-queue.sh with two refuters, an issue-build pipeline (claim, recon,
implement, gate, cold review with a cross-family runner for the rubric's risk
classes, fix loop, finisher), and a resume pipeline for a paused branch.
scripts/e2e-gate.sh serialises live-E2E across worktrees because e2e-local.sh
refuses concurrent runs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QqCpYFsKgnAnx6jVwrKiV
The mkdir lock around scripts/e2e-local.sh serialised the launch, not the run
(the launcher returns with the server up), and its stale-holder path double-
acquired in 4 of 91 measured races; the launcher's documented conflict is its
per-worktree wwwroot, so slots now run on their own port and the lock is gone
with its inventory row. process.orchestrated-session records the two scopings
the harness needed: a rebase pushed with --force-with-lease as the one sanctioned
rewrite, and the referee as the only agent that ticks Done-when boxes. Scripts:
required-arg guard, per-issue claim probe, reviewer fetch recipe, codex fallback
to a cold review-only agent with the substitution stated in the PR body, rebase
before the review loop with a patch-id check at the push, non-interactive
squash recipe, Land phase. README bullets re-parented; kickoff bullet keyed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QqCpYFsKgnAnx6jVwrKiV
The marker-overwrite instruction asserted an antecedent no agent can verify
and, since same-session worktrees carry no marker, could only fire in another
session's worktree; the scripts now stop and report. `ran` and `patch_changed`
move into required keys of their own schemas so a missing field cannot read
as a successful cross-family review or an unchanged patch. A blocking finding
in the post-rebase round now returns an error like every other failure path.
Each reviewer lens gets its own E2E port; the gate text travels with the
reviewer brief. The standing prompt no longer contradicts the substitution
the scripts perform; the record names patch-id, the mechanism the scripts use.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QqCpYFsKgnAnx6jVwrKiV
Embedding the slot's gate text in the reviewer brief carried the slot worktree
into the one prompt that forbids it, and a substring port substitution could
rewrite a path containing the same digits; gateFor(port, where) renders each
brief for its own tree and port. The port guard accepted "", null and false
through Number(); it now requires a JS integer in (1024, 65000). The finisher
schema requires only patch_changed, and a done report without a PR URL or head
sha is an error rather than a placeholder.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QqCpYFsKgnAnx6jVwrKiV
fix(907): round four — a resume that updated its existing PR is a success, and a post-push error says so
Build ErsatzTV Image / CI toolchain image resolves (pull_request) Successful in 41s
review-verdict/h10 Review-verdict: MERGEABLE @ 208ae00 (base: main)
Build ErsatzTV Image / Delimiter ban (release path) (pull_request) Successful in 50s
Review verdict / Set review-verdict status (pull_request_target) Successful in 12s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 9m48s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 6m35s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Skipped
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 6m23s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 7s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 5s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 11s
PR Gates / Docs update reminder (pull_request) Successful in 33s
PR Gates / Fix proofs (Proves trailers) (pull_request) Successful in 19s
PR Gates / Script lint and tests (ruff + pytest) (pull_request) Successful in 19m33s
PR Gates / decisions lifecycle (pull_request) Successful in 13s
208ae00953
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QqCpYFsKgnAnx6jVwrKiV
Author
Owner

Review-verdict: MERGEABLE @ 208ae00

Four cold review rounds (Opus high, worktree-isolated, same-family: docs + JS prompts + shell, outside the cross-family rubric). Round one: 3 blocking + 14 should-fix; rounds two to four each closed the previous round's adjacent defect; round four: no findings. Every finding verified by the reviewer by execution. Cross-family review: not required (routine risk class). Orchestrator session.

Review-verdict: MERGEABLE @ 208ae00 Four cold review rounds (Opus high, worktree-isolated, same-family: docs + JS prompts + shell, outside the cross-family rubric). Round one: 3 blocking + 14 should-fix; rounds two to four each closed the previous round's adjacent defect; round four: no findings. Every finding verified by the reviewer by execution. Cross-family review: not required (routine risk class). Orchestrator session.
timothy merged commit 92db6dc81e into main 2026-09-05 00:09:16 +02:00
timothy deleted branch 907-orchestrated-sessions 2026-09-05 00:09:18 +02:00
Sign in to join this conversation.