fix(orchestration): name the pending-work exception in the nextAction rule

The kernel claimed every unverifiable row reports a `none` nextAction, but
projectFleetNextAction deliberately keeps `inspect` when input or approval is
pending, because that is work a command can actually move. An agent following
the guide literally would have treated a waiting question as another wait-only
row. The guidance test asserted the sentence verbatim, so it agreed with the
wrong text instead of the behavior; it now checks the rule's two halves as
fragments, and the projection suite covers both pending flags directly.
This commit is contained in:
Jinwoo-H
2026-09-07 16:32:00 -04:00
parent b2f5533868
commit 5dc65a3820
4 changed files with 26 additions and 6 deletions
@@ -168,9 +168,11 @@ describe('orchestration kernel', () => {
expect(kernel).toContain(
'`projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv'
)
expect(kernel).toContain(
'A `none` `nextAction` has no argv to run: read `liveness.reason` and keep waiting with `check --wait`. Every `unverifiable` row reports it, `requiresAction` or not'
)
// Why fragments: the kernel must state both halves of projectFleetNextAction's unverifiable
// rule — no argv to run, and the pending-input/approval exception that still returns inspect.
expect(kernel).toContain('A `none` `nextAction` has no argv to run')
expect(kernel).toContain('read `liveness.reason` and keep waiting with `check --wait`')
expect(kernel).toContain('An `unverifiable` row reports it unless input or approval waits')
expect(kernel).toContain('choose `worker-stop` or `worker-abandon`')
})
+1 -1
View File
@@ -138,7 +138,7 @@ After three consecutive empty waits, stop waiting blindly and enumerate with
Run; `--run <run_id>` overrides; the receipt's `scope` names which), acting on
each row's `projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv.
A `none` `nextAction` has no argv to run: read `liveness.reason` and keep waiting
with `check --wait`. Every `unverifiable` row reports it, `requiresAction` or not.
with `check --wait`. An `unverifiable` row reports it unless input or approval waits.
Leave the wait only on positive proof the agent stopped: `exited` liveness, the
worker's own observation of process exit, or a transcript whose final agent turn
sent no `worker_done`. Then load `references/recovery-and-cleanup.md` and choose
File diff suppressed because one or more lines are too long
@@ -545,6 +545,24 @@ describe('fleet liveness and attention after a host verdict', () => {
expect(projected.workers[0]!.nextAction.kind).toBe('inspect')
})
// The exception the kernel's `none` rule names: pending work is something a command can move,
// so these rows keep `inspect` instead of reading as another wait-only unverifiable row.
it.each([{ pendingInput: true }, { pendingApproval: true }])(
'keeps an unverifiable worker with %o inspectable',
(pending) => {
const projected = projectOrchestrationFleet({
workers: [worker('1', pending)],
statuses: [],
now: 10_000
})
expect(projected.workers[0]!.liveness.verdict).toBe('unverifiable')
expect(projected.workers[0]!.nextAction).toEqual({
kind: 'inspect',
argv: ['orchestration', 'worker-show', '--dispatch', '1']
})
}
)
// The live worker-list row from a stopped worker: the same receipt proved the exit,
// called it absence, and pointed back at the command that reported the settlement.
it('never contradicts a proven exit on a stopped worker still owning its terminal', () => {