Repository navigation
v2.17.0 - Merge gate on main, downstream canary, driver conformance, hook golden corpus
Fixed
-
sequant initon an initialized project no longer replaces a hand-writtenAGENTS.md(#1098).initwrote the generated file unconditionally, soinit --agent codex --yeserased a user-ownedAGENTS.mdthatsyncwould have preserved (#990). It now applies the same rule assync— only an unmodified sequant-marked file is regenerated, anything else is reportedpreservedand replaced only by--force. Found by the new downstream canary; the writer-state matrix (#1097) had pinned the four cells as tracked defect #1123 and now expects them preserved. -
Codex usage-limit
turn.failedis now a typed error (#1087).CodexDriverclassified everyturn.failedas a genericSequantError, so a quota-exhausted phase looked like any other failure: the-Qquality loop re-ran into the same limit and--auto-waitcould not engage.classifyCodexTurnFailuremaps the usage-limit text toBillingError(reset beyond a fixed 7-day horizon, past, or unparseable;resetsAtTextandresetsAtin metadata), to aRateLimitErrorwithresetsAtwhen the reset is within the horizon, and throttle wording (rate limit exceeded,429,too many requests) to a retryableRateLimitError. Anything else keepsSequantErrorwith codeturn-failed. -
The pre-PR sync merges
origin/maininto a branch that is already pushed instead of rebasing it (#1069). Rebasing rewrote pushed SHAs and could only be landed by force-push.rebaseBeforePRnow merges when the branch has an upstream other than the base ref (a freshworktree add -b … origin/mainbranch tracksorigin/mainand still rebases), aborts the merge on conflict, and printsmerged origin/mainorrebased onto origin/main. -
A failing QA verdict (
AC_NOT_MET) is now posted to the issue (#1070).postQaVerdictCommentwas gated onresult.success, butAC_NOT_METis reported as a phase failure, so the one review that blocks a merge left no comment — its findings lived only in the rotating, gitignored.sequant/logs. The gate is nowresult.verdictalone: a verdict is only set once one parsed, so turn-capped and unparseable-verdict phases still post nothing (#964 AC-4). EachAC_NOT_METround in a quality loop now posts its own comment. -
parseQaSummarycounts checklist- and bold-prefixed AC lines, and the QA verdict comment no longer printsAC coverage: 0/N metunder a passing verdict (#1073, #1095). A checklist-shaped report (- [x] **AC-1**: …) matched no table row and parsed as0/1 metunder anAC_METverdict. Checklist lines now count (checkbox = MET; a table row wins on a duplicate id), andbuildQaVerdictCommentomits the count line when the parser found no counts oracMet === 0underREADY_FOR_MERGE/AC_MET*.
Added
-
Golden corpus for
pre-tool.sh(#1094).__tests__/fixtures/hook-corpus.jsonlrecords the exit code and block reason the hook gives every command form it has been asked to judge (the existing suite, the hook's own block log, and Claude Code transcripts, secrets and home paths redacted).__tests__/hook-corpus.integration.test.tsreplays it through all three hook copies, so a changed verdict fails until the snapshot is updated in the same PR.npx tsx scripts/hook-corpus.ts --diff <ref>prints only the verdict changes against a ref;--harvestadds new commands. The exec skill now requires listing changed verdicts underHook verdicts changed:whenpre-tool.shis edited. -
CI
canaryjob: a downstream fixture project on every PR (#1098).__tests__/canary/downstream.integration.test.tsdoes a realnpm installof the previous minor release, runs itsinit, customizes the project the way real ones are (constitution,.claude/settings.local.jsonmatcher group, hand-writtenAGENTS.md, tuned.sequant/settings.json, stale^1.20.0devDependency), then drives the PR's build throughsync --dry-run,sync,init --agent codex --yesandupdate. It asserts dry-run decisions equal the files that changed, every customization survives byte-for-byte, no{{PLACEHOLDER}}lands under.claude/,doctorexits 0, and a real MCPinitializeover stdio from the fixture's cwd — with the stale localnode_modules/sequantpresent — reports the PR's version (the #1084 regression test). It runs in a newcanaryvitest project (npm run test:canary) thatnpm testexcludes, so the requiredtestcheck never depends on the npm registry. -
/releasepublishes to thenextdist-tag first and refuseslatestwithout--soaked(#1098). Step 9 now runsnpm publish --tag next; new Step 9b prints the soak checklist and only runsnpm dist-tag add … latestwhen--soakedwas passed. Pinned in all three skill copies byrelease-skill-soak.test.ts. -
Driver conformance suite (#1096).
src/lib/workflow/drivers/__tests__/driver-conformance.test.tsenumerates every registered driver from the registry (listDriverNames()) and checks six contract items per driver: commit inside the sandbox, network, typed error mapping, env injection, structured outcome, and skill loading. The Codex probes run the realcodex sandboxwith the driver's own writable roots.docs/guides/writing-an-agent-driver.mdstates the contract for new drivers. Gaps in the aider and opencode drivers are tracked in #1115. -
Writer state-matrix tests (#1097).
src/lib/__tests__/writer-state-matrix.test.tsrunsinit,syncandupdate(each with and without--force, plus--dry-runfor the latter two) against nine destinations in seven starting states (absent, sequant-owned, user-modified, marker + changed body, local symlink, foreign symlink, directory) on a real temp project, and asserts every dry-run prints the decision its apply run acts on. It pins three writer defects instead of fixing them: symlink write-through (#1122),initreplacing a user-modifiedAGENTS.md(#1123), and a rawEISDIRcrash on a directory at a template path (#1124). -
The ownership gate's side (b) now registers every
writeFile(site individually (#1106). #1090's scan keyed sites by enclosing function and matchedawait writeFile(only, so a second write to a new destination inside an already-registered function, avoid writeFile(...), or a writer declared asconst foo = async () => {…}(which inherited the previous function's key) all passed silently.src/lib/ownership-gate.tsscans everywriteFile(call, attributes it to its innermost named function by brace scope, and keys the registry byfile#function#destination-expression; a test cross-checks the site count against a naive count so a scanner slip fails loudly. Test-only: the three real writers are unchanged. -
Copy-mode
syncnow replaces an existingscripts/devsymlink instead of writing through it (#1053). With no localnode_modules/sequant,syncleft foreign links (an npx cache, a sibling checkout) in place and overwrote their targets. Symlinks are now unlinked and replaced by a copy; regular files are left alone without--force.sync --dry-runlists each replacement asold → (copy), anddoctornames the fix that will actually change a machine-specific link. -
The test suite is hermetic against the orchestrator's environment (#1086).
vitest.global-setup.tsscrubs everySEQUANT_*variable by prefix before any worker starts, so a phase agent running a test file undersequant runsees the same result as a clean shell. Before this,SEQUANT_ORCHESTRATORandSEQUANT_WORKTREEturned three unrelated files red inside every exec phase.
Changed
-
/releaselands the release commit through a PR (#1131). Themainruleset (#1109) rejects direct pushes, so Step 6 now branches, opens the release PR, waits fortestandcanary, squash-merges, and Step 7 tags the merge onmain. -
A merge gate on
main, and a mechanical proof for any "pre-existing failure" claim (#1093). Themainruleset enforced onlydeletionandnon_fast_forward, and targeted no ref at all (conditions.ref_name.include: []), so a PR could merge with stale or red CI — which is how #1036, #1042 and #1048 each shipped with "3 pre-existing failures, unrelated to this diff" in the body a week before those failures (#1086) stopped a gate run.scripts/ruleset-main.sh --printnow emits the corrected payload (~DEFAULT_BRANCH, the existing two rules, and arequired_status_checksrule naming thetestcheck withstrict_required_status_checks_policy: true), documented field by field indocs/internal/graph-2026-09-guard/ruleset.md; the owner applies it by hand in #1109, and the script has--printas its only mode so no agent can.ci.yml'sbuildjob is renamedtestand loses its single-entry matrix, because a matrixed job reports the contextbuild (22.x)and a required context that never reports blocks every merge tomainpermanently.scripts/settle-against-base.sh <test-file>runs a named test at HEAD and again at the base commit in the same worktree with everySEQUANT_*var unset, prints both results as a### Settled against baseblock for the PR body, and restores the branch and any uncommitted work on every exit path — it also distinguishes "the test file does not exist at base" from "the test failed at base", which is the difference between a new test and a pre-existing failure./execnow calls it for every red test outside the diff;/qa§2a setssettle_evidence_statusand §7 step 4 floors an unbacked claim atAC_MET_BUT_NOT_A_PLUS.CLAUDE.mdgains a## Red mainrule — a failingpushrun onmainis reverted first and debugged second, and the revert PR references the run — repeated in the release skill's pre-flight checks. -
The exec skill and
CLAUDE.mdnow state theSEQUANT_MUTATIONmarker contract (#1104).failedTestmust start with the test file path (<file> > <describe> > <case>) and the payload must stay flat;/qa§6i already enforced both without telling authors, which tripped two of the four gate PRs before 2.16.0. A gate test inscripts/exec-skill-marker.test.tspins the section.
Full Changelog: v2.16.0...v2.17.0