Repository navigation
Added
sequant merge --checkwarns about open PRs outside the batch that touch the batch's files (#1454). A newexternal-pr-overlapcheck lists each open PR on origin whose changed files intersect the batch's, as one warning naming the PR number, head branch and shared paths. PRs whose head branch is in the batch are excluded. The warnings never change the batch verdict or the exit code. Whenghis unavailable or the PR list call fails, the report shows oneOpen-PR overlap check skipped: <reason>line. The file lists come from a newGitHubProvider.listOpenPRFilesSync, andassess-collision-detect.tsgainsformatOpenPrCollisionWarnings, which/assesswill call once the plugin freeze lifts. See ADR-0016.- Standalone
sequant readyrecords its run to.sequant/metrics.json(#929). OneMetricRunper gate pass withsource: "ready", the policy inflags,success/failedfrom the gate result,qaIterations,tokensUsedand the effort/model escalations. Recording is best-effort: a write failure warns on stderr and leaves the exit code and--jsonstdout alone.sequant statsreportsbySourcecounts, and headline averages coverrunrecords only. Records with nosourceread asrun; no migration. - The
mainruleset payload uses a merge queue instead of strict up-to-date (#1350).scripts/ruleset-main.sh --printnow dropsstrict_required_status_checks_policy, adds a squashmerge_queuerule and listscanarybesidetest(the live ruleset already required both).ci.ymlruns onmerge_groupso queued PRs get their checks. The live ruleset is unchanged: applying the queue waits on #1368, where/releaselearns to wait for a queued merge before tagging. /execand/qarun the tests a branch affects instead of the full suite (#1349).npx vitest run --changed origin/main(jest:--changedSince), plus ACEvidence:test files, changed test files and tests that mention a changed file. Stacks with no affected mode keep the full command (resolveAffectedTestCommand). CI still runs everything.- gitleaks scans every PR for the leak classes generic scanners miss (#1352).
.github/workflows/gitleaks.ymlruns a pinned, checksum-verified gitleaks with.gitleaks.tomlonpull_requestandmerge_group, and fails when anentire/*ref exists onorigin. Project rules flag real home paths (/Users/<name>/,/home/<name>/) and Claude session links and ids; placeholder names are allowlisted by name.scripts/gitleaks-precommit.shis an optional local gate that also loads an untracked.gitleaks.local.toml. GitHub secret scanning, push protection and Dependabot security updates are now enabled on the repo. The committed eval results and two test files no longer carry a real home path or session id. Seedocs/reference/secret-scanning.md. /qaenforces a follow-up ledger, and the PR body lists QA's follow-ups (#1249). Every item an earlier phase deferred (spec's Open Questions and out-of-scope notes, exec's PR-body follow-ups, QA's owndocumentfindings) must endfiled #N,fixed in this PRordropped: <reason>. An unresolved item caps the verdict atAC_MET_BUT_NOT_A_PLUSthrough a new §7 gate,followup_ledger_status, in both Standard and Simple Fix mode. QA writes eachdocumentfinding's resolution as a suffix on its description, andsequant run's PR body renders the latest QA pass'sdocumentfindings as a## Follow-upschecklist; a finding with no resolution readsunresolved. The sibling-site scan now names the defect class it searched (e.g. "every git call without-C <target>") rather than searching for the changed code, and an AC that promises end-to-end delivery (e.g. "surfaces in the PR body") can no longer be marked MET with that path under "Not tested": QA runs it or marks the AC PENDING.qa/SKILL.mddid not grow. See ADR-0007.- A PR from a
sequant runthat QA has not reviewed says so (#1246). The automated PR body carries one line,> **QA has not run** — phases run: spec, exec. …, until QA has reviewed the latest exec: a qa pass after the run's last exec, or, in a run with no exec, a qa an earlier run completed (--resumepast qa). The post-QA update replaces it with the verdict note, or drops it forREADY_FOR_MERGE, and a later qa-only run on the same PR removes it too. A run that skips qa (--phases exec, MCPphases: "spec,exec") or stops before it keeps the line. No draft PRs; the PR-creation gate is unchanged. The MCPsequant_runphasesdescription no longer offers'spec,exec'as a way to "skip QA".
Changed
updaterefuses on a skills-only install (#1482). It read no install scope, so it wrote hooks, agents andsettings.jsonover an install that chose skills only. On a partial scope it now writes nothing, exits non-zero and namessequant sync --only skills. Full installs are unchanged. ADR-0015 amended.- The
@anthropic-ai/claude-agent-sdkfloor is^0.3.296(#1440). Phase agents run the Claude Code bundled with the SDK (2.1.296 at 0.3.296), not the installedclaude, so a fresh install can no longer resolve a bundle older than the 2.1.290–2.1.296 fixes (remote MCP reconnect backoff,.mcp.jsonservers switched off for a folder,rate_limit_eventextra-usage flag, file-read-state,disableClaudeAiConnectors/allowedMcpServerscoverage). The README Prerequisites note says which Claude Code a phase agent runs.
Removed
- Dead
src/lib/plugin-version-sync.tsmodule and its test (#1325). Nothing in production calledcheckVersionSync; CI'sCheck version syncstep does the check.docs/internal/plugin-updates.mdnow names that step.
Fixed
sequant_runno longer reports another run's issues and verdict when its own run log is missing (#1515). A runId lookup miss fell back to the newest log in the time window, so parallel runs whose log was never written showed a sibling run's result.resolveRunLognow retries the exact runId lookup (3 × 200 ms) and returnsnullon a miss; the response carriesrunLogMissing,requestedIssuesand anotesaying the result is not a parsed log. The no-runId path is unchanged.new-feature.shreuses the issue's local branch when no worktree has it (#1491). It printed advice (one option wasgit branch -D) and exited 1, whileensureWorktreechecks the branch out as is. It now runsgit worktree add <path> <branch>with no fetch or reset, like the TS side, and keeps a recordedsequantBase. The existing-worktree lookup matches the branch exactly, sofeature/7-xno longer matchesfeature/7-xy.doctorSkills Version check warns on content drift (#1490). At a matching version it always passed withUp to date, while the pre-flight banner in the same run reported differing files. It now warns with the file count and the banner's remedy, which both read from one function.doctorno longer sends a skills-only install toinit --force(#1489). A missingAGENTS.mdraised a warning namingsequant init --force, which installs hooks, agents and settings the install skipped. On a partial scope the check now passes and saysAGENTS.mdis outside the install. Full installs keep the warning.sequant merge --checkworks on a repo whose default branch is notmain(#1484). The combined-branch test, each branch'sfilesModifieddiff, overlap detection and the residual pattern scan all hard-codedorigin/main. On amasterrepo the diffs failed, their callers read the empty output as "no changes", and the overlap checks (including #1454's external-PR overlap) reported clean.runMergeChecksnow resolvesorigin/<default branch>once and passes it to all four. A failedfilesModifieddiff is no longer an empty list: the report carries an error finding for that branch naming the git command, and the batch isBLOCKED. The worktree path ofresolveBranchesalso uses the resolved base instead ofmain.- Plain
syncanddoctorno longer steer a skills-only install into a full one (#1485). A plainsyncon a manifest withgroups: ["skills"]keeps that scope: it counts drift and writes only.claude/skills/, and widening issequant init.doctor's Skills Version check namessync --only skills, anddoctoradvises the"groups": ["skills"]hand edit for a legacy manifest with no hooks, settings or agents. See ADR-0015. sequant runreuses an issue's existing branch whatever the issue's title (#1487). The branch was looked up by a name rebuilt from the title. A failedgh issue view(titleIssue #N) or a retitled issue therefore cut a second branch,feature/N-issue-n, from the base, and QA reviewed the base instead of the PR.ensureWorktreenow finds the issue's branch by number: a localfeature/N-*(which covers worktrees) ororigin/feature/N-*. One match is reused. With several matches, the one the title names is used. Otherwise no worktree is created and every candidate is listed. When the title could not be read, a non-dry run now prints a warning that names the issue and thegherror.new-feature.shstarts fromorigin/<branch>when the remote already has the issue's branch (#1486). It always cut the worktree fromorigin/<base>, so a pushed PR branch lost its commits and a push from the new worktree diverged. The script now portsresolveBranchStartRef: it usesorigin/<branch>unless that branch is merged into the base (merge commit or squash), and--print-start-ref <branch> <base-ref>exposes the result for the parity test.- An issue whose worktree could not be created fails, and no longer runs its phases in the main checkout (#1469). When
git worktree addfailed,sequant rundropped the issue from the worktree map and ran spec, exec and QA in the main checkout on whatever branch was checked out there. Under worktree isolation the issue now fails before any phase runs, and itsabortReasonand the run log carry git's stderr.IssueExecutionContextgets an explicitworktreeRequiredflag, because a missingworktreealso means "isolation off". The provisioning summary counts failures (N created, M reused, K failed). Two CLIs provisioning in one repo race on.git/config.lock. Acould not lock config filefailure is now retried up to 5 times with 100–800 ms backoff, and a retry adds the branch the failed-battempt already created.recordWorktreeBaseRefuses the same retry and warns on a final failure instead of swallowing it. - Phase agents get
SEQUANT_BASE_BRANCH(#1460).sequant runnever set it, so skills that read${SEQUANT_BASE_BRANCH:-main}always usedmain, and on a repository whose default branch ismastereveryorigin/mainin a phase's git commands failed. Every phase, spec included, now gets the base branch name fromresolveBaseRef. When no base is recorded,resolveBaseReffalls back toorigin/<default branch>of the repository it reads, not a literalorigin/main. - Uncommitted changes to
.claude/hooks/now trigger the same warning as uncommitted skills (#1456). Phase worktrees check out the committed hooks, so asyncthat rewrotepre-tool.shand was never committed left every phase on the old guard logic with no signal.getSkillsCommitStatelists tracked-but-modified files under both directories, and the shared warning names each directory. Untracked hooks stay silent.run,doctorandsyncprint it through their existing call sites. - A worktree for a non-default bare base (
--base release) is cut fromorigin/<base>after a fetch (#1406). It was cut from the local branch, which can lag or not exist, while staleness and the recordedsequantBaseRefreadorigin/<base>. Creation, recreate and the record now share one ref. Stacked predecessors and bases with noorigin/<base>stay local. - A
SEQUANT_MUTATIONmarker whose text contains}no longer parses to nothing (#1305). The parser matched to the first}, so amutationsuch asregex changed to /x{9}/made the marker vanish from QA §6i and from the exec carry-over. It now matches to the-->terminator, andrenderMutationMarkerescapes-->and<!--inside a value so it round-trips. Existing markers, including ones whose JSON spans lines or whose values hold an HTML comment, parse as before. - A skills-only install no longer warns about files it never installed, and its remedy no longer overwrites the constitution (#1409). After
init --manifest-only+sync --only skills, every command warnedN file(s) differfor the hooks, agents,scripts/devand.claude/settings.jsonthe install left out, and suggestedsync --force, which overwrites.claude/memory/constitution.md..sequant-manifest.jsonnow records the install scope in agroupsfield (init --manifest-onlywrites[],sync --only skillsadds"skills", a full sync of a manifest without a scope removes it;updaterefuses on a partial scope). Drift is counted only inside that scope, and the remedy issync --only skills --force. A manifest without the field is a full install, so existing installs are unchanged. See ADR-0015. - A fresh machine's first worktree for a pushed PR branch starts from
origin/<branch>, and a worktree already atorigin/<branch>is not recreated on every run (#1405). With no local branch,ensureWorktreecut a new branch from the base even when the remote had the PR's commits. It now starts fromorigin/<branch>with upstream tracking, unless that branch is already merged into the base (merge commit or squash) or was deleted from the remote. A stale, clean worktree whose HEAD equalsorigin/<branch>is reused instead of being deleted and re-added. The check uses localgitonly, with noghcall. See ADR-0014. - Phase agents no longer receive the user's claude.ai connectors (#1396). The Claude Code driver passes
settings: { disableClaudeAiConnectors: true }toquery(), so Gmail, Docs and other account connectors the SDK adds outside the #936 allowlist are not reachable from abypassPermissionsphase agent. Thesequantserver,.mcp.jsonservers andmcpAllowlistnames are unaffected. - A stale worktree for a pushed PR branch is recreated from
origin/<branch>, not from the base (#1380). When a worktree was more than 5 commits behind and clean,ensureWorktreedeleted the local branch and cut a new one from the base, so a qa re-run reviewed the base without the PR's commits. The recreate now starts fromorigin/<branch>when the remote has it. If the start ref does not contain the worktree's HEAD (an offline fetch, or a local-only branch with commits of its own), it prints a warning and keeps the existing worktree. See ADR-0013. - First-run papercuts from a stranger smoke test (#1387). The "Running sequant from local node_modules" warning no longer tells a project that declares
sequantinpackage.jsonto uninstall it (a stray, undeclared install still warns).init's next steps now say to commit the generated files and runnpx sequant run <issue>.doctor'sWarnings:count includes the upstream #43869 notice it prints, so a healthy install now reads "All checks passed (1 warning)" unless--quiet. Thestill runningline shows time since the current phase started, not the issue.run --helpdescribes--no-rebaseas skipping the rebase onto the base branch. sequant run --base <branch>opens the PR against<branch>(#1386). The worktree was cut from--baseand rebased onto it, butcreatePRsetgh pr create --baseonly under--stacked, so a plain--base(orsettings.run.defaultBase) opened the PR against the repo default and its diff carried every difference between the two branches.createPRnow takes the run's one resolved base and derives the PR target throughresolvePrBase;--stackedstill wins for non-last links, and a base equal to the repo default passes no--base, as before.- Uncommitted skills no longer pass silently (#1354).
sequant runchecks phase worktrees out from git, so asequant syncthat was never committed never reached a phase, and theSkills are outdatedwarning, which reads the on-disk version, named a version the phases weren't using.sequant run(before provisioning worktrees),doctor,syncandupdatenow warn when tracked files under.claude/skills/have uncommitted changes, naming the committed version phases will run. See troubleshooting. - Worktree provisioning's skip message now says
no package.json at the worktree rootfor JS stacks, so a repo whose package lives in a subdirectory sees why nothing installed. Thenpm ciEUSAGE itself was fixed by #1196 (#1243) - The run dashboard shows phases in the order they run (#1356). When label detection adds a phase to
--phases(e.g.exec,qagainstest), the dashboard's issue card listed it last, afterqa, because the orchestrator never applied the resolved phase plan the plain-text renderer already used. It now does, soexec ▸ test ▸ qareads correctly from the start, and a configured phase the plan dropped no longer lingers as pending. .sequant-manifest.jsonends with a newline (#1355).sequant init,syncandupdatewrote the manifest with no final newline, so a repo whose CI runsprettier --checkfailed after every sync.- Parallel runs (every MCP
sequant_run) no longer drop per-issue warnings (#1342).runIssueWithLoggingsilenced all per-issue output in parallel mode, including warnings: the spec-recommendation parse failure, "PR after exec failed", the checkpoint-commit failure, relay and ready-gate warnings, and evidence bundles. They now go through awarnchannel built onbracketedConsoleLog, which uses the renderer'sappendNoticeunder the TUI andconsole.logotherwise. Progress lines stay suppressed. - The recommended Claude Code version is now 2.1.288 (#1346). Sequant relies on Claude Code's native dangerous-
rmanalyzer, which before 2.1.288 let abash -corsh -cscript run a dangerousrmwithout a prompt underbypassPermissions, the mode phase agents run in. The README gives the new recommendation and the reason, anddocs/THREAT-MODEL.mdlists the analyzer as a defense with that version dependency. ADR-0009 (#1345) is accepted. /verifyignores Claude Code's pre-commit nudge (#1345). Claude Code ≥ 2.1.286 tells Claude to run any project skill namedverifybefore committing, and Sequant ships one that expects an issue number and posts to GitHub./verifynow returns a one-line no-op, with no command, comment or question, when a Sequant phase other than verify calls it (checked throughSEQUANT_PHASE, even with an issue number), when it gets no issue number, or when Claude calls it on its own before a commit. A/verify <N>you ask for, and the verify phase, run as before.sequant update/syncdeliver the guard to existing projects./upstreamnow files changelog lines that name a skill Sequant ships under a new Name Collision category instead of No Action, and warns if it cannot find the shipped skill list. See ADR-0009.- The
sequant runPR body carries exec's## Summaryagain (#1311). The Claude Code, Codex and opencode drivers joined the agent's text messages with no separator, so a message ending mid-line ("Now the edits.") glued onto exec's final## Summaryand the line-anchored heading was never found; the PR got the placeholder body even though exec wrote a full summary (4 of 4 runs on 2026-10-05). Messages now start on their own line. The placeholder warning also printed nowhere under the MCP server, because per-issue logging is off in parallel mode; it now prints there and names the exec output's length, so "exec wrote nothing" and "exec's summary was missed" read differently. /releasebumps the plugin manifest users install (#1301). Step 4.5 edited and staged the root.claude-plugin/plugin.json, which nothing installs; the shippedplugin/.claude-plugin/plugin.jsonwas bumped only byscripts/release.sh. The root manifest is retired:/release(validate, bump,git add, summary),scripts/release.sh,scripts/prepare-marketplace.ts,plugin-version-sync.tsand the CI plugin checks now readplugin/.claude-plugin/plugin.json, the one manifest.plugin-install-test.ymlnow triggers ontemplates/memory/**, the source of the shippedplugin/memory/, instead of the repo's own rootmemory/./qa,/cleanand/setupno longer pre-approve file-deleting or API wildcards (#1292)./qadropsBash(gh api:*), which also allowedgh api -X DELETE./cleandropsBash(rm:*)andBash(find:*), and narrowsBash(mv:*)to moves intodocs/archive/andscripts/archive/./setupnarrowsBash(cp:*),Bash(cat:*)andBash(sed:*)to the files it writes or reads. When you run these skills by hand, the removed commands now ask for permission;sequant runphases are unaffected. The #1287 gate test now also rejectsBash(<tool>:*)forgh api,rm,mv,find,sedandcp, unless an allowlist entry gives a reason.- The docs no longer cite the 44% second-look figure (#1309).
docs/reference/ready-command.md,docs/features/run-ready-gate.md,docs/investigations/ready-gate-backtest.mdanddocs/internal/what-weve-built.mdnow give the figure rebuilt from the surviving transcripts: 10 of 27 second looks caught a would-ship bug, 7 of them only after the human's "any gaps?" follow-up and 3 in the first verdict. They linkdocs/investigations/qa-second-look-dataset.md, and no longer state that the in-orchestrator QA under-catches, which the transcripts cannot show. - A failed
ghcall no longer reads as a posted comment or label (#1312).GitHubProvider.postComment,addLabelandremoveLabelranspawnSyncand never looked at the result, so a non-zero exit, a timeout or a failed spawn resolved as success andpostQaVerdictComment's "Failed to post QA verdict comment" warning could never fire. They now throw withgh's stderr, so a lost QA verdict is logged. postPRCommentno longer fails silently, and the ready gate no longer swallows a failed report post (#1315).GitHubProvider.postPRCommenthad the same uncheckedspawnSyncas #1312 and now throws withgh's stderr. The ready gate'spostReportcatch was empty; it now logsFailed to post ready-gate report for #N: <error>(through the new optionallogoption, defaultconsole.warn) and stays non-fatal.PlatformProvider's doc comment now states the contract: every async method rejects on failure, exceptcheckAuth, which answersfalse.getIssueCommentsnow follows it and rejects instead of resolving[]whenghfails.- Skills no longer break when Claude Code substitutes
$0/$1in their text (#1289). Claude Code replaces positional$Nin a skill's body when the skill gets arguments, and fenced code blocks are not exempt:/qa 1249turnedsubstr($0, 2)intosubstr(1249, 2), silently breaking §6c's added-line extraction and the manual-AC attribution program. Awk fields in the shipped skills are now written$(N), which Claude Code leaves alone and which also works for drivers that read the files untemplated (codex, opencode). The §6c extraction moved toqa/scripts/added-lines.sh. A new CI lint,npm run lint:skill-argument-substitution, rejects any$Nin a SKILL.md body, escaped or not. - A ladder halt is recorded, not only printed (#1254). When a run halts on
SPEC_DIVERGENCE,DIVERGENCE_SUSPECTorTOP_OF_LADDER, the issue result carries the failure categoryladder_halt, anabortReasonsuch asladder halt: SPEC_DIVERGENCE in exec (AC-2)and the evidence bundle; the run log's issue entry recordsfailurewith the same reason and bundle. A phase that declaredSPEC_DIVERGENCEnow reads failed in state (withoutcomeanddivergenceAcs), in its run-log row and in its progress event, even when the agent exited cleanly. The MCPsequant_runresult nameshaltReason,declaredAcsandabortReasonper issue. Any other failure no failed phase accounts for (e.g. a rebase-conflict abort) is now also loggedfailurewith itsabortReason, instead ofsuccess. - Under
sequant run, phase agents no longer edit the PR body (#1302). A quality-loop exec had rewritten a PR's body itself (gh apiPATCH), because QA's missing-marker gap pointed at the PR body. That body then read as hand-edited, and every later sequant update (QA verdict note, follow-ups) was dropped. The exec skill now forbids changing the PR's body or title by any means under the orchestrator, and routes the AC table, mutation markers, settle-against-base blocks and hook-verdict changes into its final## Summary, which the orchestrator carries into the body. QA's §6iMissinggap names exec's final Summary, not the PR body. - The
sequant runPR body keeps exec's mutation markers and AC table when exec wrote no## Summary(#1297). The body now carries every validSEQUANT_MUTATIONmarker from exec's final output, including markers exec fenced in a code block (outside the 4,000-character cap, never duplicated) and, with no Summary, the AC table; the placeholder only appears when there is nothing to carry, and the run output warns when it does. The issue state's stored summary carries the markers too, so a qa-only re-run's PR update keeps them. - Skills no longer pre-approve broad shell access in
allowed-tools(#1287). BareBash(/fullsolve,/loop,/test),Bash(*)(/verify), tool wildcards such asBash(git *)andBash(gh *)(/assess,/solve,/improve,/upstream,/merger), and runner wildcards such asBash(npx tsc:*)andBash(node -e:*)are replaced by the subcommands each skill runs, or removed./test's unrenderedBash({{PM_RUN}} dev:*)becomes literalnpm/pnpm/yarn/bundev entries. When you run these skills by hand, commands outside their list now ask for permission;sequant runphases are unaffected. A new gate test rejects these forms in all four skill copies. /qa§6i counts changed.github/workflows/*.ymland*.yamlfiles in its diff-file list, so aSEQUANT_MUTATIONmarker naming a CI workflow step classifiesvalidinstead oftest_not_in_diff(#1276)- A quality loop that ends
qa AC_NOT_MET → loop → qa NEEDS_VERIFICATIONrecordsawaiting_verificationinstead ofready_for_merge, and the PR-body QA note and the MCPsequant_runresult (verdict,acMet/acTotal,gaps,findings) report the latest qa pass, not the first (#1245). The latest-attempt accessor moved tosrc/lib/workflow/latest-phase.ts; the failed-phase scans inderiveFailureCategoryandrun-display.tsuse it too
Full Changelog: v2.19.0...v2.20.0