feat(voyage): surface iterations_remaining signal in trekexecute
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e34082d79a
commit
65a51b2667
2 changed files with 51 additions and 0 deletions
|
|
@ -850,12 +850,24 @@ progress file.
|
||||||
"steps": {
|
"steps": {
|
||||||
"1": { "status": "pending", "attempts": 0, "error": null, "completed_at": null, "commit": null }
|
"1": { "status": "pending", "attempts": 0, "error": null, "completed_at": null, "commit": null }
|
||||||
},
|
},
|
||||||
|
"iterations_remaining": 25,
|
||||||
"entry_condition_checked": false,
|
"entry_condition_checked": false,
|
||||||
"exit_condition_checked": false,
|
"exit_condition_checked": false,
|
||||||
"summary": null
|
"summary": null
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**`iterations_remaining` (v1.7+, additive-optional):** the remaining
|
||||||
|
recovery/retry budget against the global hard cap (unit = **iterations**, per the
|
||||||
|
brief — token/cost budgeting is out of scope). Seeded to
|
||||||
|
`TREKEXECUTE_MAX_RECOVERY_ITERATIONS` (default 25) at start. **Single writer:** the
|
||||||
|
executor decrements it by 1 on **every recovery/retry iteration** — each step
|
||||||
|
attempt beyond the first, and each Phase 7.6 recovery dispatch. **Backfill-on-absent:**
|
||||||
|
when resuming a legacy `progress.json` that lacks the field, seed it to the cap
|
||||||
|
rather than hard-failing (forward/backward-compatible; shape-validated by
|
||||||
|
`progress-validator.mjs` — non-negative integer, never required-top). The field is
|
||||||
|
present in the spec at minimum — there is **no `(if wired)` conditional**.
|
||||||
|
|
||||||
### Mode-specific behavior
|
### Mode-specific behavior
|
||||||
|
|
||||||
**mode = execute (fresh):**
|
**mode = execute (fresh):**
|
||||||
|
|
@ -1246,6 +1258,14 @@ Record in progress file:
|
||||||
> audit ran** (never before, never in place of it). A transcript that merely
|
> audit ran** (never before, never in place of it). A transcript that merely
|
||||||
> "feels done" is NOT a stop-signal — victory-declaration bias is the exact
|
> "feels done" is NOT a stop-signal — victory-declaration bias is the exact
|
||||||
> failure mode this gate exists to defeat.
|
> failure mode this gate exists to defeat.
|
||||||
|
> 3. **Counter liveness (deterministic, defeats "never decremented")** — the gate
|
||||||
|
> reconciles the budget counter against the ground-truth counters the Phase 7.5
|
||||||
|
> audit already derives:
|
||||||
|
> `iterations_remaining == TREKEXECUTE_MAX_RECOVERY_ITERATIONS − (recovery_depth + Σ steps.*.attempts-beyond-first)`.
|
||||||
|
> A field left un-decremented (or tampered) fails this reconciliation and the
|
||||||
|
> result is OVERRIDDEN `completed → partial`, exactly as the audit-override
|
||||||
|
> already does (`:1221`). So the counter's liveness is auditable at the gate,
|
||||||
|
> not merely asserted in prose.
|
||||||
>
|
>
|
||||||
> Termination authority therefore rests on the deterministic audit + an
|
> Termination authority therefore rests on the deterministic audit + an
|
||||||
> objective stop-signal, never on the model's narrative. Native `/goal` (when
|
> objective stop-signal, never on the model's narrative. Native `/goal` (when
|
||||||
|
|
@ -1523,6 +1543,7 @@ To resume: /trekexecute --resume {path}
|
||||||
"drift_details": [],
|
"drift_details": [],
|
||||||
"recovery_dispatched": false,
|
"recovery_dispatched": false,
|
||||||
"recovery_depth": 0,
|
"recovery_depth": 0,
|
||||||
|
"iterations_remaining": 25,
|
||||||
"legacy_plan": false,
|
"legacy_plan": false,
|
||||||
"progress_file": "{path}"
|
"progress_file": "{path}"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,3 +118,33 @@ test('trekexecute — every recovery/retry loop bounded + 3-axis cap hierarchy (
|
||||||
assert.match(section, /recovery_depth/, 'hierarchy must name per-session recovery_depth axis');
|
assert.match(section, /recovery_depth/, 'hierarchy must name per-session recovery_depth axis');
|
||||||
assert.match(section, /TREKEXECUTE_MAX_RECOVERY_ITERATIONS/, 'hierarchy must name the global budget axis');
|
assert.match(section, /TREKEXECUTE_MAX_RECOVERY_ITERATIONS/, 'hierarchy must name the global budget axis');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// --- S38 loop-discipline hardening: iterations_remaining budget signal (Step 4) ---
|
||||||
|
|
||||||
|
test('trekexecute — iterations_remaining surfaced in progress schema + summary JSON, no (if wired) (SC c)', () => {
|
||||||
|
const text = read();
|
||||||
|
assert.ok(text.includes('iterations_remaining'), 'iterations_remaining must be present (SC c)');
|
||||||
|
assert.ok(!/iterations_remaining[^\n]*\(if wired\)/.test(text),
|
||||||
|
'no "(if wired)" conditional — field present in the spec at minimum');
|
||||||
|
const schemaIdx = text.indexOf('### Progress file schema');
|
||||||
|
assert.ok(schemaIdx >= 0, 'progress schema section missing');
|
||||||
|
assert.match(text.slice(schemaIdx, schemaIdx + 1400), /iterations_remaining/,
|
||||||
|
'progress schema must include iterations_remaining');
|
||||||
|
const sumIdx = text.indexOf('"trekexecute_summary": {');
|
||||||
|
assert.ok(sumIdx >= 0, 'summary JSON block missing');
|
||||||
|
assert.match(text.slice(sumIdx, sumIdx + 900), /iterations_remaining/,
|
||||||
|
'summary JSON must include iterations_remaining (observable)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('trekexecute — iterations_remaining decrement + backfill + gate cross-check documented (SC c)', () => {
|
||||||
|
const text = read();
|
||||||
|
assert.match(text, /decrement[^.\n]*iteration|every recovery\/retry iteration/i,
|
||||||
|
'decrement-per-iteration rule must be documented');
|
||||||
|
assert.match(text, /backfill|seed (it )?to (the )?cap/i,
|
||||||
|
'backfill-on-absent (legacy resume) rule must be documented');
|
||||||
|
const gateIdx = text.indexOf('machine-verifiable completion gate');
|
||||||
|
const gate = text.slice(gateIdx, gateIdx + 2200);
|
||||||
|
assert.match(gate, /iterations_remaining/, 'gate must cross-check iterations_remaining');
|
||||||
|
assert.match(gate, /recovery_depth/, 'gate cross-check must reconcile against recovery_depth');
|
||||||
|
assert.match(gate, /attempts/, 'gate cross-check must reconcile against attempts');
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue