sequant 2.2.0 → 2.3.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 (137) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +73 -0
  4. package/dist/bin/cli.js +94 -9
  5. package/dist/src/commands/doctor.d.ts +25 -0
  6. package/dist/src/commands/doctor.js +36 -1
  7. package/dist/src/commands/locks.d.ts +67 -0
  8. package/dist/src/commands/locks.js +290 -0
  9. package/dist/src/commands/merge.js +11 -0
  10. package/dist/src/commands/prompt.d.ts +39 -0
  11. package/dist/src/commands/prompt.js +179 -0
  12. package/dist/src/commands/run-display.d.ts +11 -2
  13. package/dist/src/commands/run-display.js +62 -28
  14. package/dist/src/commands/run-progress.d.ts +32 -0
  15. package/dist/src/commands/run-progress.js +76 -0
  16. package/dist/src/commands/run.js +80 -18
  17. package/dist/src/commands/stats.d.ts +2 -0
  18. package/dist/src/commands/stats.js +94 -8
  19. package/dist/src/commands/status.js +12 -0
  20. package/dist/src/commands/watch.d.ts +16 -0
  21. package/dist/src/commands/watch.js +147 -0
  22. package/dist/src/lib/ac-linter.d.ts +1 -1
  23. package/dist/src/lib/ac-linter.js +81 -0
  24. package/dist/src/lib/assess-collision-detect.d.ts +91 -0
  25. package/dist/src/lib/assess-collision-detect.js +217 -0
  26. package/dist/src/lib/assess-comment-parser.d.ts +59 -1
  27. package/dist/src/lib/assess-comment-parser.js +124 -2
  28. package/dist/src/lib/cli-ui/format.d.ts +19 -0
  29. package/dist/src/lib/cli-ui/format.js +34 -0
  30. package/dist/src/lib/cli-ui/run-renderer-types.d.ts +181 -0
  31. package/dist/src/lib/cli-ui/run-renderer-types.js +7 -0
  32. package/dist/src/lib/cli-ui/run-renderer.d.ts +239 -0
  33. package/dist/src/lib/cli-ui/run-renderer.js +1173 -0
  34. package/dist/src/lib/heuristics/behavior-rule-detector.d.ts +94 -0
  35. package/dist/src/lib/heuristics/behavior-rule-detector.js +467 -0
  36. package/dist/src/lib/locks/index.d.ts +7 -0
  37. package/dist/src/lib/locks/index.js +5 -0
  38. package/dist/src/lib/locks/lock-manager.d.ts +168 -0
  39. package/dist/src/lib/locks/lock-manager.js +433 -0
  40. package/dist/src/lib/locks/types.d.ts +59 -0
  41. package/dist/src/lib/locks/types.js +31 -0
  42. package/dist/src/lib/qa/markdown-only-ci.d.ts +46 -0
  43. package/dist/src/lib/qa/markdown-only-ci.js +74 -0
  44. package/dist/src/lib/relay/activation.d.ts +60 -0
  45. package/dist/src/lib/relay/activation.js +122 -0
  46. package/dist/src/lib/relay/archive.d.ts +34 -0
  47. package/dist/src/lib/relay/archive.js +106 -0
  48. package/dist/src/lib/relay/frame.d.ts +20 -0
  49. package/dist/src/lib/relay/frame.js +76 -0
  50. package/dist/src/lib/relay/index.d.ts +13 -0
  51. package/dist/src/lib/relay/index.js +13 -0
  52. package/dist/src/lib/relay/paths.d.ts +43 -0
  53. package/dist/src/lib/relay/paths.js +59 -0
  54. package/dist/src/lib/relay/pid.d.ts +34 -0
  55. package/dist/src/lib/relay/pid.js +72 -0
  56. package/dist/src/lib/relay/reader.d.ts +35 -0
  57. package/dist/src/lib/relay/reader.js +115 -0
  58. package/dist/src/lib/relay/types.d.ts +68 -0
  59. package/dist/src/lib/relay/types.js +76 -0
  60. package/dist/src/lib/relay/writer.d.ts +48 -0
  61. package/dist/src/lib/relay/writer.js +113 -0
  62. package/dist/src/lib/settings.d.ts +31 -1
  63. package/dist/src/lib/settings.js +18 -3
  64. package/dist/src/lib/version-check.d.ts +60 -5
  65. package/dist/src/lib/version-check.js +97 -9
  66. package/dist/src/lib/workflow/batch-executor.d.ts +20 -1
  67. package/dist/src/lib/workflow/batch-executor.js +248 -175
  68. package/dist/src/lib/workflow/config-resolver.js +4 -0
  69. package/dist/src/lib/workflow/heartbeat.d.ts +71 -0
  70. package/dist/src/lib/workflow/heartbeat.js +194 -0
  71. package/dist/src/lib/workflow/phase-executor.d.ts +62 -8
  72. package/dist/src/lib/workflow/phase-executor.js +157 -16
  73. package/dist/src/lib/workflow/phase-mapper.d.ts +3 -2
  74. package/dist/src/lib/workflow/phase-mapper.js +17 -20
  75. package/dist/src/lib/workflow/platforms/github.d.ts +1 -1
  76. package/dist/src/lib/workflow/platforms/github.js +20 -3
  77. package/dist/src/lib/workflow/pr-status.d.ts +18 -2
  78. package/dist/src/lib/workflow/pr-status.js +41 -9
  79. package/dist/src/lib/workflow/qa-stagnation.d.ts +117 -0
  80. package/dist/src/lib/workflow/qa-stagnation.js +179 -0
  81. package/dist/src/lib/workflow/run-orchestrator.d.ts +39 -0
  82. package/dist/src/lib/workflow/run-orchestrator.js +340 -15
  83. package/dist/src/lib/workflow/run-reflect.js +1 -1
  84. package/dist/src/lib/workflow/run-state.d.ts +71 -0
  85. package/dist/src/lib/workflow/run-state.js +14 -0
  86. package/dist/src/lib/workflow/state-cleanup.d.ts +13 -5
  87. package/dist/src/lib/workflow/state-cleanup.js +17 -5
  88. package/dist/src/lib/workflow/state-manager.d.ts +12 -1
  89. package/dist/src/lib/workflow/state-manager.js +37 -0
  90. package/dist/src/lib/workflow/state-schema.d.ts +62 -0
  91. package/dist/src/lib/workflow/state-schema.js +35 -1
  92. package/dist/src/lib/workflow/types.d.ts +74 -1
  93. package/dist/src/lib/workflow/worktree-manager.d.ts +8 -1
  94. package/dist/src/lib/workflow/worktree-manager.js +15 -6
  95. package/dist/src/mcp/tools/run.d.ts +44 -0
  96. package/dist/src/mcp/tools/run.js +104 -13
  97. package/dist/src/ui/tui/App.d.ts +14 -0
  98. package/dist/src/ui/tui/App.js +41 -0
  99. package/dist/src/ui/tui/ElapsedTimer.d.ts +10 -0
  100. package/dist/src/ui/tui/ElapsedTimer.js +31 -0
  101. package/dist/src/ui/tui/Header.d.ts +6 -0
  102. package/dist/src/ui/tui/Header.js +15 -0
  103. package/dist/src/ui/tui/IssueBox.d.ts +16 -0
  104. package/dist/src/ui/tui/IssueBox.js +68 -0
  105. package/dist/src/ui/tui/Spinner.d.ts +9 -0
  106. package/dist/src/ui/tui/Spinner.js +18 -0
  107. package/dist/src/ui/tui/index.d.ts +15 -0
  108. package/dist/src/ui/tui/index.js +29 -0
  109. package/dist/src/ui/tui/theme.d.ts +29 -0
  110. package/dist/src/ui/tui/theme.js +52 -0
  111. package/dist/src/ui/tui/truncate.d.ts +11 -0
  112. package/dist/src/ui/tui/truncate.js +31 -0
  113. package/package.json +10 -3
  114. package/templates/agents/sequant-explorer.md +1 -0
  115. package/templates/agents/sequant-qa-checker.md +2 -1
  116. package/templates/agents/sequant-testgen.md +1 -0
  117. package/templates/hooks/post-tool.sh +11 -0
  118. package/templates/hooks/pre-tool.sh +18 -9
  119. package/templates/hooks/relay-check.sh +107 -0
  120. package/templates/relay/frame.txt +11 -0
  121. package/templates/scripts/cleanup-worktree.sh +25 -3
  122. package/templates/scripts/new-feature.sh +6 -0
  123. package/templates/skills/_shared/references/behavior-rule-detection.md +205 -0
  124. package/templates/skills/_shared/references/subagent-types.md +21 -8
  125. package/templates/skills/assess/SKILL.md +103 -49
  126. package/templates/skills/assess/references/predicted-collision-detection.md +109 -0
  127. package/templates/skills/docs/SKILL.md +141 -22
  128. package/templates/skills/exec/SKILL.md +10 -8
  129. package/templates/skills/fullsolve/SKILL.md +79 -5
  130. package/templates/skills/loop/SKILL.md +28 -0
  131. package/templates/skills/merger/SKILL.md +621 -0
  132. package/templates/skills/qa/SKILL.md +727 -8
  133. package/templates/skills/setup/SKILL.md +6 -0
  134. package/templates/skills/spec/SKILL.md +52 -0
  135. package/templates/skills/spec/references/parallel-groups.md +7 -0
  136. package/templates/skills/spec/references/recommended-workflow.md +4 -2
  137. package/templates/skills/testgen/SKILL.md +24 -17
@@ -15,6 +15,12 @@ NC='\033[0m'
15
15
 
16
16
  BRANCH_NAME=$1
17
17
 
18
+ # Resolve main worktree (first entry in porcelain output) so subsequent git
19
+ # commands run from a stable cwd even if the caller invoked us from inside the
20
+ # worktree we are about to delete. `sed` keeps the path intact when it contains
21
+ # whitespace; `awk '{print $2}'` would truncate at the first space.
22
+ MAIN_WORKTREE=$(git worktree list --porcelain | sed -n 's/^worktree //p' | head -n 1)
23
+
18
24
  # Check if branch name provided
19
25
  if [ -z "$BRANCH_NAME" ]; then
20
26
  echo -e "${RED}❌ Error: Branch name required${NC}"
@@ -25,8 +31,17 @@ if [ -z "$BRANCH_NAME" ]; then
25
31
  exit 1
26
32
  fi
27
33
 
28
- # Find worktree path
29
- WORKTREE_PATH=$(git worktree list | grep "$BRANCH_NAME" | awk '{print $1}')
34
+ # Find worktree path. Parse porcelain (which separates path/branch onto distinct
35
+ # lines) so paths containing whitespace survive intact, and so we match against
36
+ # the branch ref rather than against a free-form `grep` over the entire line —
37
+ # the latter false-matches whenever the branch name appears in the path string.
38
+ WORKTREE_PATH=$(git worktree list --porcelain | awk -v target="$BRANCH_NAME" '
39
+ /^worktree / { sub(/^worktree /, ""); path = $0 }
40
+ /^branch refs\/heads\// {
41
+ sub(/^branch refs\/heads\//, "")
42
+ if (index($0, target) > 0) { print path; exit }
43
+ }
44
+ ')
30
45
 
31
46
  if [ -z "$WORKTREE_PATH" ]; then
32
47
  echo -e "${RED}❌ Error: Worktree not found for branch: $BRANCH_NAME${NC}"
@@ -53,6 +68,13 @@ if [ "$PR_STATUS" != "MERGED" ]; then
53
68
  fi
54
69
  fi
55
70
 
71
+ # Move to the main worktree before any destructive operation. If the caller's
72
+ # cwd is inside $WORKTREE_PATH (or any of its .exec-agents/agent-* sub-worktrees
73
+ # below), the first `git worktree remove` would invalidate cwd and every
74
+ # subsequent git/gh call would silently fail with "Unable to read current
75
+ # working directory" — including the ones with `2>/dev/null || true`.
76
+ cd "$MAIN_WORKTREE"
77
+
56
78
  # Clean up any exec-agent sub-worktrees first (from parallel isolation)
57
79
  EXEC_AGENTS_DIR="$WORKTREE_PATH/.exec-agents"
58
80
  if [ -d "$EXEC_AGENTS_DIR" ]; then
@@ -71,7 +93,7 @@ if [ -d "$EXEC_AGENTS_DIR" ]; then
71
93
  rmdir "$EXEC_AGENTS_DIR" 2>/dev/null || true
72
94
  fi
73
95
 
74
- # Remove worktree
96
+ # Remove worktree (cwd already pinned to $MAIN_WORKTREE above)
75
97
  echo -e "${BLUE}📂 Removing worktree...${NC}"
76
98
  git worktree remove "$WORKTREE_PATH" --force
77
99
 
@@ -152,6 +152,12 @@ git pull origin "$BASE_BRANCH"
152
152
  echo -e "${BLUE}🌿 Creating new worktree from ${BASE_BRANCH}...${NC}"
153
153
  git worktree add "$WORKTREE_DIR" -b "$BRANCH_NAME"
154
154
 
155
+ # Record the base branch on the new branch so downstream tooling
156
+ # (e.g. phase-executor zero-diff guard, see #537) can resolve the
157
+ # correct comparison ref when the worktree was branched off something
158
+ # other than origin/main.
159
+ git -C "$WORKTREE_DIR" config "branch.${BRANCH_NAME}.sequantBase" "$BASE_BRANCH"
160
+
155
161
  # Navigate to worktree
156
162
  cd "$WORKTREE_DIR"
157
163
 
@@ -0,0 +1,205 @@
1
+ # Behavior-Rule Detection (Shared Heuristic for /spec + /qa)
2
+
3
+ > **Source of truth:** `src/lib/heuristics/behavior-rule-detector.ts`. The
4
+ > keyword set, threshold, and pattern list live in TypeScript so they can be
5
+ > unit-tested (per AC-5 of #552); this doc cites them.
6
+
7
+ ## Why this exists
8
+
9
+ Behavior-rule ACs ("default becomes X", "always include Y", "never skip Z") are
10
+ routinely implemented at **multiple touchpoints** — typically a skill prompt
11
+ (LLM-interpreted) **and** runtime TypeScript that duplicates the same rule.
12
+ Without a shared check, edits land at one site and the other goes stale.
13
+
14
+ **Motivating miss — issue #533** ("default /assess spec phase ON, remove
15
+ bug/docs auto-skip"):
16
+
17
+ - The AC explicitly named `.claude/skills/assess/SKILL.md`.
18
+ - `/spec` scoped the work to that file + CHANGELOG. `/exec` implemented it.
19
+ `/qa` returned `READY_FOR_MERGE`.
20
+ - Meanwhile the runtime CLI (`phase-mapper.ts` `detectPhasesFromLabels` +
21
+ `batch-executor.ts` auto-detect branch) still short-circuited bug/docs
22
+ issues to `exec → qa`, contradicting the new "spec by default" rule.
23
+ - Caught only by manual user follow-up; required two extra commits and four
24
+ doc updates on top of the original PR.
25
+
26
+ A pre-flight grep for `BUG_LABELS` / `DOCS_LABELS` / `"skip spec"` at /spec
27
+ time would have surfaced 90% of these in one pass. That's exactly what this
28
+ heuristic does.
29
+
30
+ ## Trigger keywords
31
+
32
+ A `BEHAVIOR_KEYWORDS` constant in `behavior-rule-detector.ts`. Each keyword
33
+ also matches common inflections via the `KEYWORD_REGEXES` map (mirrors the
34
+ stem-aware pattern landed by #597 in `ac-linter.ts`); word boundaries are
35
+ preserved so `defaultValue` (camelCase identifier) does NOT trigger.
36
+
37
+ | Keyword | Inflections matched | Why it's in the set |
38
+ |---------|---------------------|---------------------|
39
+ | `default` | `defaults`, `defaulted`, `defaulting` | "default becomes X", "defaults to Y" |
40
+ | `always` | (adverb, no inflections) | "always include", "always run" |
41
+ | `never` | (adverb, no inflections) | "never skip", "never run when X" |
42
+ | `rule` | `rules`, `ruled`, `ruling` | "the rule is", "this rule applies" |
43
+ | `behavior` | `behaviors`, `behavioral`, `behaviorally` | "the behavior is", "behaviors change" |
44
+ | `skip` | `skips`, `skipped`, `skipping` | "skip spec", "skipping when X" |
45
+
46
+ ## Trigger threshold (false-positive guard)
47
+
48
+ To avoid flagging localized fixes that happen to mention "default" once
49
+ ("set default value to 5"), the detector requires **either**:
50
+
51
+ 1. **>= 2 distinct keywords** from `BEHAVIOR_KEYWORDS` in the AC description
52
+ (case-insensitive, word-boundary), **OR**
53
+ 2. An **explicit pattern** match (single keyword is enough):
54
+ - Mid-sentence rule constructs:
55
+ - `always X unless Y`
56
+ - `never X unless Y`
57
+ - `default X when Y`
58
+ - Capitalized imperative AC openers (case-sensitive — matches the
59
+ imperative-rule register, not "the system always defaults to..." prose):
60
+ - `Always <verb> ...` — covers AC-5 literal "Always include Y"
61
+ - `Never <verb> ...` — covers AC-5 literal "Never skip Z"
62
+ - `Default <verb> ...` — covers AC-5 literal "Default rule becomes X"
63
+
64
+ Tunable in `EXPLICIT_PATTERNS` in `behavior-rule-detector.ts`.
65
+
66
+ ## Symbol categories surfaced by `findTouchpoints`
67
+
68
+ When the trigger fires, `findTouchpoints` extracts identifier-like substrings
69
+ from the AC and greps the codebase for them, plus any line that contains >= 2
70
+ distinct behavior keywords (catches comment-only sites). Symbol categories:
71
+
72
+ | Category | Example | Why include |
73
+ |----------|---------|-------------|
74
+ | Backticked symbols | `` `BUG_LABELS` ``, `` `phase-mapper.ts` `` | Verbatim from issue body — most specific |
75
+ | `SCREAMING_SNAKE_CASE` constants | `BUG_LABELS`, `DOCS_LABELS`, `SKIP_PHASES` | Dominant runtime-rule pattern |
76
+ | `camelCase` function names | `detectPhasesFromLabels`, `shouldSkipSpec` | Rule-implementing functions |
77
+ | File paths with extensions | `.claude/skills/assess/SKILL.md`, `phase-mapper.ts` | Direct touchpoint citations |
78
+ | Bold spans | `**always X unless Y**` | Author-emphasized rule statements |
79
+
80
+ ## Inverse search (`findSurvivingInverseSymbols`, used by `/qa`)
81
+
82
+ `/qa` runs the detector against the diff blast radius (changed files +
83
+ optional 1-hop importers) using **inverse** keywords derived from each
84
+ asserted keyword. Asymmetric on purpose — an AC asserting the NEW rule
85
+ "always include spec" should look for "skip" / "exclude" / "bypass"
86
+ survivors, not "always" itself.
87
+
88
+ | Asserted keyword | Inverse search terms |
89
+ |------------------|----------------------|
90
+ | `default` | `skip`, `exclude`, `bypass`, `override` |
91
+ | `always` | `skip`, `never`, `exclude`, `conditional`, `shortcut` |
92
+ | `never` | `always`, `default`, `exclude` |
93
+ | `rule` | `exception`, `override`, `shortcut`, `bypass` |
94
+ | `behavior` | `legacy`, `deprecated` |
95
+ | `skip` | `always`, `default`, `exclude` |
96
+
97
+ High-noise common English words (`include`, `run`, `auto`, `old`,
98
+ `previous`) were pruned from this map after QA's self-dogfood pass on #552
99
+ returned 50 survivors entirely from definitional prose. As defense in
100
+ depth, `deriveInverseTerms` also drops any term in the `COMMON_WORDS`
101
+ filter at runtime, so future tunings that re-add a common word are still
102
+ filtered out.
103
+
104
+ A survival inside the diff blast radius -> AC `NOT_MET` with `path:line`
105
+ listed in the QA output (per AC-2 of #552).
106
+
107
+ ## False-positive guards
108
+
109
+ - Test files (`*.test.ts`, `*.spec.ts`) are excluded from `findTouchpoints` —
110
+ they implement *checks* of behavior rules, not the rules themselves.
111
+ - Walk-skip dirs: `node_modules`, `.git`, `dist`, `build`, `.next`,
112
+ `coverage`, `__tests__`, `__snapshots__`.
113
+ - Common English words (`the`, `from`, `should`, `becomes`, ...) are filtered
114
+ from the symbol-extraction pass to avoid grepping for "the".
115
+ - Per-file cap (3) + total cap (200) on `findTouchpoints` results — keeps
116
+ `/spec` output readable when an AC's keywords are ambient in the repo.
117
+ - Total cap (50, `SURVIVOR_TOTAL_CAP`) on `findSurvivingInverseSymbols`
118
+ results — tighter than `findTouchpoints` because survivors are surfaced
119
+ inside the QA verdict and a long list drowns out the rule-relevant hits.
120
+
121
+ ## Performance budget
122
+
123
+ - `detectBehaviorRule(ac)` is a cheap regex pass — runs per AC.
124
+ - `findTouchpoints` and `findSurvivingInverseSymbols` short-circuit to `[]`
125
+ when `detectBehaviorRule` returns `triggered: false`.
126
+ - For `/qa`, the per-AC grep cost is bounded by the diff blast radius (not
127
+ the whole repo). For `/spec`, scope is `src/lib`, `src/commands`, `bin`,
128
+ and `.claude/skills` — `bin/` and `src/commands/` are included because CLI
129
+ option registration (Commander.js `.option()` chains, `RunOptions` interface)
130
+ is a recurring rule-drift site. `templates/skills/` and `skills/` are
131
+ intentionally excluded since they 1:1 mirror `.claude/skills/`.
132
+
133
+ When zero behavior-rule ACs are detected across the issue, both detectors
134
+ should be skipped entirely (no per-file grep pass).
135
+
136
+ ## Where to call from
137
+
138
+ - **`/spec`** — `### Rule Touchpoints (Conditional)` subsection under
139
+ `## Context Gathering`. Calls `findTouchpoints` per AC; emits a
140
+ `## Rule Touchpoints` section in the plan output when any hits found.
141
+ - **`/qa`** — `### 6e. Behavior-Rule Survival Check` between
142
+ `### 6d. Adversarial Re-Read` and `### 7. A+ Status Verdict`. Calls
143
+ `findSurvivingInverseSymbols` per behavior-rule AC; survivals -> AC
144
+ `NOT_MET`, gated through `behavior_rule_survival_status` in section 7.
145
+
146
+ ## API
147
+
148
+ ```typescript
149
+ import {
150
+ detectBehaviorRule,
151
+ findTouchpoints,
152
+ findSurvivingInverseSymbols,
153
+ } from "./src/lib/heuristics/behavior-rule-detector.ts";
154
+
155
+ // Cheap gate
156
+ const detection = detectBehaviorRule(ac);
157
+ // detection.triggered: boolean
158
+ // detection.keywords: BehaviorKeyword[]
159
+ // detection.matchedPattern?: string
160
+
161
+ // /spec: enumerate likely implementation sites
162
+ const hits = findTouchpoints(ac, repoRoot);
163
+ // hits: { path, line, snippet }[]
164
+
165
+ // /qa: search diff blast radius for OLD-rule survivors
166
+ const survivors = findSurvivingInverseSymbols(ac, repoRoot, diffPaths);
167
+ // survivors: { path, line, snippet }[]
168
+ ```
169
+
170
+ ## Known limitations
171
+
172
+ **Meta-recursion** — when an AC describes the detector itself (e.g. lists
173
+ all six trigger keywords as part of explaining the trigger condition),
174
+ `findSurvivingInverseSymbols` will self-fire on the new feature's own
175
+ definitional documentation. The pruned `INVERSE_KEYWORDS` map and
176
+ `SURVIVOR_TOTAL_CAP = 50` mitigate noise, but the heuristic cannot
177
+ distinguish "code that implements the OLD rule" from "documentation that
178
+ defines the rule". Treat survivors inside this reference doc and
179
+ `spec/SKILL.md` / `qa/SKILL.md` as definitional, not stale. See QA's
180
+ self-dogfood result on PR #607 for the original observation.
181
+
182
+ **Inverse-phrase fallback (deferred)** — `findSurvivingInverseSymbols`
183
+ relies on the asserted-→-inverse keyword map. When an AC's inverse rule
184
+ has no obvious symbol or keyword (e.g. "API responses always include the
185
+ user ID field"), the natural next layer is to derive an inverse English
186
+ phrase ("exclude user ID", "don't include user ID") and grep for that.
187
+ Not implemented — file a follow-up issue with the concrete fixture if a
188
+ real `/qa` miss surfaces.
189
+
190
+ ## Tuning
191
+
192
+ Edit `src/lib/heuristics/behavior-rule-detector.ts`:
193
+
194
+ - `BEHAVIOR_KEYWORDS` — keyword stem set (the source of truth)
195
+ - `KEYWORD_REGEXES` — per-stem inflection regex map; word-boundary
196
+ guarded so identifier-shaped tokens (`defaultValue`, `skipperFn`) do
197
+ not trigger
198
+ - `EXPLICIT_PATTERNS` — single-keyword override patterns (mid-sentence
199
+ rule constructs + capitalized imperative AC openers)
200
+ - `INVERSE_KEYWORDS` — asserted -> inverse mapping for `/qa` (common
201
+ English words filtered at runtime in `deriveInverseTerms`)
202
+ - `TOUCHPOINT_ROOTS` — directories scanned by `findTouchpoints`
203
+ - `SURVIVOR_TOTAL_CAP` — total survivor cap on `findSurvivingInverseSymbols`
204
+
205
+ Re-run `npx vitest run src/lib/heuristics` after tuning.
@@ -17,10 +17,15 @@ Claude Code supports exactly **4 built-in subagent types**:
17
17
 
18
18
  Sequant defines **4 custom agents** in `.claude/agents/`. These centralize model, permissions, effort, and tool restrictions that were previously duplicated inline.
19
19
 
20
- | Agent Name | Based On | Model | Permission Mode | Used By |
21
- |------------|----------|-------|-----------------|---------|
20
+ > **Upstream caveat:** `Model` values below are *declared* in the agent files but
21
+ > currently ignored at runtime per anthropics/claude-code#43869 — every subagent
22
+ > inherits the parent session's model. Tiers are kept aligned with intended
23
+ > defaults so they reactivate when upstream fixes ship.
24
+
25
+ | Agent Name | Based On | Model (declared) | Permission Mode | Used By |
26
+ |------------|----------|------------------|-----------------|---------|
22
27
  | `sequant-explorer` | Explore | haiku | (default) | `/spec` |
23
- | `sequant-qa-checker` | general-purpose | haiku | bypassPermissions | `/qa` |
28
+ | `sequant-qa-checker` | general-purpose | sonnet | bypassPermissions | `/qa` |
24
29
  | `sequant-implementer` | general-purpose | (inherits) | bypassPermissions | `/exec` |
25
30
  | `sequant-testgen` | general-purpose | haiku | (default) | `/testgen` |
26
31
 
@@ -68,7 +73,8 @@ Custom agents are defined in `.claude/agents/*.md` with YAML frontmatter:
68
73
  ---
69
74
  name: sequant-qa-checker
70
75
  description: Quality check agent for sequant QA phase.
71
- model: haiku
76
+ # Note: per anthropics/claude-code#43869 this is currently a no-op; agent runs on parent's model
77
+ model: sonnet
72
78
  permissionMode: bypassPermissions
73
79
  effort: low
74
80
  maxTurns: 15
@@ -139,10 +145,17 @@ Agent(subagent_type="Plan",
139
145
 
140
146
  **Default:** Use `haiku` unless the task requires deep reasoning.
141
147
 
148
+ > **Note:** Per anthropics/claude-code#43869, both the per-call `model:`
149
+ > parameter and the agent-definition `model:` field are currently ignored.
150
+ > Subagents inherit the parent session's model regardless of what you specify
151
+ > here. The guidance below reflects the *intended* tier each task should use
152
+ > once upstream fixes land.
153
+
142
154
  Custom agents set their model in the agent definition, so you don't need to specify it inline:
143
155
 
144
156
  ```
145
- # Model comes from .claude/agents/sequant-qa-checker.md (haiku)
157
+ # Model comes from .claude/agents/sequant-qa-checker.md (sonnet, declared)
158
+ # Note: declared tier currently inert per anthropics/claude-code#43869
146
159
  Agent(subagent_type="sequant-qa-checker",
147
160
  prompt="...")
148
161
  ```
@@ -201,10 +214,10 @@ inline when spawning them.
201
214
 
202
215
  | Task | Recommended Agent | Why |
203
216
  |------|-------------------|-----|
204
- | Quality checks (git diff, npm test) | `sequant-qa-checker` | bypassPermissions + haiku + effort:low |
205
- | Codebase exploration | `sequant-explorer` | Read-only, haiku, focused tools |
217
+ | Quality checks (git diff, npm test) | `sequant-qa-checker` | bypassPermissions + effort:low (declared model: sonnet, inert per #43869) |
218
+ | Codebase exploration | `sequant-explorer` | Read-only, focused tools (declared model: haiku, inert per #43869) |
206
219
  | Implementation subtask | `sequant-implementer` | Full access, inherits model |
207
- | Test stub generation | `sequant-testgen` | Write access, no Bash, haiku |
220
+ | Test stub generation | `sequant-testgen` | Write access, no Bash (declared model: haiku, inert per #43869) |
208
221
  | One-off custom task | `general-purpose` | Flexible, specify model/mode inline |
209
222
 
210
223
  **CRITICAL:** If your background agent runs `git diff`, `npm test`, `git status`, or any shell command, use `sequant-qa-checker` or `sequant-implementer` (both have bypassPermissions). Do NOT use `general-purpose` without `mode="bypassPermissions"` — Bash calls will silently fail.