-
Notifications
You must be signed in to change notification settings - Fork 134
feat: harness reliability — run termination protocol, context-safety margins, compaction fidelity #1171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
feat: harness reliability — run termination protocol, context-safety margins, compaction fidelity #1171
Changes from all commits
Commits
Show all changes
61 commits
Select commit
Hold shift + click to select a range
8380ceb
fix(session): head-truncation fallback for unrecoverable context over…
anandgupta42 361f7c9
feat(harness): proactive overflow estimation + agent finish protocol
anandgupta42 3a5a09b
fix(session): turn-boundary truncation + accurate tail token estimation
anandgupta42 b6f478b
feat(harness): Wave 1 structural fixes — summarizer integrity, trunca…
anandgupta42 0bfa719
feat(harness): Wave 2 core-loop fixes — termination path, task pinnin…
anandgupta42 d48d674
feat(harness): Wave 2 config schema — starvation/idle-done/pin knobs
anandgupta42 20c1661
feat(harness): Wave 3 reliability — context estimator safety margin, …
anandgupta42 3e1e988
test(harness): pin compaction.test.ts isOverflow suite to safety frac…
anandgupta42 b510f46
fix(harness): review-driven hardening — termination false-positives, …
anandgupta42 77abbf0
chore: neutralize internal fixture identifiers and planning shorthand…
anandgupta42 51feb09
fix(harness): track code-fence marker char+length in completion detec…
anandgupta42 3d01d49
fix(harness): compaction breaker returns stop and clears attempts; pr…
anandgupta42 e7b3a48
fix(harness): terminal finish outcomes take precedence over compaction
anandgupta42 5780502
fix(harness): head-truncation fallback cuts only at user boundaries, …
anandgupta42 706084f
fix(harness): run exit-code stickiness — spurious beforeExit can no l…
anandgupta42 f3d35e6
fix(harness): estimator safety fraction applies only to estimated tok…
anandgupta42 ca1f42d
fix(harness): clamp retained tail+ledger below the overflow trigger o…
anandgupta42 cdfb9da
fix(harness): carry the verbatim-tail turn count through the v2 confi…
anandgupta42 6be3d0d
fix(harness): mutation credit on success only, doom-loop stop latched…
anandgupta42 aef20e7
fix(harness): prototype-safe tool-call id tables (Map) + per-processo…
anandgupta42 f333429
fix(harness): scope the idle-done challenge attribution and abort for…
anandgupta42 78ead32
fix(harness): session-state eviction is LRU (refresh on access), not …
anandgupta42 0134e90
fix(harness): batch of small hardening fixes — conservative cap defau…
anandgupta42 8183789
chore: neutralize remaining planning shorthand in comments and test n…
anandgupta42 11b5224
chore: keep the relocated compaction check inside change markers
anandgupta42 c49df38
fix(harness): address external review feedback — small correctness an…
anandgupta42 e9bde73
fix(harness): second-wave review fixes — completion detector and run …
anandgupta42 c9f78ec
fix(harness): extend change markers over the braced tool-input-start …
anandgupta42 2a8850c
fix(harness): close the unpaired change marker in run accounting
anandgupta42 8f765a0
fix(harness): address multi-model review consensus — summarizer starv…
anandgupta42 3137696
fix(harness): fourth-pass review fixes — fence conformance, config bo…
anandgupta42 b9c5dca
fix(harness): fifth-pass review fixes — truncation fallbacks, ledger …
anandgupta42 3e50ebb
fix(harness): include the tool outcome in the doom-loop repeat signature
anandgupta42 a6b6c6d
chore(harness): wrap the dispatch-capped tool-error assignment in alt…
anandgupta42 54e93b7
fix(harness): sixth-pass — correct four defects the bots found in the…
anandgupta42 d55dc8a
Merge remote-tracking branch 'origin/main' into codex/harness-reliabi…
anandgupta42 e9ecdbd
fix(harness): close release-blocking review findings
anandgupta42 5f33361
Merge remote-tracking branch 'origin/main' into codex/harness-reliabi…
anandgupta42 66a2830
fix(harness): harden recovery and replay edge cases
anandgupta42 ef9cc3e
chore(harness): mark nudge generation wiring
anandgupta42 cddf8c2
fix(harness): close termination safety gaps
anandgupta42 420f438
fix(harness): close compaction cleanup review
anandgupta42 e7151f2
fix(harness): fail closed on mixed git commands
anandgupta42 13dfa1d
test(harness): accept async prompt cleanup finalizer
anandgupta42 22ad2f0
fix: close harness lifecycle review findings
anandgupta42 a95f5e5
fix: close remaining harness lifecycle findings
anandgupta42 0011ec3
fix: close final harness release findings
anandgupta42 1999fe3
fix: mark remaining harness release changes
anandgupta42 2592608
fix: mark harness execution cleanup
anandgupta42 bd03a23
merge: origin/main into feat/harness-reliability
anandgupta42 3982f0d
fix(harness): close four review findings — redaction, mutation gate, …
anandgupta42 4bbb259
fix(harness): a space-separated substitution after a configured verif…
anandgupta42 7f5385a
test: use Array.from instead of spread in the surrogate check
anandgupta42 69374ef
fix(harness): exempt UID:GID explicitly, not by absence of letters
anandgupta42 22f405d
fix(harness): redact observation masks before they are replayed
anandgupta42 49e95ef
fix(harness): scope the DONE completion instruction to run mode
anandgupta42 8b4dab7
refactor: convert the five new modules to the prescribed ESM shape
anandgupta42 ee7cb0e
fix(harness): close final reliability review gaps
anandgupta42 c27b880
chore(harness): protect final review fixes with markers
anandgupta42 a1bb0a6
fix(harness): close final context safety gaps
anandgupta42 dda3a2d
fix: resolve remaining harness review findings
anandgupta42 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| # Harness reliability review — deferred follow-ups | ||
|
|
||
| Deferred MED findings from the pre-PR release review (pre-PR adversarial review). | ||
| All 5 HIGH findings plus selected MED/LOW items were fixed on this branch; the | ||
| items below were explicitly deferred and are listed verbatim from the review. | ||
|
|
||
| [MED] packages/opencode/src/tool/truncation.ts:66 — the plain-async truncation path hardcodes 2,000 lines/50KiB while the Effect wrapper honors `tool_output` configuration — MCP output through `prompt.ts` therefore ignores user caps despite the shared-core claim — consolidate the wrappers or pass the resolved configuration through both, with parity tests. | ||
|
|
||
| [MED] packages/opencode/src/session/compaction.ts:663 (renderCarryAnchors) — carry-anchor trimming stops when one item remains — one oversized model-generated "Accomplished" item defeats `maxTokens` and can undo compaction — permit dropping or truncating the final item and assert the rendered result satisfies the cap. | ||
|
|
||
| [MED] packages/opencode/src/cli/cmd/idle-done.ts:157 — every command not recognized as read-only is treated as verification — an exit-zero install, cleanup, deployment, or arbitrary unknown command can satisfy the "green verify" precondition and trigger a false completion challenge — require configured or positively classified verification evidence; unknown commands should be ineligible. | ||
|
|
||
| [MED] packages/opencode/src/session/compaction.ts:70 — observation masks retain the first 80 characters of pruned output, while the ledger retains raw command/path/pattern text — credentials, authorization headers, query data, and signed URLs can survive pruning and be recopied into later synthetic prompts — retain only allowlisted metadata or hashes and apply shared secret redaction. | ||
|
|
||
| [MED] packages/opencode/src/session/compaction.ts:572 (renderLedger) — ledger capping repeatedly joins and re-estimates the whole array while removing one line at a time, after collecting the full session history — this is quadratic in unique writes and adds latency at the critical compaction path — bound collection early and trim using accumulated token costs or a single cutoff search. | ||
|
|
||
| The following items from a later review pass were also considered and deliberately | ||
| deferred (no behavior change on this branch): | ||
|
|
||
| [LOW] packages/opencode/src/cli/cmd/run/run-mode.ts — the run entrypoint writes its process-scoped mode marker into the environment for the process lifetime, and child processes inherit it; a nested interactive server launched from such a session would arm run-mode mechanisms for genuinely interactive clients — clear or scope the marker in the interactive entrypoints (mirroring the existing child-env cleanup for the sibling non-interactive marker). | ||
|
|
||
| [LOW] packages/opencode/src/session/compaction.ts — module-level per-session pin state and per-session tracker read maps grow without bound in a long-lived server process (the pin map has no production eviction path; per-session read tracking keeps one entry per unique path for the session's lifetime) — bound both with the same LRU pattern now used by the session-state stores. | ||
|
|
||
| [LOW] packages/opencode/src/session/prompt.ts — the post-compaction pinned-task reminder re-derives its source by streaming the FULL session history from the database on every generation once a session has compacted — cache the resolved pin source per session or query only the needed boundary messages. | ||
|
|
||
| [LOW] packages/opencode/src/session/termination.ts — the post-compaction three-option completion nudge (including the completion-token instruction) is injected in ALL modes; interactive users can see an occasional bare completion token line with no interactive function — mode-gate the nudge text or document the cosmetic change. | ||
|
|
||
| The following items came from an external multi-reviewer pass over this branch and | ||
| were deliberately deferred (no behavior change on this branch): | ||
|
|
||
| [MED] packages/opencode/src/cli/cmd/run.ts — a prompt retry after a client timeout or connection reset can hit the server AFTER it accepted the original POST, sending the same task again and creating a second user message / duplicate execution — the retry loop has no way to tell "not yet accepted" from "accepted, response lost." Needs a stable idempotency/message key the server honors, or a way to confirm the first attempt was never accepted before retrying. | ||
|
|
||
| [MED] packages/opencode/src/session/compaction.ts (fitHead) — the summarization-request budget reserves a fixed 2,000 tokens for the summary prompt, but the actual assembled prompt (default template + carry anchors + pin-summary addition + first-person reframe, or a plugin-supplied override) can exceed that on an active session — size the reservation from the actual assembled prompt instead of a constant. | ||
|
|
||
| [MED] packages/opencode/src/session/compaction.ts (buildLedger) — on a session's second or later auto-compaction, the ledger is built from the already-filtered/compacted message view, not the full session stream, so verified-write facts from before the first compaction silently drop out of later ledgers — build from the full stream and let selection filter afterward. | ||
|
|
||
| [LOW] packages/opencode/src/session/prompt.ts (uncountedTail) — the proactive overflow estimate sums tool-result tokens on messages AFTER the last-finished assistant message, but a tool call and its result can live on that SAME message when the turn is still mid-flight — those results are excluded from the estimate, so compaction can fire a step later than it should on a heavy tool-output turn. | ||
|
|
||
| [LOW] packages/opencode/src/tool/truncate-core.ts (preview, middle direction) — a degenerate `maxBytes: 1` config (no realistic caller sets this) can still allocate one byte to each of the head/tail halves and exceed the byte budget by a small margin; the equivalent `maxLines: 1` case was already fixed by degrading to tail-only — extend the same degrade to the byte-only case. | ||
|
|
||
| [LOW] packages/opencode/src/session/{termination,nudge,tool-result-cap}.ts, packages/opencode/src/cli/cmd/{run-accounting,idle-done}.ts — these 5 new modules use `export namespace` for organization, which the repo's module-shape convention (`packages/opencode/AGENTS.md`) asks new code to avoid in favor of flat exports + a bottom-of-file self-reexport. ~69 pre-existing files in the package already use the same pattern, so this is consistent with existing debt rather than a regression; fold into a holistic namespace-to-flat-exports cleanup across the package rather than converting these 5 files in isolation. | ||
|
|
||
| [LOW] packages/opencode/test/session/starvation.test.ts — the run-mode "armed" gate test re-implements the gate expression as a local helper instead of importing the real predicate from processor.ts, so a future change to the actual gate (added condition, reordered precedence, renamed exemption) would not be caught by this test — extract the gate into a shared, directly-testable predicate. | ||
|
|
||
| The following items came from a further multi-reviewer pass over this branch and | ||
| were deliberately deferred (no behavior change on this branch): | ||
|
|
||
| [MED] packages/opencode/src/session/llm.ts (addHistoricalToolStubs) — the stub-injection skip was narrowed to `toolChoice === "none" && no tools`, which closes the normal-turn regression, but the compaction summarizer still takes that path with a head that references historical tool calls. The skip rests on the claim that omitting both `tools` and `tool_choice` is universally accepted; the issue the stubs exist to fix concerned validation of referenced historical tool calls, which is orthogonal. Verify against the provider that originally needed the stubs before changing anything — reintroducing stubs here would advertise callable tools on a text-only call, so this is a provider-compatibility question, not a code cleanup. | ||
|
|
||
| [MED] packages/opencode/src/session/starvation.ts (repeatSignature / consecutive-signature counter) — the repeat signature folds the failure message in but the counter accumulates for successful calls too, so three identical SUCCESSFUL calls (re-running the same verification command, polling a status probe) reach the threshold and produce a directive asserting that repeating the call cannot change the result, which is untrue for a polling or verification call. Restricting accumulation to calls that carry a failure message narrows the detector's semantics and its overlap with the identical-args doom-loop ladder; make that change with validation data rather than in review, and note the directive text is prompt-visible. | ||
|
|
||
| [LOW] packages/opencode/src/cli/cmd/run-accounting.ts (termination) — `why_model_stopped` falls back to "stop" when no step-finish reason was ever recorded (an aborted run that never completed a step), so the run record attributes an ordinary stop to a session that never reported one. Distinguishing it needs a new value in the published record's enum, which is an output-contract change for downstream consumers — batch it with the next deliberate revision of the run record schema. | ||
|
|
||
| [LOW] packages/opencode/src/session/prompt.ts (post-compaction pin reminder) and packages/opencode/test/cli/idle-done.test.ts — the idle-done unit tests now pin production event ordering (step-finish part before the step's snapshot patch part) in two dedicated cases, but the shared fixtures still emit the patch first; converge the remaining fixtures on production ordering when the file is next touched. | ||
|
|
||
| The following items came from the fourth review pass over this branch and were | ||
| deliberately deferred (no behavior change on this branch): | ||
|
|
||
| [MED] packages/opencode/src/session/processor.ts (dispatch tool-result cap) — the per-result hard cap is applied on the successful `tool-result` branch only. A `tool-error` still persists an unbounded error string, and an interrupted running tool keeps partial output in metadata that `message-v2.ts` later replays as a tool result, so a failed call with very large stderr can still overflow the next request. Applying the cap to error text and interrupted partial output changes what gets PERSISTED on the failure path, which is where diagnostics come from — size it against real failure payloads before truncating them. | ||
|
|
||
| [MED] packages/opencode/src/cli/cmd/idle-done.ts (verify classification) — with no configured verify command, ANY non-read-only bash command is treated as a verification, so a mutating command that is not on the mutating-heads list (a build script that also writes generated sources, say) can register as the green verify it is not. The mutation watermark now advances for the in-place, redirection, and known-mutating-head forms, but the underlying "not read-only implies verification" inference still needs replacing with an explicit verify classifier. | ||
|
|
||
| [LOW] packages/opencode/src/cli/cmd/run.ts (--max-turns) — `--max-turns 0` disables the limit entirely because the guard is a truthiness check, and negative or fractional values are accepted without validation. For a governance control an explicit zero should not mean unlimited; fix it together with the CLI validation pass that gives the other numeric options explicit range errors, so the behavior change is documented in one place. | ||
|
|
||
| [LOW] packages/opencode/src/tool/bash.ts (child env) — `ALTIMATE_RUN_RESUMED` joins `ALTIMATE_RUN_MODE` as a marker that nested processes inherit from the bash tool's merged env. Its leak direction is benign (a nested run would use interactive pin selection), but it belongs in the same holistic decision about which run-scoped markers get stripped for child processes. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.