sequant 2.11.0 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +47 -3
  4. package/dist/dashboard/server.js +4 -0
  5. package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +1 -1
  6. package/dist/marketplace/external_plugins/sequant/.mcp.json +1 -1
  7. package/dist/marketplace/external_plugins/sequant/README.md +9 -0
  8. package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +331 -12
  9. package/dist/marketplace/external_plugins/sequant/skills/_shared/references/subagent-types.md +7 -18
  10. package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +5 -1
  11. package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +62 -8
  12. package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +187 -28
  13. package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +127 -23
  14. package/dist/marketplace/external_plugins/sequant/skills/merger/SKILL.md +130 -13
  15. package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +306 -8
  16. package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +79 -0
  17. package/dist/marketplace/external_plugins/sequant/skills/spec/SKILL.md +42 -20
  18. package/dist/marketplace/external_plugins/sequant/skills/spec/references/recommended-workflow.md +14 -1
  19. package/dist/marketplace/external_plugins/sequant/skills/test/SKILL.md +1 -1
  20. package/dist/marketplace/external_plugins/sequant/skills/testgen/SKILL.md +23 -6
  21. package/dist/src/commands/doctor.js +20 -18
  22. package/dist/src/commands/ready.js +7 -1
  23. package/dist/src/commands/status.js +4 -0
  24. package/dist/src/lib/ac-linter.js +26 -0
  25. package/dist/src/lib/ac-parser.d.ts +40 -0
  26. package/dist/src/lib/ac-parser.js +202 -16
  27. package/dist/src/lib/markdown-fence.d.ts +24 -0
  28. package/dist/src/lib/markdown-fence.js +51 -0
  29. package/dist/src/lib/mcp-config.d.ts +24 -0
  30. package/dist/src/lib/mcp-config.js +51 -0
  31. package/dist/src/lib/scope/analyzer.d.ts +4 -0
  32. package/dist/src/lib/scope/analyzer.js +7 -1
  33. package/dist/src/lib/settings.d.ts +73 -14
  34. package/dist/src/lib/settings.js +45 -3
  35. package/dist/src/lib/system.d.ts +7 -3
  36. package/dist/src/lib/system.js +7 -3
  37. package/dist/src/lib/test-tautology-detector.js +50 -3
  38. package/dist/src/lib/workflow/batch-executor.d.ts +20 -1
  39. package/dist/src/lib/workflow/batch-executor.js +81 -6
  40. package/dist/src/lib/workflow/config-resolver.d.ts +30 -2
  41. package/dist/src/lib/workflow/config-resolver.js +59 -2
  42. package/dist/src/lib/workflow/drivers/agent-driver.d.ts +14 -0
  43. package/dist/src/lib/workflow/drivers/claude-code.js +36 -4
  44. package/dist/src/lib/workflow/metrics-schema.d.ts +10 -1
  45. package/dist/src/lib/workflow/metrics-schema.js +13 -1
  46. package/dist/src/lib/workflow/metrics-writer.d.ts +3 -1
  47. package/dist/src/lib/workflow/mutation-marker.d.ts +86 -0
  48. package/dist/src/lib/workflow/mutation-marker.js +97 -0
  49. package/dist/src/lib/workflow/phase-executor.d.ts +17 -0
  50. package/dist/src/lib/workflow/phase-executor.js +60 -6
  51. package/dist/src/lib/workflow/qa-gaps-marker.d.ts +38 -0
  52. package/dist/src/lib/workflow/qa-gaps-marker.js +66 -0
  53. package/dist/src/lib/workflow/ready-gate.d.ts +25 -1
  54. package/dist/src/lib/workflow/ready-gate.js +81 -11
  55. package/dist/src/lib/workflow/reconcile.js +4 -2
  56. package/dist/src/lib/workflow/run-log-schema.d.ts +120 -0
  57. package/dist/src/lib/workflow/run-log-schema.js +40 -0
  58. package/dist/src/lib/workflow/run-orchestrator.d.ts +18 -0
  59. package/dist/src/lib/workflow/run-orchestrator.js +38 -2
  60. package/dist/src/lib/workflow/state-cleanup.d.ts +4 -4
  61. package/dist/src/lib/workflow/state-cleanup.js +9 -5
  62. package/dist/src/lib/workflow/state-schema.d.ts +10 -1
  63. package/dist/src/lib/workflow/state-schema.js +13 -1
  64. package/dist/src/lib/workflow/types.d.ts +20 -0
  65. package/dist/src/mcp/tools/run.js +10 -1
  66. package/package.json +13 -12
  67. package/templates/hooks/pre-tool.sh +108 -17
  68. package/templates/memory/constitution.md +112 -45
  69. package/templates/skills/exec/SKILL.md +1 -1
  70. package/templates/skills/fullsolve/SKILL.md +62 -9
  71. package/templates/skills/loop/SKILL.md +71 -12
  72. package/templates/skills/merger/SKILL.md +32 -3
  73. package/templates/skills/qa/SKILL.md +247 -2
  74. package/templates/skills/spec/SKILL.md +11 -5
  75. package/templates/skills/test/SKILL.md +1 -1
@@ -39,7 +39,55 @@ else
39
39
  HOOK_CWD=$(echo "$INPUT_JSON" | grep -oE '"cwd"\s*:\s*"[^"]+"' | head -1 | cut -d'"' -f4)
40
40
  # For Bash tool, extract command from tool_input; for others, extract the whole object
41
41
  if [[ "$TOOL_NAME" == "Bash" ]]; then
42
- TOOL_INPUT=$(echo "$INPUT_JSON" | grep -oE '"command"\s*:\s*"[^"]+"' | head -1 | cut -d'"' -f4)
42
+ # Escape-aware extraction (#963 gap B): the naive `grep -oE '"[^"]+"'`
43
+ # form (still used for the simpler fields above, where an embedded
44
+ # `\"` is implausible) stops at the FIRST escaped quote inside the
45
+ # JSON string, truncating any command containing one — e.g.
46
+ # `git commit -m "msg with \"quotes\""` would be cut down to just
47
+ # `git commit -m ` before it ever reaches the guards below.
48
+ #
49
+ # sed's `(([^"\\]|\\.)*)` captures the full escaped run — any run of
50
+ # non-quote/non-backslash chars, or a backslash paired with whatever
51
+ # follows it — up to the closing unescaped `"`. `JSON.stringify`
52
+ # guarantees the whole payload is one physical line, so a single
53
+ # sed pass is enough (no multi-line `-z` needed, which BSD sed lacks
54
+ # anyway).
55
+ #
56
+ # The captured text still carries JSON string escapes literally
57
+ # (`\"`, `\\`, `\n`, `\t`, ...); the awk pass resolves them in ONE
58
+ # left-to-right scan, consuming two characters per recognized
59
+ # escape. That ordering is what a chain of separate sed/tr
60
+ # substitutions cannot get right without a placeholder: a literal
61
+ # `\\n` in the original command is an escaped backslash (`\\`)
62
+ # immediately followed by a literal `n`, and must stay a backslash
63
+ # plus 'n' — not become an escaped-newline (`\n`) if the two escapes
64
+ # were resolved out of order. Scanning once and advancing past both
65
+ # characters of whichever escape is recognized sidesteps that
66
+ # ambiguity entirely.
67
+ TOOL_INPUT=$(printf '%s' "$INPUT_JSON" \
68
+ | sed -E -n 's/.*"command"[[:space:]]*:[[:space:]]*"(([^"\\]|\\.)*)".*/\1/p' \
69
+ | head -1 \
70
+ | awk '
71
+ {
72
+ s = $0; out = ""; n = length(s)
73
+ for (i = 1; i <= n; i++) {
74
+ c = substr(s, i, 1)
75
+ if (c == "\\" && i < n) {
76
+ nc = substr(s, i + 1, 1)
77
+ if (nc == "\"") { out = out "\""; i++ }
78
+ else if (nc == "\\") { out = out "\\"; i++ }
79
+ else if (nc == "n") { out = out "\n"; i++ }
80
+ else if (nc == "t") { out = out "\t"; i++ }
81
+ else if (nc == "r") { out = out "\r"; i++ }
82
+ else if (nc == "/") { out = out "/"; i++ }
83
+ else { out = out c }
84
+ } else {
85
+ out = out c
86
+ }
87
+ }
88
+ print out
89
+ }
90
+ ')
43
91
  else
44
92
  TOOL_INPUT=$(echo "$INPUT_JSON" | grep -oE '"tool_input"\s*:\s*\{[^}]+\}' | head -1)
45
93
  fi
@@ -258,6 +306,43 @@ seg_match() {
258
306
  [[ -n "$SEGMENTS" ]] && grep -qE "$1" <<< "$SEGMENTS"
259
307
  }
260
308
 
309
+ # resolve_cd_target <tool_input> — print the target directory of the LAST
310
+ # `cd <path>` line in a (possibly multi-line) Bash command, if and only if
311
+ # the path is a static literal (quoted or unquoted) that resolves to an
312
+ # existing directory. Scans the raw command, not $SEGMENTS — emit_segments
313
+ # drops double-quoted regions, so `cd "$WT"` would vanish there before this
314
+ # ever saw it. Prints nothing when there is no `cd` line, the target is
315
+ # dynamic (contains `$` or a backtick), or the path doesn't exist — callers
316
+ # must treat empty output as "fail open", never as license to guess a
317
+ # directory (#963).
318
+ resolve_cd_target() {
319
+ local input="$1" line target
320
+ line=$(printf '%s\n' "$input" | grep -E '^[[:space:]]*cd[[:space:]]+' | tail -1)
321
+ [[ -z "$line" ]] && return 0
322
+
323
+ target=$(printf '%s' "$line" | sed -E 's/^[[:space:]]*cd[[:space:]]+//; s/[[:space:]]*[;&|].*$//; s/[[:space:]]+$//')
324
+
325
+ # Strip one layer of surrounding matching quotes.
326
+ case "$target" in
327
+ \"*\") target="${target#\"}"; target="${target%\"}" ;;
328
+ \'*\') target="${target#\'}"; target="${target%\'}" ;;
329
+ esac
330
+
331
+ # Fail open on anything dynamic — resolving shell expansions means
332
+ # reimplementing the shell, which is disproportionate; `git commit`
333
+ # itself already rejects a genuinely empty commit. A backslash is
334
+ # rejected too: it can escape a following `$`/`` ` `` into a form this
335
+ # literal-string check would otherwise miss, and a backslash also carries
336
+ # its own shell meaning (line continuation, escaped chars) that this
337
+ # function does not attempt to resolve — failing open is the safe
338
+ # direction either way (#963).
339
+ case "$target" in
340
+ *'$'*|*'`'*|*'\'*) return 0 ;;
341
+ esac
342
+
343
+ [[ -n "$target" && -d "$target" ]] && printf '%s' "$target"
344
+ }
345
+
261
346
  # Path of the session->issue binding the checkout guard maintains (#906).
262
347
  # $1 = repo toplevel, $2 = session id. The id is opaque, so squash everything
263
348
  # outside a filename-safe set — it must not be able to escape the directory.
@@ -552,11 +637,10 @@ if [[ -z "${SEQUANT_ORCHESTRATOR:-}" ]] \
552
637
  # `.cwd` is part of Claude Code's PreToolUse envelope (verified against a
553
638
  # live payload alongside `session_id`), with $PWD as the fallback.
554
639
  _CO_CWD="${HOOK_CWD:-$PWD}"
555
- # Honor a leading `cd <dir>` the same way the commit guard below does.
556
- if echo "$TOOL_INPUT" | grep -qE '^cd [^;&|]+'; then
557
- _CO_CD=$(echo "$TOOL_INPUT" | grep -oE '^cd [^;&|]+' | head -1 | sed 's/^cd //' | sed 's/[[:space:]]*$//')
558
- [[ -n "$_CO_CD" && -d "$_CO_CD" ]] && _CO_CWD="$_CO_CD"
559
- fi
640
+ # Honor a `cd <dir>` the same way the commit guard below does — including
641
+ # multi-line commands and quoted/dynamic targets (#963).
642
+ _CO_CD=$(resolve_cd_target "$TOOL_INPUT")
643
+ [[ -n "$_CO_CD" ]] && _CO_CWD="$_CO_CD"
560
644
 
561
645
  # A linked worktree's toplevel has `.git` as a FILE; the main checkout has
562
646
  # it as a directory.
@@ -782,18 +866,25 @@ fi
782
866
  # Skips for --amend since amending doesn't require new changes
783
867
  if [[ "$TOOL_NAME" == "Bash" ]] && seg_match 'git commit'; then
784
868
  if ! echo "$TOOL_INPUT" | grep -qE -- '--amend|--allow-empty'; then
785
- # Extract target directory from cd command if present (for worktree commits)
786
- # Handles: "cd /path && git commit" or "cd /path; git commit"
787
- TARGET_DIR=""
788
- if echo "$TOOL_INPUT" | grep -qE '^cd [^;&|]+'; then
789
- TARGET_DIR=$(echo "$TOOL_INPUT" | grep -oE '^cd [^;&|]+' | head -1 | sed 's/^cd //' | tr -d ' ')
790
- fi
791
-
792
- # Check for changes in the target directory (or current if no cd)
793
- if [[ -n "$TARGET_DIR" && -d "$TARGET_DIR" ]]; then
794
- CHANGES=$(cd "$TARGET_DIR" && git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
869
+ # Resolve where to check for changes: the last resolvable `cd`
870
+ # target if the command has one (multi-line commands included
871
+ # #963), else the command's own cwd from the hook payload (never
872
+ # this hook process's own cwd, which need not match).
873
+ TARGET_DIR=$(resolve_cd_target "$TOOL_INPUT")
874
+ HAS_CD_LINE=false
875
+ echo "$TOOL_INPUT" | grep -qE '^[[:space:]]*cd[[:space:]]+' && HAS_CD_LINE=true
876
+
877
+ if [[ -n "$TARGET_DIR" ]]; then
878
+ CHANGES=$(git -C "$TARGET_DIR" status --porcelain 2>/dev/null | wc -l | tr -d ' ')
879
+ elif [[ "$HAS_CD_LINE" == true ]]; then
880
+ # A `cd` line is present but its target is dynamic (a shell
881
+ # variable/command substitution) or doesn't exist as a
882
+ # directory — fail open rather than check the wrong
883
+ # directory. `git commit` itself already rejects a genuinely
884
+ # empty commit, so this costs one harmless git error (#963).
885
+ CHANGES=1
795
886
  else
796
- CHANGES=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
887
+ CHANGES=$(git -C "${HOOK_CWD:-$PWD}" status --porcelain 2>/dev/null | wc -l | tr -d ' ')
797
888
  fi
798
889
 
799
890
  if [[ "$CHANGES" -eq 0 ]]; then
@@ -1,64 +1,131 @@
1
- # {{PROJECT_NAME}} Constitution
1
+ # {{PROJECT_NAME}} Agent Contract
2
2
 
3
- This document defines the core principles and patterns for AI-assisted development in this project.
3
+ This document is the enforceable contract surface for AI-assisted development in this project. Every section either machine-checked or referenced by a named skill at a named decision point. Values with no consumer are not present.
4
4
 
5
- ## Core Principles
5
+ ---
6
6
 
7
- 1. **Quality First** - Never sacrifice code quality for speed
8
- 2. **Test Everything** - All features must have appropriate test coverage
9
- 3. **Document Decisions** - Important decisions should be captured in issue comments
10
- 4. **Incremental Progress** - Break large tasks into small, reviewable chunks
11
- 5. **Respect Existing Patterns** - Follow established project conventions
7
+ ## 1. Definition of Done
12
8
 
13
- ## Workflow Phases
9
+ Every PR must pass all gates below before merge. Verified by `/qa` §7 — the table is generated from that section and cannot drift from it (`lint:constitution-dod` fails CI on divergence).
14
10
 
15
- ### Phase 1: Planning (`/spec`)
16
- - Understand requirements and acceptance criteria
17
- - Identify potential risks and dependencies
18
- - Draft implementation plan for approval
11
+ **These are project-wide gates. Do not restate them as issue-level ACs.**
19
12
 
20
- ### Phase 2: Implementation (`/exec`)
21
- - Work in feature worktree
22
- - Follow established patterns
23
- - Keep commits atomic and well-described
13
+ <!-- BEGIN:DOD-GATES -->
14
+ | Gate | Trigger | Verdict impact |
15
+ |------|---------|----------------|
16
+ | All ACs MET | any `NOT_MET` or `PARTIALLY_MET` | `AC_NOT_MET` — blocks merge |
17
+ | Detection patterns (§6c) | `Failed` | `AC_NOT_MET` — blocks merge |
18
+ | Behavior-rule check (§6e) | `Survivors Found` | `AC_NOT_MET` — blocks merge |
19
+ | Trust boundary (§6f) | `Injection Acted On` | `AC_NOT_MET` — blocks merge |
20
+ | CLI registration (§2h) | `Failed` | `AC_NOT_MET` — blocks merge |
21
+ | Mutation verification (§6i) | `Failed` | `AC_NOT_MET` — blocks merge |
22
+ | Adversarial re-read (§6d) | `Severe Gap` | `AC_NOT_MET` — blocks merge |
23
+ | Skill verification (§6a) | `Failed` | `AC_MET_BUT_NOT_A_PLUS` — cannot be A+ |
24
+ | Script execution evidence | `Incomplete` | `AC_MET_BUT_NOT_A_PLUS` — cannot be A+ |
25
+ | Declared evidence (§6h) | `Incomplete` | `AC_MET_BUT_NOT_A_PLUS` — cannot be A+ |
26
+ | Mutation verification (§6i) | `Missing` | `AC_MET_BUT_NOT_A_PLUS` — cannot be A+ |
27
+ | Script verification (§11) | `Not Verified` | `AC_MET_BUT_NOT_A_PLUS` — cannot be A+ |
28
+ | CHANGELOG entry (§10a) | both conditions true | `AC_MET_BUT_NOT_A_PLUS` — cannot be A+ |
29
+ | Quality plan (Phase 0b) | both conditions true | `AC_MET_BUT_NOT_A_PLUS` — cannot be A+ |
30
+ | Browser test | condition true | `AC_MET_BUT_NOT_A_PLUS` — cannot be A+ |
31
+ | Pending verifications | count `> 0` | `NEEDS_VERIFICATION` — holds for external verification |
32
+ | Quality plan (Phase 0b) | `Partial` | `AC_MET_BUT_NOT_A_PLUS` — cannot be A+ |
33
+ | Smoke tests (§6b) | `Partial` | `AC_MET_BUT_NOT_A_PLUS` — cannot be A+ |
34
+ | Detection patterns (§6c) | `Insufficient Samples` | `AC_MET_BUT_NOT_A_PLUS` — cannot be A+ |
35
+ | Detection patterns (§6c) | `Skipped` | `AC_MET_BUT_NOT_A_PLUS` — cannot be A+ |
36
+ | Adversarial re-read (§6d) | `Gaps Found` | `AC_MET_BUT_NOT_A_PLUS` — cannot be A+ |
37
+ | Improvement suggestions | list non-empty | `AC_MET_BUT_NOT_A_PLUS` — cannot be A+ |
38
+ <!-- END:DOD-GATES -->
24
39
 
25
- ### Phase 3: Quality (`/qa`)
26
- - Review against acceptance criteria
27
- - Run all quality checks
28
- - Address feedback before merge
40
+ ---
29
41
 
30
- ## Code Standards
42
+ ## 2. AC Authoring Standard
31
43
 
32
- ### Naming Conventions
33
- - Use descriptive variable and function names
34
- - Follow language-specific conventions (camelCase, snake_case, etc.)
44
+ Referenced by `/spec`'s AC Quality Check step when flagging lint warnings.
35
45
 
36
- ### Error Handling
37
- - Handle errors gracefully
38
- - Log meaningful error messages
39
- - Don't swallow exceptions silently
46
+ ### Format rules
40
47
 
41
- ### Testing
42
- - Write tests for new features
43
- - Update tests when modifying existing code
44
- - Test edge cases and error paths
48
+ **Write each AC on a single line.** The parser is line-anchored; an AC that wraps to a second line is silently truncated at the first newline, producing a partial description with no `Evidence:` or `Risk:` clause. This is the most common AC-hygiene defect in this repo.
45
49
 
46
- ## Commands Available
50
+ **Required fields for testable ACs:**
47
51
 
48
- | Command | Purpose |
49
- |---------|---------|
50
- | `/spec` | Plan implementation for an issue |
51
- | `/exec` | Implement a planned feature |
52
- | `/test` | Run browser-based UI tests |
53
- | `/qa` | Quality review before merge |
54
- | `/loop` | Fix iteration when tests fail |
55
- | `/docs` | Generate feature documentation |
52
+ | Field | Purpose | Required when |
53
+ |-------|---------|---------------|
54
+ | `Evidence:` | Names the artifact that proves the AC was met | AC is verifiable by code review or test |
55
+ | `Risk:` | Names the failure mode if this AC is wrong | AC has a non-obvious failure mode |
56
+ | `Human decision` | Flags the AC as requiring human judgment | AC cannot be verified mechanically |
56
57
 
57
- ## Stack-Specific Notes
58
+ **Non-Goals section:** Every issue must have a `## Non-Goals` section. Scope without a boundary is unbounded scope. The Non-Goals section is where you declare what the issue explicitly does not do.
59
+
60
+ ### Examples
61
+
62
+ **Bad** (three violations on one AC):
63
+ ```
64
+ - [ ] **AC-3:** The feature should work correctly and handle errors
65
+ gracefully with good test coverage.
66
+ ```
67
+ Problems: wraps to second line (parser truncates), "work correctly" is not measurable, no `Evidence:` clause.
68
+
69
+ **Good** (single line, measurable, evidence declared):
70
+ ```
71
+ - [ ] **AC-3:** `/spec`'s AC Quality Check output references the constitution AC standard. Evidence: scoped gate test on the spec skill's reference string, mutation-verified.
72
+ ```
73
+
74
+ ---
75
+
76
+ ## 3. Boundaries
77
+
78
+ Every rule below names its enforcing mechanism. Rules without a named enforcer are not in this section.
79
+
80
+ | Rule | Enforcing mechanism |
81
+ |------|---------------------|
82
+ | No force-push or amend on pushed branches | `templates/hooks/pre-tool.sh` (pre-tool hook, `HOOK_BLOCKED: Force push`) |
83
+ | No edits outside the issue worktree | `templates/hooks/pre-tool.sh` (worktree-only editing guard) |
84
+ | Gate tests must be mutation-verified | `/qa` §6i + `SEQUANT_MUTATION` marker in PR body (#939); `Missing` caps at `AC_MET_BUT_NOT_A_PLUS`, `Failed` floors at `AC_NOT_MET` |
85
+ | All §1 Definition of Done gates | `/qa` §7 verdict algorithm (see §1 above) |
86
+
87
+ ---
88
+
89
+ ## 4. Budgets & Stop Conditions
90
+
91
+ ### Iteration and token caps
92
+
93
+ | Cap | Setting key | Default |
94
+ |-----|-------------|---------|
95
+ | Quality-loop max iterations | `run.maxIterations` | 3 |
96
+ | Auto-wait budget (rate-limit windows) | `run.autoWaitMinutes` | 0 (off) |
97
+
98
+ When a cap is reached, the run stops at the human merge gate rather than continuing indefinitely.
99
+
100
+ ### Stop and hold states
101
+
102
+ The workflow stops or holds at these states — do not attempt to continue past them:
103
+
104
+ | State | Meaning | Action |
105
+ |-------|---------|--------|
106
+ | `waiting_for_human_merge` | All gates passed; PR is open | Human reviews and merges |
107
+ | `awaiting_verification` | At least one AC is `PENDING` external verification | Wait for the external signal; see [`docs/features/qa-verdict-workflow-states.md`](../../docs/features/qa-verdict-workflow-states.md) |
108
+ | `blocked` | A guard halted the run | Investigate the block; do not bypass |
109
+
110
+ ### Gap-prompt discipline
111
+
112
+ **Diagnostic prompts are cheap and high-yield. Imperative prompts require triage first.**
113
+
114
+ - **Diagnostic** ("what are the gaps?"): run freely. Surfaces unknowns at low cost.
115
+ - **Imperative** ("fix all gaps"): requires triage first. An unfocused imperative prompt against a list of gaps produces shallow patches for every item rather than deep fixes for the real ones. The 2026-08 incident (#930) demonstrated this directly: an imperative "fix all gaps" run against a QA verdict produced 12 surface-level changes that passed re-QA but left the root-cause gap intact, requiring a third QA cycle.
116
+
117
+ When QA returns gaps: run a diagnostic first ("which gap is the most blocking?"), then issue a targeted imperative for that gap specifically.
118
+
119
+ ---
120
+
121
+ ## 5. Stack-Specific Notes
58
122
 
59
123
  {{STACK_NOTES}}
60
124
 
61
- ## Project-Specific Notes
125
+ ---
62
126
 
63
- <!-- Add your project-specific guidelines below -->
127
+ ## Project-Specific Notes
64
128
 
129
+ <!-- Add your project-specific guidelines below this line.
130
+ This section is preserved across `sequant update` and `sync` runs
131
+ (it is in CUSTOMIZABLE_FILES). Everything above is updated automatically. -->
@@ -1955,7 +1955,7 @@ The goal is to satisfy AC with the smallest, safest change possible.
1955
1955
  ### 6. Progress Summary and Draft Issue Update
1956
1956
 
1957
1957
  **If orchestrated (SEQUANT_ORCHESTRATOR is set):**
1958
- - Skip posting progress comments to GitHub (orchestrator handles summary)
1958
+ - Skip posting progress comments to GitHub no per-phase comment is posted under `sequant run`; progress surfaces through the run summary and the PR body (#964)
1959
1959
  - Still provide AC coverage summary in output for orchestrator to capture
1960
1960
  - Let orchestrator handle final GitHub update
1961
1961
 
@@ -23,7 +23,6 @@ allowed-tools:
23
23
  - Bash(gh issue edit:*)
24
24
  - Bash(gh pr create:*)
25
25
  - Bash(gh pr list:*)
26
- - Bash(gh pr merge:*)
27
26
  - Bash(npm test:*)
28
27
  - Bash(npm run build:*)
29
28
  - Bash(git diff:*)
@@ -52,22 +51,28 @@ When invoked as `/fullsolve <issue-number>`, execute the complete issue resoluti
52
51
 
53
52
  ## CRITICAL: Auto-Progression Between Phases
54
53
 
55
- **DO NOT wait for user confirmation between phases.** This is an autonomous workflow.
54
+ **DO NOT wait for user confirmation between phases.** This is an autonomous workflow — through PR creation.
56
55
 
57
56
  After each phase completes successfully, **immediately proceed** to the next phase:
58
57
  1. `/spec` completes → **immediately** invoke `/exec`
59
58
  2. `/exec` completes → **immediately** invoke `/test` (if UI) or `/qa`
60
59
  3. `/test` completes → **immediately** invoke `/qa`
61
- 4. `/qa` completes → **immediately** create PR
60
+ 4. `/qa` completes → **immediately** create the PR and post the final summary
62
61
 
63
- **The user invoked `/fullsolve` expecting end-to-end automation.** Only stop for:
62
+ <!-- BEGIN: merge-gate (#958) -->
63
+ **The workflow's terminal state is PR created + final summary posted — not merged.** Merging (§5.3), post-merge verification (§5.4), and the auto-merge-path lock release (§5.5) run **only** when `--auto-merge` is passed, `run.autoMerge` is `true` in `.sequant/settings.json`, or the user has explicitly instructed a merge in this conversation (invoking `/fullsolve` alone does not count). Without one of those, `/fullsolve` stops after the final summary — the PR is left open for human review. See "Merge Gate" below for how this is resolved, and §5.3 for the gate itself.
64
+
65
+ **The user invoked `/fullsolve` expecting end-to-end automation up to a mergeable PR.** Only stop for:
64
66
  - Unrecoverable errors (after retry attempts exhausted)
65
- - Final summary after PR creation
67
+ - Final summary after PR creation — **this is the workflow's terminal state**, not a pause
66
68
  - Explicit user interruption
69
+ <!-- END: merge-gate (#958) -->
67
70
 
68
71
  ```
69
72
  WRONG: "Spec complete. Ready for exec phase." [waits]
70
73
  RIGHT: "Spec complete. Proceeding to exec..." [invokes /exec immediately]
74
+ WRONG (no --auto-merge): [creates PR, immediately runs `gh pr merge`]
75
+ RIGHT (no --auto-merge): [creates PR, posts final summary, stops]
71
76
  ```
72
77
 
73
78
  ## Workflow Overview
@@ -125,6 +130,7 @@ RIGHT: "Spec complete. Proceeding to exec..." [invokes /exec immediately]
125
130
  /fullsolve 218 --max-iterations 5 # Override max fix iterations
126
131
  /fullsolve 218 --parallel # Force parallel agent execution (faster, higher token usage)
127
132
  /fullsolve 218 --sequential # Force sequential agent execution (slower, lower token usage)
133
+ /fullsolve 218 --auto-merge # Merge the PR automatically once QA passes (default: off)
128
134
  ```
129
135
 
130
136
  ## Agent Execution Mode
@@ -151,6 +157,30 @@ When spawning sub-agents for quality checks, determine the execution mode:
151
157
 
152
158
  **Pass execution mode to child skills:** When invoking `/qa` or other skills that spawn agents, pass the `--parallel` or `--sequential` flag to maintain consistency.
153
159
 
160
+ ## Merge Gate (#958)
161
+
162
+ Determine whether Phase 5.3–5.5's merge workflow runs at all. This mirrors the Agent Execution Mode resolution above — flag first, then settings, defaulting closed.
163
+
164
+ 1. **Check for CLI flag override:**
165
+ - `--auto-merge` → run the merge workflow (§5.3) after the final summary
166
+ - No flag → do not merge automatically; fall through to step 2
167
+
168
+ 2. **If no flag, read project settings:**
169
+ Use the Read tool to check project settings:
170
+ ```
171
+ Read(file_path=".sequant/settings.json")
172
+ # Parse JSON and extract run.autoMerge (default: false)
173
+ ```
174
+
175
+ 3. **Default:** off. `/fullsolve` ends at PR creation + final summary — this
176
+ preserves the human merge gate recorded in #817–#819 (`sequant ready`
177
+ drives an issue to merge-*readiness*; a human runs `sequant merge` to
178
+ actually merge it).
179
+
180
+ **Explicit user instruction overrides the gate independent of the flag or setting.** If the user has told you in this conversation to merge once ready (not merely "run `/fullsolve`"), treat that as satisfying the gate for this run.
181
+
182
+ **If the gate does not fire:** skip §5.3 and §5.4 entirely. Release the concurrency locks immediately after §5.2 (see §5.5) and stop — do not attempt `gh pr merge` under any circumstance without one of the three conditions above.
183
+
154
184
  ## Orchestration Context
155
185
 
156
186
  This skill acts as an **orchestrator** and sets environment variables for child skills to optimize their behavior:
@@ -306,7 +336,7 @@ npx sequant locks checkout acquire \
306
336
 
307
337
  Release it alongside the per-issue lock: `npx sequant locks checkout release --issue=<issue-number> || true`. **`--issue` is mandatory** (#906) — it is what proves you are the holder. `--skip-pid-check` means the acquiring shell's PID is already dead, so PID identity is unavailable and a release without `--issue` is refused, not merely ineffective. Stale recovery is therefore age-based only for this lock: the 6h `SEQUANT_SKILL_LOCK_TTL_MS` and the 24h `SEQUANT_MAX_LOCK_AGE_MS` ceiling, *not* same-host dead-PID recovery, which `--skip-pid-check` disables by definition. An abandoned holder still cannot wedge the checkout permanently.
308
338
 
309
- **Release contract:** Phase 5.5 releases both locks on the happy path. On ANY branch that **exits the workflow without reaching Phase 5** — spec failure, exec iterations exhausted, unrecoverable error — you MUST run `npx sequant locks release <issue-number> || true` and `npx sequant locks checkout release --issue=<issue-number> || true` **before** printing the halt message. The explicit release calls below cover the known branches; if you add a new early-exit path, add a release call there too.
339
+ **Release contract (#958):** the happy path releases both locks **right after §5.2's final summary** — that is the workflow's default terminal state, since §5.3–5.4 do not run without the Merge Gate firing. Only when the Merge Gate *does* fire does release move to §5.5, after merge (§5.3) and post-merge verification (§5.4) complete. On ANY branch that **exits the workflow without reaching Phase 5** — spec failure, exec iterations exhausted, unrecoverable error — you MUST run `npx sequant locks release <issue-number> || true` and `npx sequant locks checkout release --issue=<issue-number> || true` **before** printing the halt message. The explicit release calls below cover the known branches; if you add a new early-exit path, add a release call there too.
310
340
 
311
341
  **Do NOT release at a branch that continues to Phase 5.** QA-loop exhaustion and the stagnation halt both fall through to PR creation, which still runs git in this tree — releasing there would leave Phase 5 unprotected.
312
342
 
@@ -763,9 +793,17 @@ Post completion comment to issue with:
763
793
  - PR link
764
794
  - Quality metrics
765
795
 
766
- ### 5.3 Merge Workflow (Correct Order)
796
+ ### 5.3 Merge Workflow (Opt-In Only) (#958)
767
797
 
768
- **IMPORTANT:** Merge the PR first, then clean up the worktree.
798
+ **STOP do not run this section unless the Merge Gate above fired.** That
799
+ means one of: `--auto-merge` was passed on this `/fullsolve` invocation,
800
+ `run.autoMerge` is `true` in `.sequant/settings.json`, or the user
801
+ explicitly instructed a merge in this conversation. If none of those hold,
802
+ **do not run `gh pr merge`.** Stop after §5.2's final summary instead — the
803
+ PR stays open, awaiting human review. That is the default terminal state,
804
+ not a fallback.
805
+
806
+ **IMPORTANT (once the gate above has fired):** Merge the PR first, then clean up the worktree.
769
807
 
770
808
  ```bash
771
809
  # 1. Merge PR (without --delete-branch; cleanup happens after success)
@@ -783,6 +821,8 @@ gh pr merge <N> --squash
783
821
 
784
822
  ### 5.4 Post-Merge Verification
785
823
 
824
+ **Skip this section if §5.3 did not run.** Nothing to verify post-merge when there was no merge.
825
+
786
826
  **Recommended:** After merge, verify the build and CLI still work:
787
827
 
788
828
  ```bash
@@ -800,7 +840,14 @@ If any command fails, fix immediately on main before continuing. This catches is
800
840
 
801
841
  ### 5.5 Release Concurrency Locks (#625, #901)
802
842
 
803
- After the PR is created (or earlier if the workflow exits gracefully), release both locks so other sessions can claim them:
843
+ **Default path (Merge Gate did not fire):** release runs immediately after
844
+ §5.2's final summary — that is the happy path, since §5.3–5.4 never execute.
845
+ Run the release calls below there, not here.
846
+
847
+ **Auto-merge path (Merge Gate fired):** release runs here, after §5.3
848
+ (merge) and §5.4 (post-merge verification) complete.
849
+
850
+ Either way, release both locks so other sessions can claim them:
804
851
 
805
852
  ```bash
806
853
  npx sequant locks release <issue-number> || true
@@ -949,6 +996,7 @@ Do **not** run it on the two branches below that continue to Phase 5 — "test l
949
996
  | MAX_QA_ITERATIONS | 2 | Max fix loops for QA phase |
950
997
  | SKIP_TEST | false | Skip testing phase |
951
998
  | AUTO_PR | true | Create PR automatically |
999
+ | AUTO_MERGE | false | Merge the PR automatically once QA passes (#958) |
952
1000
 
953
1001
  ## Smart Tests Integration
954
1002
 
@@ -993,6 +1041,11 @@ npx tsx scripts/dev/analyze-hook-logs.ts --tests
993
1041
  /fullsolve 218 --max-iterations 5
994
1042
  ```
995
1043
 
1044
+ **End-to-end including merge:**
1045
+ ```
1046
+ /fullsolve 218 --auto-merge
1047
+ ```
1048
+
996
1049
  ## Batch Processing
997
1050
 
998
1051
  For multiple issues, run `/fullsolve` on each sequentially:
@@ -59,7 +59,7 @@ When running as part of an orchestrated workflow (e.g., `sequant run` or `/fulls
59
59
  1. **Use provided worktree** - Work in `SEQUANT_WORKTREE` path directly
60
60
  2. **Use `SEQUANT_ISSUE`** - Skip issue number parsing from invocation
61
61
  3. **Reduce GitHub comment frequency** - Defer updates to orchestrator
62
- 4. **Trust issue context** - Orchestrator has already validated issue
62
+ 4. **Trust embedded context** - when the orchestrator injected a `<!-- SEQUANT_PROMPT_CONTEXT -->` block into this invocation, it is the authoritative QA-findings source; do not re-fetch from GitHub (see Step 1A below)
63
63
 
64
64
  **Behavior when standalone (SEQUANT_ORCHESTRATOR is NOT set):**
65
65
 
@@ -81,10 +81,20 @@ When running as part of an orchestrated workflow (e.g., `sequant run` or `/fulls
81
81
 
82
82
  #### Step 1A: Orchestrated Mode (SEQUANT_ORCHESTRATOR is set)
83
83
 
84
- When `SEQUANT_ORCHESTRATOR` is set, read QA findings from the GitHub issue comments instead of a log file:
84
+ <!-- BEGIN: step-1a-context-source (#960) -->
85
+
86
+ **Check your own invocation first — before fetching anything.** The orchestrator (`ready-gate.ts` or `batch-executor.ts`, via `buildLoopContext`) may already have embedded the QA findings directly into the prompt that invoked this `/loop` run, wrapped in a `<!-- SEQUANT_PROMPT_CONTEXT -->` / `<!-- /SEQUANT_PROMPT_CONTEXT -->` sentinel pair (`getPhasePrompt`, `src/lib/workflow/phase-executor.ts`). When that sentinel is present in your own invocation text:
87
+
88
+ - Treat the text between the markers as the authoritative QA findings (`qa_comment` below).
89
+ - **Do not** fetch `gh issue view` for QA comments — skip straight to "Parsing QA comment" below, using the embedded text in place of the fetched comment.
90
+ - **The embedded block's gap list IS your `recommendations`.** The block is not QA-comment-shaped — the parsing snippets below (verdict grep aside) won't extract anything from it. Read the list under `Gaps to address:` (ready-gate) or `QA Gaps:` (batch-executor) directly as the findings to fix; the `QA Verdict:` line is the verdict.
91
+ - That gap list is already `fixableGaps`-filtered by the orchestrator (`selectFixableGaps` in `batch-executor.ts`, or ready-gate's own filter), so the `document`/`pause_for_human` exclusion in "Excluded Finding Classes" below is redundant for it — it exists for the fetched-comment fallback. But this applies **only to the gap list**: any `Suggestions:` or `Last output:` sections in the block (batch-executor only) are raw, unfiltered context — use them for understanding, never as additional findings to fix.
92
+
93
+ **Only when the sentinel is absent** from your invocation — a standalone-style dispatch, or an orchestrator that doesn't inject `promptContext` — fall back to reading QA findings from the GitHub issue comments instead of a log file:
85
94
 
86
95
  ```bash
87
- # Check if we're in orchestrated mode
96
+ # Fallback: no embedded SEQUANT_PROMPT_CONTEXT sentinel in the invocation.
97
+ # Fetch QA findings from GitHub comments instead.
88
98
  if [[ -n "$SEQUANT_ORCHESTRATOR" ]]; then
89
99
  echo "Orchestrated mode detected (orchestrator: $SEQUANT_ORCHESTRATOR)"
90
100
 
@@ -96,6 +106,8 @@ if [[ -n "$SEQUANT_ORCHESTRATOR" ]]; then
96
106
  fi
97
107
  ```
98
108
 
109
+ <!-- END: step-1a-context-source (#960) -->
110
+
99
111
  **How to identify QA comments:**
100
112
 
101
113
  | Pattern | Meaning |
@@ -103,7 +115,7 @@ fi
103
115
  | `## QA Review for Issue #N` | QA phase comment header |
104
116
  | `### Verdict:` | Contains AC_NOT_MET, AC_MET_BUT_NOT_A_PLUS, etc. |
105
117
  | `### AC Coverage` | Table with MET/NOT_MET/PARTIALLY_MET statuses |
106
- | `### Required Fixes` or `### Recommendations` | Actionable items to fix |
118
+ | `<!-- SEQUANT_QA_GAPS: {...} -->` | Structured findings the primary source for `recommendations` (#937; see below) |
107
119
 
108
120
  **Parsing QA comment:**
109
121
 
@@ -116,14 +128,58 @@ verdict=$(echo "$qa_comment" | grep -oE "Verdict:\s*\w+" | head -1 | awk '{print
116
128
  # Extract NOT_MET AC items
117
129
  not_met_acs=$(echo "$qa_comment" | grep -E "NOT_MET|PARTIALLY_MET" || true)
118
130
 
119
- # Extract recommendations section
120
- recommendations=$(echo "$qa_comment" | sed -n '/### Required Fixes/,/###/p' | head -n -1)
131
+ # #937: prefer the structured SEQUANT_QA_GAPS marker over prose scraping.
132
+ # `### Required Fixes` was removed no QA output template ever emitted that
133
+ # heading, so the old `sed -n '/### Required Fixes/,/###/p'` extraction was
134
+ # dead code that always produced an empty `recommendations`. The marker's
135
+ # payload is an array of objects, so a single node -e does the stripping,
136
+ # matching, AND JSON.parse/filter in one script — instead of hand-rolled
137
+ # jq/awk — mirroring the "do real parsing in TS/JS, keep shell to presence
138
+ # checks" split from the #871 drift-guard lesson (the same taxonomy filter
139
+ # also lives in `selectFixableGaps`, `src/lib/workflow/phase-executor.ts` —
140
+ # this is the shell-side mirror for standalone/orchestrated-comment-scrape
141
+ # mode, not a duplicate contract).
142
+ #
143
+ # Stripping fenced/inline code before matching mirrors `stripMarkdownCode`
144
+ # (src/lib/workflow/phase-detection.ts) — a bare grep/sed would mistake a
145
+ # marker quoted inside a code fence (e.g. this skill's own "QA Log Example"
146
+ # below) for a real one. Latest-wins if more than one marker is present,
147
+ # matching `parseQaGapsMarker`'s semantics.
148
+ recommendations=$(printf '%s' "$qa_comment" | node -e '
149
+ let input = "";
150
+ process.stdin.on("data", (d) => (input += d));
151
+ process.stdin.on("end", () => {
152
+ const stripped = input
153
+ .replace(/`{3,}[\s\S]*?`{3,}|~{3,}[\s\S]*?~{3,}/g, "")
154
+ .replace(/`[^`\n]+`/g, "");
155
+ const matches = [...stripped.matchAll(/<!-- SEQUANT_QA_GAPS: (\{[\s\S]*?\}) -->/g)];
156
+ if (matches.length === 0) return;
157
+ try {
158
+ const payload = JSON.parse(matches[matches.length - 1][1]);
159
+ const findings = Array.isArray(payload.findings) ? payload.findings : [];
160
+ for (const f of findings) {
161
+ if (f.recommendedAction === "document" || f.recommendedAction === "pause_for_human") continue;
162
+ console.log(`- ${f.description}`);
163
+ }
164
+ } catch {
165
+ // Malformed marker JSON — leave recommendations empty, not a crash.
166
+ }
167
+ });
168
+ ' || true)
169
+
170
+ # No marker (QA output predates #937, or emitted an empty findings array
171
+ # with everything filtered) — fall back to the AC-table NOT_MET/PARTIALLY_MET
172
+ # rows already captured above; there is no prose "Required Fixes" section to
173
+ # scrape.
174
+ if [[ -z "$recommendations" ]]; then
175
+ recommendations="$not_met_acs"
176
+ fi
121
177
  ```
122
178
 
123
- **If no QA comment found in orchestrated mode:**
124
- 1. Log a clear error: `"Warning: No QA comment found in issue #N"`
179
+ **If neither an embedded `SEQUANT_PROMPT_CONTEXT` block nor a matching GitHub QA comment is found in orchestrated mode:**
180
+ 1. Log a clear error: `"Warning: No embedded context or QA comment found for issue #N"`
125
181
  2. Fall back to Step 1B (log file) as a recovery mechanism
126
- 3. If log file also doesn't exist, exit with error
182
+ 3. If log file also doesn't exist, exit with error — do not silently report "no actionable issues"; a silent miss here is the exact failure mode this section exists to prevent (#960)
127
183
 
128
184
  #### Step 1B: Standalone Mode (no SEQUANT_ORCHESTRATOR)
129
185
 
@@ -178,13 +234,14 @@ Some QA findings are real but **not fixable by a code change**. Feeding them to
178
234
  | Class | Marker in the QA comment | Why it is not actionable |
179
235
  |-------|--------------------------|--------------------------|
180
236
  | Infra-blocked CI | `<!-- qa:ci-infra-blocked -->` | Every check failed without a runner ever starting (e.g. an Actions spending-limit lockout). The cause is account/infrastructure state; no diff can turn the checks green. See `qa/SKILL.md` § "Infra-Blocked CI Detection". |
237
+ | `document`/`pause_for_human` findings | `<!-- SEQUANT_QA_GAPS: {...} -->`, per-finding `recommendedAction` | Quality/polish gaps deliberately deferred (`document`), or findings needing a human decision the loop can't make on its own (`pause_for_human`) — see #937. Filtered directly out of the `recommendations` extraction above (Step 1A), not by rebinding `qa_comment` — the marker's structure makes a per-finding filter more precise than a text-range delete. |
181
238
 
182
239
  ```bash
183
240
  # Drop the marked findings when QA flagged CI as infra-blocked, so its
184
241
  # NEEDS_VERIFICATION AC items are never mistaken for actionable findings.
185
242
  # Range is EXCLUSIVE of the next `### ` header — a `sed '/marker/,/^### /d'`
186
243
  # range would delete that header too and orphan the following section's
187
- # content (verified: it silently swallows `### Required Fixes`).
244
+ # content (verified against this file's own `### AC Coverage` header).
188
245
  qa_comment_raw="$qa_comment" # keep the original so the cause can be reported verbatim
189
246
 
190
247
  if echo "$qa_comment" | grep -q '<!-- qa:ci-infra-blocked -->'; then
@@ -513,13 +570,15 @@ For each iteration, output:
513
570
 
514
571
  ### Verdict: AC_NOT_MET
515
572
 
516
- ### Required Fixes
573
+ ### Next Steps
517
574
 
518
575
  1. Complete URL validation in ExternalUrlTab
519
576
  2. Add focal point persistence in updateArticleImage action
577
+
578
+ <!-- SEQUANT_QA_GAPS: {"findings":[{"category":"requirement_gap","evidence":"AC-3 row: PARTIALLY_MET — External URL validation incomplete","description":"Complete URL validation in ExternalUrlTab","recommendedAction":"fix_now","affectedAcs":["AC-3"]},{"category":"requirement_gap","evidence":"AC-4 row: NOT_MET — Focal point not persisted to database","description":"Add focal point persistence in updateArticleImage action","recommendedAction":"fix_now","affectedAcs":["AC-4"]}]} -->
520
579
  ```
521
580
 
522
- **Parsed Output:**
581
+ **Parsed Output** (marker-derived — this is the real emission shape, see §"How to build the marker" in `qa/SKILL.md`):
523
582
  - Last phase: `/qa`
524
583
  - Verdict: `AC_NOT_MET`
525
584
  - Issues to fix: