sequant 2.6.2 → 2.8.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 (62) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +13 -1
  4. package/dist/bin/cli.d.ts +1 -1
  5. package/dist/bin/cli.js +11 -1
  6. package/dist/bin/preflight.d.ts +21 -0
  7. package/dist/bin/preflight.js +45 -0
  8. package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +1 -1
  9. package/dist/marketplace/external_plugins/sequant/skills/_shared/references/force-push.md +34 -0
  10. package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +24 -7
  11. package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +29 -0
  12. package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +100 -2
  13. package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +24 -0
  14. package/dist/marketplace/external_plugins/sequant/skills/qa/references/anti-pattern-detection.md +285 -0
  15. package/dist/marketplace/external_plugins/sequant/skills/qa/references/call-site-review.md +202 -0
  16. package/dist/marketplace/external_plugins/sequant/skills/qa/references/quality-gates.md +287 -0
  17. package/dist/marketplace/external_plugins/sequant/skills/qa/references/test-quality-checklist.md +272 -0
  18. package/dist/marketplace/external_plugins/sequant/skills/qa/references/testing-requirements.md +40 -0
  19. package/dist/marketplace/external_plugins/sequant/skills/qa/scripts/quality-checks.sh +95 -11
  20. package/dist/marketplace/external_plugins/sequant/skills/references/shared/framework-gotchas.md +186 -0
  21. package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +661 -0
  22. package/dist/marketplace/external_plugins/sequant/skills/test/references/browser-testing-patterns.md +423 -0
  23. package/dist/marketplace/external_plugins/sequant/skills/upstream/SKILL.md +419 -0
  24. package/dist/src/commands/sync.d.ts +1 -0
  25. package/dist/src/commands/sync.js +56 -1
  26. package/dist/src/commands/update.js +7 -0
  27. package/dist/src/lib/errors.d.ts +85 -0
  28. package/dist/src/lib/errors.js +111 -0
  29. package/dist/src/lib/version-check.d.ts +19 -0
  30. package/dist/src/lib/version-check.js +44 -0
  31. package/dist/src/lib/workflow/batch-executor.js +61 -6
  32. package/dist/src/lib/workflow/drivers/agent-driver.d.ts +17 -0
  33. package/dist/src/lib/workflow/drivers/claude-code.d.ts +22 -0
  34. package/dist/src/lib/workflow/drivers/claude-code.js +111 -7
  35. package/dist/src/lib/workflow/log-writer.d.ts +1 -1
  36. package/dist/src/lib/workflow/phase-executor.d.ts +18 -0
  37. package/dist/src/lib/workflow/phase-executor.js +76 -14
  38. package/dist/src/lib/workflow/run-log-schema.d.ts +3 -0
  39. package/dist/src/lib/workflow/run-log-schema.js +7 -0
  40. package/dist/src/lib/workflow/state-manager.d.ts +1 -0
  41. package/dist/src/lib/workflow/state-manager.js +6 -0
  42. package/dist/src/lib/workflow/state-schema.d.ts +3 -0
  43. package/dist/src/lib/workflow/state-schema.js +7 -0
  44. package/dist/src/lib/workflow/types.d.ts +17 -0
  45. package/dist/src/ui/tui/theme.d.ts +18 -4
  46. package/dist/src/ui/tui/theme.js +18 -4
  47. package/package.json +4 -3
  48. package/templates/skills/_shared/references/force-push.md +34 -0
  49. package/templates/skills/assess/SKILL.md +24 -7
  50. package/templates/skills/exec/SKILL.md +29 -0
  51. package/templates/skills/loop/SKILL.md +100 -2
  52. package/templates/skills/qa/SKILL.md +24 -0
  53. package/templates/skills/qa/references/anti-pattern-detection.md +285 -0
  54. package/templates/skills/qa/references/call-site-review.md +202 -0
  55. package/templates/skills/qa/references/quality-gates.md +287 -0
  56. package/templates/skills/qa/references/test-quality-checklist.md +272 -0
  57. package/templates/skills/qa/references/testing-requirements.md +40 -0
  58. package/templates/skills/qa/scripts/quality-checks.sh +95 -11
  59. package/templates/skills/references/shared/framework-gotchas.md +186 -0
  60. package/templates/skills/release/SKILL.md +661 -0
  61. package/templates/skills/test/references/browser-testing-patterns.md +423 -0
  62. package/templates/skills/upstream/SKILL.md +419 -0
@@ -42,6 +42,30 @@ When invoked as `/loop <issue-number>`, your job is to:
42
42
  4. Re-run validation until quality gates pass
43
43
  5. Exit when `READY_FOR_MERGE` or max iterations reached
44
44
 
45
+ ## Orchestration Context
46
+
47
+ When running as part of an orchestrated workflow (e.g., `sequant run` or `/fullsolve`), this skill receives environment variables that indicate the orchestration context:
48
+
49
+ | Environment Variable | Description | Example Value |
50
+ |---------------------|-------------|---------------|
51
+ | `SEQUANT_ORCHESTRATOR` | The orchestrator invoking this skill | `sequant-run` |
52
+ | `SEQUANT_PHASE` | Current phase in the workflow | `loop` |
53
+ | `SEQUANT_ISSUE` | Issue number being processed | `123` |
54
+ | `SEQUANT_WORKTREE` | Path to the feature worktree | `/path/to/worktrees/feature/...` |
55
+
56
+ **Behavior when orchestrated (SEQUANT_ORCHESTRATOR is set):**
57
+
58
+ 1. **Use provided worktree** - Work in `SEQUANT_WORKTREE` path directly
59
+ 2. **Use `SEQUANT_ISSUE`** - Skip issue number parsing from invocation
60
+ 3. **Reduce GitHub comment frequency** - Defer updates to orchestrator
61
+ 4. **Trust issue context** - Orchestrator has already validated issue
62
+
63
+ **Behavior when standalone (SEQUANT_ORCHESTRATOR is NOT set):**
64
+
65
+ - Locate worktree from issue number
66
+ - Post progress comments to GitHub
67
+ - Fetch issue context if needed
68
+
45
69
  ## Invocation
46
70
 
47
71
  - `/loop 123` - Parse log for issue #123, fix issues, re-validate
@@ -50,12 +74,66 @@ When invoked as `/loop <issue-number>`, your job is to:
50
74
 
51
75
  ### Step 1: Read Previous Phase Output
52
76
 
77
+ **The source of findings depends on whether you're running in orchestrated or standalone mode.**
78
+
79
+ #### Step 1A: Orchestrated Mode (SEQUANT_ORCHESTRATOR is set)
80
+
81
+ When `SEQUANT_ORCHESTRATOR` is set, read QA findings from the GitHub issue comments instead of a log file:
82
+
83
+ ```bash
84
+ # Check if we're in orchestrated mode
85
+ if [[ -n "$SEQUANT_ORCHESTRATOR" ]]; then
86
+ echo "Orchestrated mode detected (orchestrator: $SEQUANT_ORCHESTRATOR)"
87
+
88
+ # Use SEQUANT_ISSUE if provided, otherwise parse from invocation
89
+ ISSUE_NUMBER="${SEQUANT_ISSUE:-<issue-number>}"
90
+
91
+ # Fetch QA findings from issue comments (use startswith to avoid matching comments that reference QA format)
92
+ gh issue view "$ISSUE_NUMBER" --json comments -q '.comments[] | select(.body | startswith("## QA Review for Issue")) | .body' | tail -1
93
+ fi
94
+ ```
95
+
96
+ **How to identify QA comments:**
97
+
98
+ | Pattern | Meaning |
99
+ |---------|---------|
100
+ | `## QA Review for Issue #N` | QA phase comment header |
101
+ | `### Verdict:` | Contains AC_NOT_MET, AC_MET_BUT_NOT_A_PLUS, etc. |
102
+ | `### AC Coverage` | Table with MET/NOT_MET/PARTIALLY_MET statuses |
103
+ | `### Required Fixes` or `### Recommendations` | Actionable items to fix |
104
+
105
+ **Parsing QA comment:**
106
+ ```bash
107
+ # Extract verdict from QA comment
108
+ verdict=$(echo "$qa_comment" | grep -oE "Verdict:\s*\w+" | head -1 | awk '{print $2}' || true)
109
+
110
+ # Extract NOT_MET AC items
111
+ not_met_acs=$(echo "$qa_comment" | grep -E "NOT_MET|PARTIALLY_MET" || true)
112
+
113
+ # Extract recommendations section
114
+ recommendations=$(echo "$qa_comment" | sed -n '/### Required Fixes/,/###/p' | head -n -1)
115
+ ```
116
+
117
+ **If no QA comment found in orchestrated mode:**
118
+ 1. Log a clear error: `"Warning: No QA comment found in issue #N"`
119
+ 2. Fall back to Step 1B (log file) as a recovery mechanism
120
+ 3. If log file also doesn't exist, exit with error
121
+
122
+ #### Step 1B: Standalone Mode (no SEQUANT_ORCHESTRATOR)
123
+
124
+ When running standalone (interactive `/loop` invocation), read from the log file:
125
+
53
126
  Use the Read tool to read the log file for this issue:
54
127
  ```
55
128
  Read(file_path="/tmp/claude-issue-<issue-number>.log")
56
129
  ```
57
130
 
58
- Parse the log to find:
131
+ **If log file doesn't exist:**
132
+ - Error: `"Log file not found at /tmp/claude-issue-<N>.log. Please run /spec, /exec, /test, or /qa first."`
133
+
134
+ #### Parsing Findings (Both Modes)
135
+
136
+ Parse the output (from comment or log file) to find:
59
137
  - **Last phase executed:** `/test` or `/qa`
60
138
  - **Verdict:** `READY_FOR_MERGE`, `AC_MET_BUT_NOT_A_PLUS`, `NEEDS_VERIFICATION`,
61
139
  or `AC_NOT_MET`
@@ -102,6 +180,12 @@ Extract:
102
180
 
103
181
  ### Step 4: Locate Feature Worktree
104
182
 
183
+ **If orchestrated (SEQUANT_WORKTREE is set):**
184
+ - Use the provided worktree path directly: `cd $SEQUANT_WORKTREE`
185
+ - Skip the lookup steps below
186
+
187
+ **If standalone:**
188
+
105
189
  Find the worktree for this issue:
106
190
  ```bash
107
191
  git worktree list | grep -E "feature.*<issue-number>" || true
@@ -365,7 +449,21 @@ For each iteration, output:
365
449
 
366
450
  ## Error Handling
367
451
 
368
- **If log file doesn't exist:**
452
+ **If orchestrated but no QA comment found:**
453
+ ```
454
+ Warning: No QA comment found in issue #<N>
455
+ Attempting fallback to log file...
456
+ ```
457
+
458
+ If fallback also fails:
459
+ ```
460
+ Error: No QA findings available.
461
+ - No QA comment found in issue #<N>
462
+ - Log file not found at /tmp/claude-issue-<N>.log
463
+ Please run /qa <N> first.
464
+ ```
465
+
466
+ **If log file doesn't exist (standalone mode):**
369
467
  ```
370
468
  Error: Log file not found at /tmp/claude-issue-<N>.log
371
469
  Please run /spec, /exec, /test, or /qa first.
@@ -1085,6 +1085,30 @@ skill_modified=$(git diff main...HEAD --name-only | grep -E "^\.(claude/skills|s
1085
1085
  ```
1086
1086
  If skill files are modified, the quality-checks.sh script automatically runs the three-directory sync check (section 12). If divergence is detected, this blocks `READY_FOR_MERGE` — verdict becomes `AC_MET_BUT_NOT_A_PLUS` with a note to run `npx tsx scripts/check-skill-sync.ts --fix`.
1087
1087
 
1088
+ #### Turn-Capped Checks → Inconclusive (not a phase failure)
1089
+
1090
+ A spawned quality-check sub-agent runs under a `maxTurns` cap (live on every agent since #484). When an agent hits that cap, its work is **partial**, not failed — the driver returns it flagged `capped: true` and warns rather than erroring (#733).
1091
+
1092
+ **How to recognize a turn-capped check:** the sub-agent's reported output is truncated mid-analysis, ends without a verdict, or explicitly notes it ran out of turns (look for `error_max_turns`, "turn cap", or "Returning partial results" in its output).
1093
+
1094
+ **How to handle it — do NOT fail the whole QA phase on a cap alone:**
1095
+
1096
+ - Mark that single check **⚠️ Inconclusive** in the QA summary, with a one-line reason (`hit turn cap — partial analysis`).
1097
+ - Use whatever partial findings the capped agent *did* surface; do not discard them.
1098
+ - Let the **other** checks proceed and contribute to the verdict normally.
1099
+ - If a capped check leaves an AC genuinely unverified, the verdict is `NEEDS_VERIFICATION` for that AC — never a hard `AC_NOT_MET` justified solely by the cap.
1100
+ - Surface inconclusive checks prominently so a human can re-run that check (e.g. with a higher cap) rather than silently treating the cap as a pass.
1101
+
1102
+ ```markdown
1103
+ ### Quality Checks
1104
+
1105
+ | Check | Status | Notes |
1106
+ |-------|--------|-------|
1107
+ | Type safety | ✅ Pass | 0 issues |
1108
+ | Scope/size | ⚠️ Inconclusive | hit turn cap — partial analysis, re-run recommended |
1109
+ | Security | ✅ Pass | 0 critical |
1110
+ ```
1111
+
1088
1112
  See [quality-gates.md](references/quality-gates.md) for detailed verdict synthesis.
1089
1113
 
1090
1114
  ### Using MCP Tools (Optional)
@@ -0,0 +1,285 @@
1
+ # Anti-Pattern Detection
2
+
3
+ ## Purpose
4
+
5
+ Lightweight pattern-matching sanity checks to catch issues that experts would flag immediately. These checks complement code review by automating detection of common mistakes.
6
+
7
+ ## Part 1: New Dependency Audit
8
+
9
+ ### When to Run
10
+
11
+ Run dependency audit when `package.json` is modified:
12
+
13
+ ```bash
14
+ # Check if package.json was modified
15
+ pkg_modified=$(git diff main...HEAD --name-only | grep -E "^package\.json$" | head -1)
16
+ if [[ -n "$pkg_modified" ]]; then
17
+ echo "package.json modified - running dependency audit"
18
+ fi
19
+ ```
20
+
21
+ ### Detecting New Dependencies
22
+
23
+ ```bash
24
+ # Get new dependencies (added in this branch)
25
+ git diff main...HEAD -- package.json | grep '^\+.*":' | grep -v '^\+\+\+' | sed 's/.*"\([^"]*\)".*/\1/' | grep -v -E '^(@types/|version|name|description|scripts|devDependencies|dependencies|peerDependencies)$'
26
+ ```
27
+
28
+ ### Audit Criteria
29
+
30
+ | Flag | Threshold | Detection Method | Risk Level |
31
+ |------|-----------|------------------|------------|
32
+ | Low downloads | <1,000/week | `npm view <pkg> --json \| jq '.downloads'` | ⚠️ Medium |
33
+ | Stale | No updates 12+ months | `npm view <pkg> time.modified` | ⚠️ Medium |
34
+ | License risk | UNLICENSED, GPL in MIT project | `npm view <pkg> license` | ❌ High |
35
+ | Size concern | >100kb for utility | `npm view <pkg> dist.unpackedSize` | ⚠️ Low |
36
+ | Security advisory | Known vulnerabilities | `npm audit --json` | ❌ High |
37
+
38
+ ### Audit Commands
39
+
40
+ ```bash
41
+ # Get package metadata (run for each new dependency)
42
+ pkg="<package-name>"
43
+
44
+ # Weekly downloads (requires npm API)
45
+ downloads=$(curl -s "https://api.npmjs.org/downloads/point/last-week/$pkg" | jq '.downloads // 0')
46
+
47
+ # Last update date
48
+ last_update=$(npm view "$pkg" time.modified 2>/dev/null)
49
+
50
+ # License
51
+ license=$(npm view "$pkg" license 2>/dev/null)
52
+
53
+ # Unpacked size (in bytes)
54
+ size=$(npm view "$pkg" dist.unpackedSize 2>/dev/null)
55
+
56
+ # Security check (all dependencies)
57
+ npm audit --json 2>/dev/null | jq '.vulnerabilities | length'
58
+ ```
59
+
60
+ ### Output Format
61
+
62
+ ```markdown
63
+ ### Dependency Audit
64
+
65
+ | Package | Downloads/wk | Last Update | License | Size | Flags |
66
+ |---------|--------------|-------------|---------|------|-------|
67
+ | foo-pkg | 500 | 2023-01-15 | MIT | 45kb | ⚠️ Low downloads, ⚠️ Stale |
68
+ | bar-lib | 50,000 | 2024-12-01 | Apache-2.0 | 120kb | ⚠️ Size |
69
+
70
+ **Security Audit:** 0 vulnerabilities found
71
+
72
+ **Flagged Dependencies:**
73
+ 1. `foo-pkg` - Low downloads (<1,000/week) and stale (12+ months)
74
+ - **Risk:** Unmaintained packages may have unpatched vulnerabilities
75
+ - **Suggestion:** Consider alternative with active maintenance or vendor the code
76
+ ```
77
+
78
+ ### Verdict Impact
79
+
80
+ | Finding | Verdict Impact |
81
+ |---------|----------------|
82
+ | Low downloads only | Note in QA, no verdict change |
83
+ | Stale only | Note in QA, no verdict change |
84
+ | Security vulnerability (moderate+) | `AC_NOT_MET` (blocker) |
85
+ | License incompatibility | `AC_NOT_MET` (blocker) |
86
+ | Multiple flags on same package | `AC_MET_BUT_NOT_A_PLUS` |
87
+
88
+ ---
89
+
90
+ ## Part 2: Code Pattern Checks
91
+
92
+ ### Anti-Pattern Detection Matrix
93
+
94
+ | Category | Pattern | Detection | Suggestion | Risk |
95
+ |----------|---------|-----------|------------|------|
96
+ | **Performance** | N+1 query (`await` in loop) | `for.*await\|\.forEach.*await` | Use batch query or `Promise.all` | ⚠️ Medium |
97
+ | **Performance** | Unbounded loop | `while.*true\|for.*;;` without break limit | Add iteration limit | ⚠️ Medium |
98
+ | **Performance** | Sync file ops in async context | `fs\.readFileSync\|fs\.writeFileSync` | Use async fs methods | ⚠️ Low |
99
+ | **Error Handling** | Empty catch block | `catch.*\{\s*\}` | Log or rethrow | ⚠️ Medium |
100
+ | **Error Handling** | Swallowed error | `catch.*\{\s*//` | Handle or rethrow | ⚠️ Medium |
101
+ | **Error Handling** | Unhandled promise | `\.then\(.*\)[^.]` without `.catch` | Add `.catch()` or use try/catch | ⚠️ Medium |
102
+ | **Security** | Hardcoded secret | `(api[_-]?key\|secret\|password)\s*[:=]\s*['"][^'"]+['"]` | Use env variable | ❌ High |
103
+ | **Security** | SQL concatenation | `\+.*SELECT\|SELECT.*\+\|'.*\$\{.*\}.*SELECT` | Use parameterized query | ❌ High |
104
+ | **Security** | eval usage | `eval\(` | Remove eval, use safe alternative | ❌ High |
105
+ | **Security** | Server binds all interfaces | `.listen(port)` without explicit host | Bind to `127.0.0.1` for local-only servers | ❌ High |
106
+ | **Memory** | Uncleared interval | `setInterval\(` without corresponding `clearInterval` | Clear in cleanup | ⚠️ Medium |
107
+ | **Memory** | Uncleared timeout | `setTimeout\(` in component without cleanup | Clear in useEffect cleanup | ⚠️ Low |
108
+ | **A11y** | Image without alt | `<img[^>]*(?!alt=)[^>]*>` | Add descriptive alt | ⚠️ Low |
109
+ | **A11y** | Click handler without keyboard | `onClick=\{` without `onKeyDown` | Add keyboard handler | ⚠️ Low |
110
+
111
+ ### Detection Commands
112
+
113
+ ```bash
114
+ # Run on changed files only
115
+ changed_files=$(git diff main...HEAD --name-only | grep -E '\.(ts|tsx|js|jsx)$')
116
+
117
+ # N+1 query pattern (await in loop)
118
+ grep -n -E 'for\s*\([^)]*\)\s*\{[^}]*await|\.forEach\([^)]*async' $changed_files 2>/dev/null
119
+
120
+ # Empty catch block
121
+ grep -n -E 'catch\s*\([^)]*\)\s*\{\s*\}' $changed_files 2>/dev/null
122
+
123
+ # Hardcoded secrets (case insensitive)
124
+ grep -ni -E '(api[_-]?key|secret|password|token)\s*[:=]\s*['"'"'"][^'"'"'"]+['"'"'"]' $changed_files 2>/dev/null | grep -v -E '(\.env|example|test|mock|placeholder)'
125
+
126
+ # SQL concatenation
127
+ grep -n -E "(\+\s*['\"].*SELECT|SELECT.*['\"].*\+|\`.*\$\{.*\}.*SELECT)" $changed_files 2>/dev/null
128
+
129
+ # Server binding to all interfaces (0.0.0.0)
130
+ grep -n -E '\.listen\(\s*(port|[0-9]+)\s*[,)]' $changed_files 2>/dev/null | grep -v -E '127\.0\.0\.1|localhost'
131
+
132
+ # Uncleared intervals (check for setInterval without corresponding clear)
133
+ for f in $changed_files; do
134
+ intervals=$(grep -c 'setInterval(' "$f" 2>/dev/null || echo 0)
135
+ clears=$(grep -c 'clearInterval(' "$f" 2>/dev/null || echo 0)
136
+ if [[ $intervals -gt $clears ]]; then
137
+ echo "$f: $intervals setInterval calls, only $clears clearInterval calls"
138
+ fi
139
+ done
140
+ ```
141
+
142
+ ### Output Format
143
+
144
+ ```markdown
145
+ ### Code Pattern Analysis
146
+
147
+ | File:Line | Category | Pattern | Suggestion |
148
+ |-----------|----------|---------|------------|
149
+ | `src/api/users.ts:45` | Performance | N+1 query | Use `Promise.all` for batch fetching |
150
+ | `src/utils/config.ts:12` | Error Handling | Empty catch | Log error or rethrow |
151
+ | `src/components/List.tsx:89` | Memory | Uncleared interval | Add `clearInterval` in cleanup |
152
+
153
+ **Critical Issues:** 0
154
+ **Warnings:** 3
155
+
156
+ **Details:**
157
+
158
+ 1. **N+1 Query** at `src/api/users.ts:45`
159
+ ```typescript
160
+ // Current (N+1)
161
+ for (const id of userIds) {
162
+ await fetchUser(id); // Makes N requests
163
+ }
164
+
165
+ // Suggested
166
+ await Promise.all(userIds.map(id => fetchUser(id)));
167
+ // Or use batch endpoint
168
+ await fetchUsers(userIds);
169
+ ```
170
+
171
+ 2. **Empty Catch** at `src/utils/config.ts:12`
172
+ ```typescript
173
+ // Current
174
+ try { ... } catch (e) { }
175
+
176
+ // Suggested
177
+ try { ... } catch (e) {
178
+ console.error('Config load failed:', e);
179
+ // or rethrow: throw e;
180
+ }
181
+ ```
182
+ ```
183
+
184
+ ### Verdict Impact
185
+
186
+ | Finding | Verdict Impact |
187
+ |---------|----------------|
188
+ | Performance warnings | Note in QA, no verdict change |
189
+ | Empty catch blocks | `AC_MET_BUT_NOT_A_PLUS` |
190
+ | Security issues (secrets, SQL injection) | `AC_NOT_MET` (blocker) |
191
+ | Uncleared intervals/timeouts | `AC_MET_BUT_NOT_A_PLUS` |
192
+ | A11y issues | Note in QA, no verdict change |
193
+
194
+ ---
195
+
196
+ ## Part 3: Integration with QA Workflow
197
+
198
+ ### When to Run
199
+
200
+ 1. **Dependency Audit:** Only when `package.json` is modified
201
+ 2. **Code Pattern Checks:** Always run on changed `.ts/.tsx/.js/.jsx` files
202
+
203
+ ### Execution Order
204
+
205
+ ```
206
+ 1. Standard quality checks (type safety, deleted tests, scope)
207
+ 2. Dependency audit (if package.json modified)
208
+ 3. Code pattern checks
209
+ 4. Test quality review (if test files modified)
210
+ 5. Execution evidence (if scripts/CLI modified)
211
+ ```
212
+
213
+ ### Combined Output Section
214
+
215
+ ```markdown
216
+ ### Anti-Pattern Detection
217
+
218
+ #### Dependency Audit
219
+ [Include if package.json modified, otherwise: "N/A - No dependency changes"]
220
+
221
+ #### Code Patterns
222
+ [Always include for code changes]
223
+
224
+ **Summary:**
225
+ - Dependencies flagged: X
226
+ - Code patterns flagged: Y
227
+ - Critical issues: Z (blockers)
228
+ ```
229
+
230
+ ---
231
+
232
+ ## Appendix: Full Detection Script
233
+
234
+ For automation, this script can be run to check all patterns:
235
+
236
+ ```bash
237
+ #!/bin/bash
238
+ # anti-pattern-check.sh
239
+
240
+ set -e
241
+
242
+ echo "=== Anti-Pattern Detection ==="
243
+
244
+ # Get changed files
245
+ CHANGED_TS=$(git diff main...HEAD --name-only | grep -E '\.(ts|tsx|js|jsx)$' || true)
246
+ PKG_CHANGED=$(git diff main...HEAD --name-only | grep -E '^package\.json$' || true)
247
+
248
+ # 1. Dependency Audit
249
+ if [[ -n "$PKG_CHANGED" ]]; then
250
+ echo ""
251
+ echo "## Dependency Audit"
252
+ # Get new deps
253
+ NEW_DEPS=$(git diff main...HEAD -- package.json | grep '^\+.*":' | grep -v '^\+\+\+' | sed 's/.*"\([^"]*\)".*/\1/' | grep -v -E '^(@types/|version|name|description|scripts|devDependencies|dependencies|peerDependencies|engines|repository|author|license|bugs|homepage|main|module|types)$' || true)
254
+
255
+ if [[ -n "$NEW_DEPS" ]]; then
256
+ echo "New dependencies detected:"
257
+ for dep in $NEW_DEPS; do
258
+ echo " - $dep"
259
+ done
260
+ else
261
+ echo "No new dependencies added"
262
+ fi
263
+ fi
264
+
265
+ # 2. Code Pattern Checks
266
+ if [[ -n "$CHANGED_TS" ]]; then
267
+ echo ""
268
+ echo "## Code Pattern Checks"
269
+
270
+ echo ""
271
+ echo "### N+1 Queries (await in loop):"
272
+ grep -n -E 'for\s*\([^)]*\)\s*\{[^}]*await|\.forEach\([^)]*async' $CHANGED_TS 2>/dev/null || echo " None found"
273
+
274
+ echo ""
275
+ echo "### Empty Catch Blocks:"
276
+ grep -n -E 'catch\s*\([^)]*\)\s*\{\s*\}' $CHANGED_TS 2>/dev/null || echo " None found"
277
+
278
+ echo ""
279
+ echo "### Potential Hardcoded Secrets:"
280
+ grep -ni -E '(api[_-]?key|secret|password|token)\s*[:=]\s*['"'"'"][^'"'"'"]+['"'"'"]' $CHANGED_TS 2>/dev/null | grep -v -E '(\.env|example|test|mock|placeholder|process\.env)' || echo " None found"
281
+ fi
282
+
283
+ echo ""
284
+ echo "=== End Anti-Pattern Detection ==="
285
+ ```
@@ -0,0 +1,202 @@
1
+ # Call-Site Review Checklist
2
+
3
+ ## Purpose
4
+
5
+ When new exported functions are added, QA must review not just the function itself but **where** and **how** it's called. A function can be perfectly implemented but called incorrectly at the call site.
6
+
7
+ **Origin:** Issue #295 — `rebaseBeforePR()` had thorough unit tests but was called for every issue in a chain loop when the AC specified "only the final branch."
8
+
9
+ ## When to Apply
10
+
11
+ This review is **triggered** when new exported functions are detected in the diff:
12
+
13
+ ```bash
14
+ # Detect new exported functions (added lines only)
15
+ # Catches: export function foo, export async function foo,
16
+ # export const foo = () =>, export const foo = async () =>
17
+ git diff main...HEAD | grep -E '^\+export (async )?function \w+' | sed 's/^+//'
18
+ git diff main...HEAD | grep -E '^\+export const \w+ = (async )?\(' | sed 's/^+//'
19
+ ```
20
+
21
+ ## Review Steps
22
+
23
+ ### Step 1: Call-Site Inventory
24
+
25
+ For each new exported function, find where it's called using the Grep tool:
26
+
27
+ ```bash
28
+ # Find call sites for a function (exclude test files)
29
+ Grep(pattern="functionName\\(", glob="*.{ts,tsx}", output_mode="content")
30
+ # Then exclude results from .test. files and __tests__ directories
31
+ ```
32
+
33
+ ### Step 2: Condition Audit
34
+
35
+ For each call site, identify:
36
+
37
+ 1. **What conditions gate the call?**
38
+ - `if` statements, `&&` guards, ternaries
39
+ - Mode flags, feature flags, environment checks
40
+
41
+ 2. **Is it in a loop?**
42
+ - `for`, `while`, `forEach`, `.map()`, `.filter()`, etc.
43
+ - If yes: Should it run for every iteration or only specific ones?
44
+
45
+ 3. **Is it in an async context?**
46
+ - Is `await` used appropriately?
47
+ - Could it cause race conditions?
48
+
49
+ 4. **Error handling at call site?**
50
+ - Is the call wrapped in try/catch?
51
+ - What happens if the function throws?
52
+
53
+ **AC Constraint Matching:**
54
+
55
+ Compare call-site conditions against AC constraints:
56
+
57
+ | AC Constraint | Call Site Check |
58
+ |---------------|-----------------|
59
+ | "Only for X" | Is there a guard: `if (X)` before the call? |
60
+ | "When Y happens" | Is the call triggered by the Y event/condition? |
61
+ | "Not in Z mode" | Is there a guard: `if (!Z)` or similar? |
62
+ | "Final item only" | If in loop, is there an index check or break? |
63
+
64
+ ### Step 3: Loop Awareness
65
+
66
+ When a function is called inside a loop, answer:
67
+
68
+ 1. **Iteration scope:**
69
+ - Should it run for ALL iterations? → OK
70
+ - Should it run for FIRST/LAST only? → Check for index guard
71
+ - Should it run for SOME iterations? → Check for condition filter
72
+
73
+ 2. **Common patterns to verify:**
74
+
75
+ ```typescript
76
+ // LAST only - should have index check
77
+ for (let i = 0; i < items.length; i++) {
78
+ if (i === items.length - 1) {
79
+ newFunction(); // ✅ Guarded for last
80
+ }
81
+ }
82
+
83
+ // FIRST only - should have index check
84
+ items.forEach((item, index) => {
85
+ if (index === 0) {
86
+ newFunction(); // ✅ Guarded for first
87
+ }
88
+ });
89
+
90
+ // CONDITIONAL - should have filter
91
+ for (const item of items) {
92
+ if (item.shouldProcess) {
93
+ newFunction(item); // ✅ Conditionally called
94
+ }
95
+ }
96
+ ```
97
+
98
+ 3. **Red flags:**
99
+ - Function called unconditionally in loop when AC says "only once"
100
+ - No break/return after the call when AC implies single execution
101
+ - Missing mode/flag guard when AC specifies conditions
102
+
103
+ ### Step 4: Mode Sensitivity
104
+
105
+ If the function behaves differently based on mode flags:
106
+
107
+ 1. **Identify mode parameters:**
108
+ - Does the function accept `options` or `config`?
109
+ - Are there mode-specific code paths inside?
110
+
111
+ 2. **Verify at call site:**
112
+ - Is the mode passed correctly?
113
+ - Does the caller's context match the mode expected?
114
+
115
+ ## Output Format
116
+
117
+ ```markdown
118
+ ### Call-Site Review
119
+
120
+ **New exported functions detected:** N
121
+
122
+ | Function | Call Sites | Loop? | Conditions | AC Match |
123
+ |----------|-----------|-------|------------|----------|
124
+ | `newFunction()` | `file.ts:123` | No | `if (condition)` | ✅ Matches AC-2 |
125
+ | `anotherFunc()` | `run.ts:456` | Yes (forEach) | None | ⚠️ Missing guard (AC-3 says "final only") |
126
+ | `thirdFunc()` | Not called | - | - | ⚠️ Unused export |
127
+
128
+ **Findings:**
129
+ - `anotherFunc()` is called in a loop without iteration guard. AC-3 specifies "only for the final item."
130
+
131
+ **Recommendations:**
132
+ 1. Add index check: `if (index === items.length - 1)` before calling `anotherFunc()`
133
+ ```
134
+
135
+ ## Verdict Impact
136
+
137
+ | Finding | Verdict Impact |
138
+ |---------|----------------|
139
+ | All call sites match AC | No impact |
140
+ | Call site missing AC-required guard | `AC_NOT_MET` |
141
+ | Function not called anywhere | `AC_MET_BUT_NOT_A_PLUS` (dead export) |
142
+ | Call site in loop, AC unclear about iteration | `NEEDS_VERIFICATION` |
143
+
144
+ ## Examples
145
+
146
+ ### Example 1: Missing Chain Mode Guard (Issue #295)
147
+
148
+ **AC:** "Rebase only the final branch in a chain"
149
+
150
+ **Detection:**
151
+
152
+ ```bash
153
+ Grep(pattern="rebaseBeforePR\\(", glob="*.ts", output_mode="content")
154
+ # Output: src/run.ts:2977: await rebaseBeforePR(worktreePath)
155
+ ```
156
+
157
+ **Analysis:**
158
+
159
+ ```typescript
160
+ // Found in loop over all chain issues
161
+ for (const result of chainResults) {
162
+ if (result.success && result.worktreePath) {
163
+ await rebaseBeforePR(result.worktreePath); // ⚠️ Called for ALL
164
+ }
165
+ }
166
+ ```
167
+
168
+ **Finding:** No guard for "final only" — function called for every issue in chain.
169
+
170
+ **Fix:** Add final-issue check:
171
+
172
+ ```typescript
173
+ for (let i = 0; i < chainResults.length; i++) {
174
+ const result = chainResults[i];
175
+ const isFinal = i === chainResults.length - 1;
176
+ if (result.success && result.worktreePath && isFinal) {
177
+ await rebaseBeforePR(result.worktreePath); // ✅ Only final
178
+ }
179
+ }
180
+ ```
181
+
182
+ ### Example 2: Correct Guard Present
183
+
184
+ **AC:** "Send notification only when status is 'complete'"
185
+
186
+ **Detection:**
187
+
188
+ ```bash
189
+ Grep(pattern="sendNotification\\(", glob="*.ts", output_mode="content")
190
+ # Output: src/handlers.ts:89: sendNotification(user.email, message)
191
+ ```
192
+
193
+ **Analysis:**
194
+
195
+ ```typescript
196
+ // Found with proper guard
197
+ if (task.status === 'complete') {
198
+ sendNotification(user.email, message); // ✅ Guarded
199
+ }
200
+ ```
201
+
202
+ **Finding:** Call site condition matches AC constraint. No issues.