* fix(#4218): stop the orchestrator steering an executor that is still working An executor with recent RED/GREEN/REFACTOR commits and passing verification had not yet written its SUMMARY because it was finishing closeout. The parent saw no local OS test/build process, inferred an "idle tail", and sent "Finalize immediately" into a working child; in CLI runs the same inference interrupted an executor before GREEN, leaving a RED commit and an uncommitted edit. The stall block said only "if no completion signal, no SUMMARY.md, and no expected-branch commits appear for N minutes" — it never said what to do when commits DO exist and only the SUMMARY is outstanding, never defined the threshold as a period without progress rather than a total runtime, and never ruled out a process listing as an idleness signal. Four rules close that: - the threshold measures a period WITHOUT MEANINGFUL PROGRESS, from the last sign of progress, not from dispatch — a long verification tail is not a stall; - commits + missing SUMMARY + recent activity resolves to KEEP WAITING, with steering, interrupting and re-dispatching each named and forbidden; - urgency/finalization messages ("Finalize immediately" and family) are forbidden outright — they arrive mid-verification and truncate a correct run. The existing user-facing pause is the only sanctioned stop, and `kill and retry` is a clean restart, not a nudge; - the absence of a local OS test/build process is NOT idleness: a native subagent runs in the runtime's own session, and an executor between two tool calls shows no process at all. Progress is judged only by the signals this workflow names. Five prose-contract assertions in tests/execute-phase-wave.test.cjs, all red on next. * fix(#4218): extract the progress policy to a step fragment CI's #1168 gate caught it: execute-phase.md sits 77 bytes under a frozen 93600 ceiling and the four rules added ~2.3 KB. "Extract, not bump" is the repo's stated remedy, and this workflow already carries policy detail that way. execute-phase/steps/executor-progress-policy.md owns the policy. The worktree-recovery arm moved with it — `kill and switch to inline execution` qualifies the stop this policy governs, so it belongs beside the rule about when stopping is sanctioned at all, not stranded in the host. The #3212 recovery OPTIONS stay in the host, where tests/config.test.cjs pins them. The host keeps what must be read before the orchestrator acts: the verdict, the threshold definition, and a pointer that fires before any message is sent to the child. execute-phase.md is now 93475 bytes — 48 SMALLER than next. * chore: add changeset for #4218 * chore(#4218): regenerate the inventory manifest for the new step fragment docs/INVENTORY-MANIFEST.json is the authoritative per-file list behind INVENTORY.md's `<workflow>/steps/*.md` row, so a new fragment has to appear there or gen-inventory-manifest --check reds the lint-tests lane. * chore(#4218): restore the issue ref on the allow-test-rule marker ADR-456 requires a #NNN on a new exemption; the block rewrite that moved the policy into the fragment dropped it. * chore(#4218): regenerate the install-tree fixtures for the new step fragment The fragment ships with the workflow, so every runtime's golden install tree gains one path — gen:install-tree is the generator that owns those fixtures. --------- Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
Apply response_language to all user-facing prose — narration between tool calls, status updates, progress notes, and findings included; preserve code, paths, and identifiers.
|
||||
|
||||
# Executor Progress Policy (#4218)
|
||||
|
||||
Read this before treating any executor as stalled.
|
||||
|
||||
## A working executor is never steered
|
||||
|
||||
The stall threshold measures a period **without meaningful progress**. It is not a maximum
|
||||
total runtime, and it is not a budget the executor has to finish inside. A plan with a long
|
||||
verification or closeout tail legitimately spends many minutes between its last commit and
|
||||
its SUMMARY.
|
||||
|
||||
Reconcile activity as well as artifacts:
|
||||
|
||||
- **Commits exist and SUMMARY.md is missing, with recent meaningful activity → KEEP
|
||||
WAITING.** Do not steer it, do not interrupt it, do not re-dispatch it. Recent
|
||||
RED/GREEN/REFACTOR commits, passing verification, or ongoing reasoning/tool telemetry
|
||||
from the child are all meaningful activity.
|
||||
- **Only after `${EXECUTOR_STALL_THRESHOLD_MINUTES}` of no meaningful progress** — measured
|
||||
from the LAST sign of progress, not from dispatch — may the pause in step 3 fire, and it
|
||||
asks the user; it does not act on its own.
|
||||
|
||||
## Never inject urgency or finalization instructions into a live executor
|
||||
|
||||
Messages of the "Finalize immediately", "wrap up now", "you are taking too long" family are
|
||||
forbidden: they arrive mid-verification and turn a correct run into a truncated one. If an
|
||||
executor must be stopped, the pause in step 3 is the only route, and `kill and retry` is a
|
||||
clean restart — not a nudge.
|
||||
|
||||
## The absence of a local OS test/build process is NOT idleness
|
||||
|
||||
The orchestrator cannot see the child's work that way. A native subagent runs in the
|
||||
runtime's own session, not as a visible local process, and an executor between two tool
|
||||
calls — reasoning, reading a file, waiting on a runtime round-trip — shows no process at
|
||||
all. Judge progress ONLY by the signals this workflow names: commits on the expected
|
||||
branch, the SUMMARY, and the child's own activity. A process listing is not one of them.
|
||||
|
||||
## If a stalled executor ran in an isolated worktree
|
||||
|
||||
`kill and switch to inline execution` edits the primary checkout — see worktree recovery
|
||||
policy (`execute-phase/steps/worktree-recovery-policy.md`). Prefer `kill and retry` in a
|
||||
fresh worktree; inline execution requires explicit confirmation, never the default.
|
||||
Reference in New Issue
Block a user