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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +13 -1
- package/dist/bin/cli.d.ts +1 -1
- package/dist/bin/cli.js +11 -1
- package/dist/bin/preflight.d.ts +21 -0
- package/dist/bin/preflight.js +45 -0
- package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/_shared/references/force-push.md +34 -0
- package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +24 -7
- package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +29 -0
- package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +100 -2
- package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +24 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/anti-pattern-detection.md +285 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/call-site-review.md +202 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/quality-gates.md +287 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/test-quality-checklist.md +272 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/testing-requirements.md +40 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/scripts/quality-checks.sh +95 -11
- package/dist/marketplace/external_plugins/sequant/skills/references/shared/framework-gotchas.md +186 -0
- package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +661 -0
- package/dist/marketplace/external_plugins/sequant/skills/test/references/browser-testing-patterns.md +423 -0
- package/dist/marketplace/external_plugins/sequant/skills/upstream/SKILL.md +419 -0
- package/dist/src/commands/sync.d.ts +1 -0
- package/dist/src/commands/sync.js +56 -1
- package/dist/src/commands/update.js +7 -0
- package/dist/src/lib/errors.d.ts +85 -0
- package/dist/src/lib/errors.js +111 -0
- package/dist/src/lib/version-check.d.ts +19 -0
- package/dist/src/lib/version-check.js +44 -0
- package/dist/src/lib/workflow/batch-executor.js +61 -6
- package/dist/src/lib/workflow/drivers/agent-driver.d.ts +17 -0
- package/dist/src/lib/workflow/drivers/claude-code.d.ts +22 -0
- package/dist/src/lib/workflow/drivers/claude-code.js +111 -7
- package/dist/src/lib/workflow/log-writer.d.ts +1 -1
- package/dist/src/lib/workflow/phase-executor.d.ts +18 -0
- package/dist/src/lib/workflow/phase-executor.js +76 -14
- package/dist/src/lib/workflow/run-log-schema.d.ts +3 -0
- package/dist/src/lib/workflow/run-log-schema.js +7 -0
- package/dist/src/lib/workflow/state-manager.d.ts +1 -0
- package/dist/src/lib/workflow/state-manager.js +6 -0
- package/dist/src/lib/workflow/state-schema.d.ts +3 -0
- package/dist/src/lib/workflow/state-schema.js +7 -0
- package/dist/src/lib/workflow/types.d.ts +17 -0
- package/dist/src/ui/tui/theme.d.ts +18 -4
- package/dist/src/ui/tui/theme.js +18 -4
- package/package.json +4 -3
- package/templates/skills/_shared/references/force-push.md +34 -0
- package/templates/skills/assess/SKILL.md +24 -7
- package/templates/skills/exec/SKILL.md +29 -0
- package/templates/skills/loop/SKILL.md +100 -2
- package/templates/skills/qa/SKILL.md +24 -0
- package/templates/skills/qa/references/anti-pattern-detection.md +285 -0
- package/templates/skills/qa/references/call-site-review.md +202 -0
- package/templates/skills/qa/references/quality-gates.md +287 -0
- package/templates/skills/qa/references/test-quality-checklist.md +272 -0
- package/templates/skills/qa/references/testing-requirements.md +40 -0
- package/templates/skills/qa/scripts/quality-checks.sh +95 -11
- package/templates/skills/references/shared/framework-gotchas.md +186 -0
- package/templates/skills/release/SKILL.md +661 -0
- package/templates/skills/test/references/browser-testing-patterns.md +423 -0
- 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
|
-
|
|
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
|
|
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.
|