Repository navigation
v3.16.5
What's new
Bug Fixes
Workflow guard replay keeps the original failure reason (#954). When a completion tool call was delivered twice for the same callID, two terminal outcomes lost their specific reason code on the second delivery: a mismatched completion target (invalid-transition) and a host-tool failure (failed-operation) both fell through to a generic guard-unavailable. Both branches now record a terminal completion and replay it with the reason code that produced it. The host-tool-failure record is flagged metadata-only, so a replay corrects the guard metadata without overwriting the host's own failure title and output — that text is evidence and has to survive untouched.
Two related fidelity gaps closed alongside it. TerminalComplete.target is now optional, so a genuinely-unknown target is omitted from the emitted workflowGuard metadata rather than fabricated as unit. And stale questionAttestation stripping is now gated on the guard instance's own sourceDigest, so two guard instances sharing an output reference can no longer erase each other's attestation evidence. All three gaps were safety-neutral — the host failure sentinel survived in every case, and no fail-closed behaviour was weakened — but each degraded the accuracy of the completion evidence record the guard exists to protect.
Code Refactoring
The host-contract CI guard is now a tested script (#949). The host-contract job's skip/pass guard lived as a ~135-line inline .mjs heredoc inside .github/workflows/main.yaml. It parsed JUnit XML and the bun test log with regexes and gated release — an untested parser sitting on a required release gate, invisible to biome check ., typecheck:all, and bun test. It now lives in scripts/host-contract-guard.ts as pure functions plus a thin CLI wrapper, with unit tests covering all five failure conditions, XML entity decoding, and the artifact-reading paths.
The move also fixed a gating bug that had survived two rounds of review. The guard step's if: condition contained no status check function, so GitHub Actions applied its own implicit, job-scoped success() on top — skipping the guard on exactly the runs where its diagnosis matters most, a failing suite. Adding !cancelled() fixes it, and tests/unit/host-contract-workflow.test.ts now mechanically pins the condition's properties so this class of regression fails a test instead of shipping a third time. A missing or unreadable artifact is now reported as the contract failure it is — routed through the same evaluate() path a truncated one already used — instead of a raw ENOENT crash.
Tests
A mis-detected quote can no longer blind the fixture-version scanner (#947). The scanner in tests/unit/opencode-pin.test.ts enforces that every OpenCode-shaped version literal in the allowlisted fixture files stays inside the unpinnable 9999.x sentinel range. Its tokenizer could misread an apostrophe inside a regex literal in an unrecognised position — if (a && /don't/.test(b)) — as an opening quote; the phantom string then searched forward for the next real quote and swallowed everything in between.
Rather than extend the regex-position heuristic for a third time, this applies a structural bound: a ' or " string cannot span a real newline in JS/TS, so the tokenizer abandons an unterminated span the moment it reaches a line break. That caps the damage from any mis-detected quote, whatever the trigger, to the rest of the current line. Backtick template literals are excluded, since they legitimately span newlines. Three regression tests pin each of the three historical hazards, and the plant-at-every-line-position probe reports zero genuine misses across all four scanned files.
Documentation
How to measure a source scanner's blind spots (#950). A new docs/solutions/best-practices/ entry records the lesson from that scanner work: a guard that scans source text has to have its blind spots measured, because reading it finds nothing. Three successive designs each read as correct and each had a measured hole — context-anchored matching missed the four literal shapes the scanned file actually used, a blanket match with a comment stripper went blind over 143 of 143 positions after a single // inside a string desynced quote parity, and removing comment awareness entirely missed 166–580 positions to a phantom string opened by an apostrophe in prose. The doc records the probe that found all three, the structural bound that caps any future mis-detection regardless of cause, and the cheap positive result: the same probe cleared the sibling guards in tests/unit/spawn-and-signal-conventions.test.ts at zero misses, so the technique validates as well as condemns.
Also in this release
- dev: OpenCode updated to v1.18.28 (#944) and v1.18.29 (#952).
- dev: Playwright updated to v1.63.0 (#953); TypeBox updated to v1.3.26 (#948).