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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +73 -0
- package/dist/bin/cli.js +94 -9
- package/dist/src/commands/doctor.d.ts +25 -0
- package/dist/src/commands/doctor.js +36 -1
- package/dist/src/commands/locks.d.ts +67 -0
- package/dist/src/commands/locks.js +290 -0
- package/dist/src/commands/merge.js +11 -0
- package/dist/src/commands/prompt.d.ts +39 -0
- package/dist/src/commands/prompt.js +179 -0
- package/dist/src/commands/run-display.d.ts +11 -2
- package/dist/src/commands/run-display.js +62 -28
- package/dist/src/commands/run-progress.d.ts +32 -0
- package/dist/src/commands/run-progress.js +76 -0
- package/dist/src/commands/run.js +80 -18
- package/dist/src/commands/stats.d.ts +2 -0
- package/dist/src/commands/stats.js +94 -8
- package/dist/src/commands/status.js +12 -0
- package/dist/src/commands/watch.d.ts +16 -0
- package/dist/src/commands/watch.js +147 -0
- package/dist/src/lib/ac-linter.d.ts +1 -1
- package/dist/src/lib/ac-linter.js +81 -0
- package/dist/src/lib/assess-collision-detect.d.ts +91 -0
- package/dist/src/lib/assess-collision-detect.js +217 -0
- package/dist/src/lib/assess-comment-parser.d.ts +59 -1
- package/dist/src/lib/assess-comment-parser.js +124 -2
- package/dist/src/lib/cli-ui/format.d.ts +19 -0
- package/dist/src/lib/cli-ui/format.js +34 -0
- package/dist/src/lib/cli-ui/run-renderer-types.d.ts +181 -0
- package/dist/src/lib/cli-ui/run-renderer-types.js +7 -0
- package/dist/src/lib/cli-ui/run-renderer.d.ts +239 -0
- package/dist/src/lib/cli-ui/run-renderer.js +1173 -0
- package/dist/src/lib/heuristics/behavior-rule-detector.d.ts +94 -0
- package/dist/src/lib/heuristics/behavior-rule-detector.js +467 -0
- package/dist/src/lib/locks/index.d.ts +7 -0
- package/dist/src/lib/locks/index.js +5 -0
- package/dist/src/lib/locks/lock-manager.d.ts +168 -0
- package/dist/src/lib/locks/lock-manager.js +433 -0
- package/dist/src/lib/locks/types.d.ts +59 -0
- package/dist/src/lib/locks/types.js +31 -0
- package/dist/src/lib/qa/markdown-only-ci.d.ts +46 -0
- package/dist/src/lib/qa/markdown-only-ci.js +74 -0
- package/dist/src/lib/relay/activation.d.ts +60 -0
- package/dist/src/lib/relay/activation.js +122 -0
- package/dist/src/lib/relay/archive.d.ts +34 -0
- package/dist/src/lib/relay/archive.js +106 -0
- package/dist/src/lib/relay/frame.d.ts +20 -0
- package/dist/src/lib/relay/frame.js +76 -0
- package/dist/src/lib/relay/index.d.ts +13 -0
- package/dist/src/lib/relay/index.js +13 -0
- package/dist/src/lib/relay/paths.d.ts +43 -0
- package/dist/src/lib/relay/paths.js +59 -0
- package/dist/src/lib/relay/pid.d.ts +34 -0
- package/dist/src/lib/relay/pid.js +72 -0
- package/dist/src/lib/relay/reader.d.ts +35 -0
- package/dist/src/lib/relay/reader.js +115 -0
- package/dist/src/lib/relay/types.d.ts +68 -0
- package/dist/src/lib/relay/types.js +76 -0
- package/dist/src/lib/relay/writer.d.ts +48 -0
- package/dist/src/lib/relay/writer.js +113 -0
- package/dist/src/lib/settings.d.ts +31 -1
- package/dist/src/lib/settings.js +18 -3
- package/dist/src/lib/version-check.d.ts +60 -5
- package/dist/src/lib/version-check.js +97 -9
- package/dist/src/lib/workflow/batch-executor.d.ts +20 -1
- package/dist/src/lib/workflow/batch-executor.js +248 -175
- package/dist/src/lib/workflow/config-resolver.js +4 -0
- package/dist/src/lib/workflow/heartbeat.d.ts +71 -0
- package/dist/src/lib/workflow/heartbeat.js +194 -0
- package/dist/src/lib/workflow/phase-executor.d.ts +62 -8
- package/dist/src/lib/workflow/phase-executor.js +157 -16
- package/dist/src/lib/workflow/phase-mapper.d.ts +3 -2
- package/dist/src/lib/workflow/phase-mapper.js +17 -20
- package/dist/src/lib/workflow/platforms/github.d.ts +1 -1
- package/dist/src/lib/workflow/platforms/github.js +20 -3
- package/dist/src/lib/workflow/pr-status.d.ts +18 -2
- package/dist/src/lib/workflow/pr-status.js +41 -9
- package/dist/src/lib/workflow/qa-stagnation.d.ts +117 -0
- package/dist/src/lib/workflow/qa-stagnation.js +179 -0
- package/dist/src/lib/workflow/run-orchestrator.d.ts +39 -0
- package/dist/src/lib/workflow/run-orchestrator.js +340 -15
- package/dist/src/lib/workflow/run-reflect.js +1 -1
- package/dist/src/lib/workflow/run-state.d.ts +71 -0
- package/dist/src/lib/workflow/run-state.js +14 -0
- package/dist/src/lib/workflow/state-cleanup.d.ts +13 -5
- package/dist/src/lib/workflow/state-cleanup.js +17 -5
- package/dist/src/lib/workflow/state-manager.d.ts +12 -1
- package/dist/src/lib/workflow/state-manager.js +37 -0
- package/dist/src/lib/workflow/state-schema.d.ts +62 -0
- package/dist/src/lib/workflow/state-schema.js +35 -1
- package/dist/src/lib/workflow/types.d.ts +74 -1
- package/dist/src/lib/workflow/worktree-manager.d.ts +8 -1
- package/dist/src/lib/workflow/worktree-manager.js +15 -6
- package/dist/src/mcp/tools/run.d.ts +44 -0
- package/dist/src/mcp/tools/run.js +104 -13
- package/dist/src/ui/tui/App.d.ts +14 -0
- package/dist/src/ui/tui/App.js +41 -0
- package/dist/src/ui/tui/ElapsedTimer.d.ts +10 -0
- package/dist/src/ui/tui/ElapsedTimer.js +31 -0
- package/dist/src/ui/tui/Header.d.ts +6 -0
- package/dist/src/ui/tui/Header.js +15 -0
- package/dist/src/ui/tui/IssueBox.d.ts +16 -0
- package/dist/src/ui/tui/IssueBox.js +68 -0
- package/dist/src/ui/tui/Spinner.d.ts +9 -0
- package/dist/src/ui/tui/Spinner.js +18 -0
- package/dist/src/ui/tui/index.d.ts +15 -0
- package/dist/src/ui/tui/index.js +29 -0
- package/dist/src/ui/tui/theme.d.ts +29 -0
- package/dist/src/ui/tui/theme.js +52 -0
- package/dist/src/ui/tui/truncate.d.ts +11 -0
- package/dist/src/ui/tui/truncate.js +31 -0
- package/package.json +10 -3
- package/templates/agents/sequant-explorer.md +1 -0
- package/templates/agents/sequant-qa-checker.md +2 -1
- package/templates/agents/sequant-testgen.md +1 -0
- package/templates/hooks/post-tool.sh +11 -0
- package/templates/hooks/pre-tool.sh +18 -9
- package/templates/hooks/relay-check.sh +107 -0
- package/templates/relay/frame.txt +11 -0
- package/templates/scripts/cleanup-worktree.sh +25 -3
- package/templates/scripts/new-feature.sh +6 -0
- package/templates/skills/_shared/references/behavior-rule-detection.md +205 -0
- package/templates/skills/_shared/references/subagent-types.md +21 -8
- package/templates/skills/assess/SKILL.md +103 -49
- package/templates/skills/assess/references/predicted-collision-detection.md +109 -0
- package/templates/skills/docs/SKILL.md +141 -22
- package/templates/skills/exec/SKILL.md +10 -8
- package/templates/skills/fullsolve/SKILL.md +79 -5
- package/templates/skills/loop/SKILL.md +28 -0
- package/templates/skills/merger/SKILL.md +621 -0
- package/templates/skills/qa/SKILL.md +727 -8
- package/templates/skills/setup/SKILL.md +6 -0
- package/templates/skills/spec/SKILL.md +52 -0
- package/templates/skills/spec/references/parallel-groups.md +7 -0
- package/templates/skills/spec/references/recommended-workflow.md +4 -2
- 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
|
-
|
|
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
|
-
|
|
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 |
|
|
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
|
-
|
|
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 (
|
|
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 +
|
|
205
|
-
| Codebase exploration | `sequant-explorer` | Read-only, haiku,
|
|
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
|
|
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.
|