@mmerterden/multi-agent-pipeline 16.13.0 → 16.15.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/CHANGELOG.md +24 -0
- package/docs/adr/0010-own-code-graph.md +6 -0
- package/package.json +1 -1
- package/pipeline/commands/multi-agent/help/SKILL.md +42 -39
- package/pipeline/multi-agent-refs/analysis/evidence.md +21 -0
- package/pipeline/multi-agent-refs/features/code-graph.md +8 -1
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +4 -0
- package/pipeline/multi-agent-refs/phases/phase-7-report.md +1 -1
- package/pipeline/rules/outside-the-pipeline.md +1 -1
- package/pipeline/schemas/code-graph.schema.json +1 -1
- package/pipeline/scripts/_code-graph.mjs +28 -1
- package/pipeline/scripts/code-graph-rules/go.json +124 -0
- package/pipeline/scripts/test-gap-rules/go.json +24 -0
- package/pipeline/scripts/validate-code-graph.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,30 @@ Internal file-layout changes that don't affect the slash-command surface are sti
|
|
|
16
16
|
|
|
17
17
|
## [Unreleased]
|
|
18
18
|
|
|
19
|
+
## [16.15.0] - 2026-08-30
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **Usage reporting only ever reported runs that finished.** The reporter was called from Phase 7, and Phase 7 is the phase a run is least likely to reach: across the whole local log history, exactly one run got there. So the admin dashboard read zero of everything while the table held real rows, and the question it exists to answer - who is using this - had no answer. Phase 0's exit gate now reports the run as started, on the same upsert key, so Phase 7 replaces that row with the final state and nothing is double-counted. The read side of the same bug is fixed in the dashboard: its rollup folded only `terminal` rows, and an unfinished run was scored `fail`.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- **`/multi-agent:help` now covers the plugins and the toolkit MCP.** It named both in passing - a `refactor` line, a `sync` line, a `design-check` footnote - and never said what they are or how to call one. The catalog gained a Plugins & tools block in both languages: how a stack skill is invoked, which plugins are always on, and the MCP's 83 tools grouped by what they drive (device control, web, design comparison, store and accessibility audits), with the note that its registration survives uninstall. Paid for by compressing the `design-check` entry, which was restating its own contract in fifteen lines: 353 tokens reclaimed before a byte was added, so the file sits within its 8500 ceiling. The tool count that went in with it was wrong and is now absent: `rules/outside-the-pipeline.md` said 83 while the server serves 84, and the pipeline cannot check either number because it does not vendor the toolkit. Both places now say 80+, which stays true; the exact count lives in the toolkit's own README, where a gate boots the server and compares `tools/list` against it.
|
|
29
|
+
|
|
30
|
+
- **ADR-0010 now says where its numbers come from.** The coverage and token figures were published with no path to the harness that produced them, because the harness names a private codebase and cannot ship. It lives beside the graph it measured, under `~/.claude/knowledge/<project>/gate/`, and the ADR points at it: a number nobody can re-derive is an assertion, not a measurement.
|
|
31
|
+
|
|
32
|
+
## [16.14.0] - 2026-08-28
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- **Go joins the code graph, and `web` / `frontend` resolve to the node rules.** Phase 1 has detected Go since the stack table existed, while both rule layers rejected it: there was no `test-gap-rules/go.json` either, so `/multi-agent:test "test-gap"` was equally blind to a Go repo. Both files now exist. The alias is the other half of the same gap: the rule files are named after the toolchain (`node`) and the rest of the pipeline names the product surface (`web`), so a caller typing the word they were last shown got "unsupported". `backend` is deliberately NOT an alias - it could be node, python or go, and the error names all three rather than guessing one.
|
|
37
|
+
- **Analysis ranks its repo-evidence candidates with the graph (opt-in).** The reuse-first scan (Locked 11) caps its candidate set at 200 files and warns when a generic feature name blows past it. `head -200` truncates in grep's walk order, so the cap was dropping an arbitrary file, not the least relevant one. With `prefs.global.codeGraph.enabled`, a graph query over the feature name sorts the set first. It is a sort key, never a filter: a file grep found and the graph did not rank is still scanned, because a regex extractor's silence is not evidence of absence. The 13 buckets, the cap and the no-graph behaviour are unchanged.
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- **The code-graph ref did not say to build when no graph exists.** It said "build only when `baseCommit` no longer matches HEAD", which has no answer when there is nothing to compare. That is the case that matters: the knowledge base's prose files are written in Phase 7, the phase a run is least likely to reach, so a repo can carry a long task history and an empty knowledge directory. The graph must not inherit that dependency, and `--status` exiting 1 on a missing file now explicitly means build.
|
|
42
|
+
|
|
19
43
|
## [16.13.0] - 2026-08-28
|
|
20
44
|
|
|
21
45
|
### Added
|
|
@@ -96,6 +96,12 @@ domain words, the graph scored 63.3% against 32.0% at 10,937 tokens against
|
|
|
96
96
|
24,983. The value is in narrowing an open-ended search, not in replacing a
|
|
97
97
|
grep for a name you already know.
|
|
98
98
|
|
|
99
|
+
The harness, the ten graded questions and the raw per-question result live
|
|
100
|
+
beside the graph they measured, under `~/.claude/knowledge/<project>/gate/`.
|
|
101
|
+
They stay there rather than in this repo because they name the private
|
|
102
|
+
codebase's files and symbols, and a number nobody can re-derive is an assertion
|
|
103
|
+
rather than a measurement: `node run-gate.mjs` reproduces the table above.
|
|
104
|
+
|
|
99
105
|
That measurement covers the context each strategy assembles, not the quality
|
|
100
106
|
of an answer written from it. graphify's numbers measure the latter. The two
|
|
101
107
|
are not directly comparable and this ADR does not claim they are.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mmerterden/multi-agent-pipeline",
|
|
3
|
-
"version": "16.
|
|
3
|
+
"version": "16.15.0",
|
|
4
4
|
"description": "8-phase AI development pipeline with full orchestration on Claude Code, Copilot CLI and Codex CLI. Analysis, planning, TDD, CLI-aware parallel review with consensus surfacing + Fable triage, default-FAIL evidence gates, secret + intent guards, per-phase cost ledger, persistent learnings memory, wiki generation, commit automation. Token-preserving uninstall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -147,8 +147,17 @@ Setup & Maintenance:
|
|
|
147
147
|
|
|
148
148
|
------------------------------------------------------------
|
|
149
149
|
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
Plugins & tools (called directly, no wrappers):
|
|
151
|
+
|
|
152
|
+
Stack plugins ai-<stack>-toolkit:<skill>, e.g. ai-ios-toolkit:create-component
|
|
153
|
+
Catalog: ai-<stack>-toolkit:help Enable per repo: /multi-agent:stack
|
|
154
|
+
common + analyst are always on; the rest follow the repo's stack.
|
|
155
|
+
Toolkit MCP multi-agent-toolkit - 80+ tools for a RUNNING app, no pipeline run needed:
|
|
156
|
+
ios_* / android_* (launch, tap, screenshot, ui tree, logs, crashes,
|
|
157
|
+
locale, dark mode), web_*, design_* (Design Check above), and the
|
|
158
|
+
audits (ios_app_store_audit, ios_testflight_validate, android_apk_audit,
|
|
159
|
+
*_accessibility_audit). Use them instead of guessing about on-screen
|
|
160
|
+
state. Its registration survives uninstall. Not registered = silent no-op.
|
|
152
161
|
|
|
153
162
|
------------------------------------------------------------
|
|
154
163
|
|
|
@@ -202,24 +211,17 @@ Manual Test (Phase 5 standalone - Xcode hint flow):
|
|
|
202
211
|
/multi-agent:manual-test Checkout task branch, print Xcode/SourceTree hints,
|
|
203
212
|
/multi-agent:manual-test #N wait for your "ok" / "fix: ..." verdict.
|
|
204
213
|
|
|
205
|
-
Design Check (mock-mode vs Figma, local-only
|
|
206
|
-
|
|
207
|
-
/multi-agent:design-check Whole-module
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
/multi-agent:design-check
|
|
214
|
-
|
|
215
|
-
/
|
|
216
|
-
/multi-agent:design-check --resume Audit the unaudited remainder of the last run for this repo+module.
|
|
217
|
-
# COVERAGE GATE: every target is either audited or skipped WITH a concrete reason. Anything else fails the
|
|
218
|
-
# gate and the run is reported INCOMPLETE with the missing target ids - a partial audit never reads as clean.
|
|
219
|
-
# "Requires a scenario / prefix / launch-arg" is not a reason; reaching those states is the run's job.
|
|
220
|
-
# When a live screen has no confident Figma match, it SHOWS you the screenshot and asks for the node-id.
|
|
221
|
-
|
|
222
|
-
Drives the toolkit design_* tools (+ ios_* / android_* device control).
|
|
214
|
+
Design Check (mock-mode vs Figma, local-only):
|
|
215
|
+
|
|
216
|
+
/multi-agent:design-check Whole-module audit: enumerate every state driver into a countable
|
|
217
|
+
target set, build Debug in a worktree, drive each in mock mode,
|
|
218
|
+
diff against its Figma frame, export HTML+PDF to ~/DesignChecks.
|
|
219
|
+
/multi-agent:design-check <screen> Scope to one screen (e.g. boarding-pass).
|
|
220
|
+
/multi-agent:design-check <screen@var> Scope to one variant (e.g. boarding-pass@expired).
|
|
221
|
+
/multi-agent:design-check <figma-url...> Scope to the frame(s) those URLs name.
|
|
222
|
+
/multi-agent:design-check --resume Audit what the last run for this repo+module left unaudited.
|
|
223
|
+
# COVERAGE GATE: every target is audited or skipped WITH a concrete reason; anything else reports
|
|
224
|
+
# INCOMPLETE with the missing ids. "Needs a scenario/launch-arg" is not a reason, reaching it is the job.
|
|
223
225
|
|
|
224
226
|
------------------------------------------------------------
|
|
225
227
|
|
|
@@ -421,9 +423,17 @@ Setup & Maintenance:
|
|
|
421
423
|
|
|
422
424
|
------------------------------------------------------------
|
|
423
425
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
ai-<stack>-toolkit
|
|
426
|
+
Plugin'ler ve tool'lar (doğrudan çağrılır, sarmalayıcı yok):
|
|
427
|
+
|
|
428
|
+
Stack plugin ai-<stack>-toolkit:<skill>, örn. ai-ios-toolkit:create-component
|
|
429
|
+
Katalog: ai-<stack>-toolkit:help Repo bazında aç: /multi-agent:stack
|
|
430
|
+
common + analyst hep açık; gerisi repo'nun stack'ini izler.
|
|
431
|
+
Toolkit MCP multi-agent-toolkit - ÇALIŞAN uygulama için 80+ tool, pipeline koşusu
|
|
432
|
+
gerektirmez: ios_* / android_* (başlat, tap, screenshot, ui ağacı, log,
|
|
433
|
+
crash, locale, koyu mod), web_*, design_* (yukarıdaki Design Check), ve
|
|
434
|
+
denetimler (ios_app_store_audit, ios_testflight_validate,
|
|
435
|
+
android_apk_audit, *_accessibility_audit). Ekrandaki durumu tahmin etmek
|
|
436
|
+
yerine bunları kullan. Kaydı uninstall'dan sağ çıkar. Kayıtlı değilse sessizce devre dışı.
|
|
427
437
|
|
|
428
438
|
------------------------------------------------------------
|
|
429
439
|
|
|
@@ -477,24 +487,17 @@ Manuel Test (Phase 5 standalone - Xcode hint akışı):
|
|
|
477
487
|
/multi-agent:manual-test Task branch'ine checkout, Xcode/SourceTree hint basar,
|
|
478
488
|
/multi-agent:manual-test #N "ok" / "fix: ..." yanıtını bekler.
|
|
479
489
|
|
|
480
|
-
Design Check (mock-mod vs Figma, yalnızca lokal
|
|
490
|
+
Design Check (mock-mod vs Figma, yalnızca lokal):
|
|
481
491
|
|
|
482
|
-
/multi-agent:design-check Tüm modül
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
Figma frame'iyle karşılaştır (padding/genişlik/spacing/font/renk/
|
|
487
|
-
component/localization), rapor çıkar (HTML+PDF, ~/DesignChecks).
|
|
488
|
-
/multi-agent:design-check <ekran> Tek ekranın hedeflerine kapsa (örn. boarding-pass).
|
|
492
|
+
/multi-agent:design-check Tüm modül denetimi: her state sürücüsünü sayılabilir hedef kümesine
|
|
493
|
+
çıkar, worktree'de Debug derle, her hedefi mock modda sür, Figma
|
|
494
|
+
frame'iyle diff'le, HTML+PDF raporu ~/DesignChecks'e yaz.
|
|
495
|
+
/multi-agent:design-check <ekran> Tek ekrana kapsa (örn. boarding-pass).
|
|
489
496
|
/multi-agent:design-check <ekran@varyant> Tek varyanta kapsa (örn. boarding-pass@expired).
|
|
490
|
-
/multi-agent:design-check <figma-url...>
|
|
491
|
-
/multi-agent:design-check --resume
|
|
492
|
-
# KAPSAM GEÇİDİ: her hedef ya denetlenir ya da SOMUT
|
|
493
|
-
#
|
|
494
|
-
# "Senaryo / prefix / launch-arg gerektirir" gerekçe değildir; o state'lere ulaşmak koşunun işidir.
|
|
495
|
-
# Canlı ekranın Figma karşılığı kesin bulunamazsa SANA screenshot'ı gösterip node-id sorar.
|
|
496
|
-
|
|
497
|
-
multi-agent-toolkit-mcp design_* tool takımını sürer (+ ios_* / android_* cihaz kontrolü).
|
|
497
|
+
/multi-agent:design-check <figma-url...> URL'lerin işaret ettiği frame'lere kapsa.
|
|
498
|
+
/multi-agent:design-check --resume Son koşunun denetlenmemiş kalanını denetle.
|
|
499
|
+
# KAPSAM GEÇİDİ: her hedef ya denetlenir ya da SOMUT gerekçeyle atlanır; başka her durum eksik hedef
|
|
500
|
+
# id'leriyle EKSİK raporlanır. "Senaryo/launch-arg gerektirir" gerekçe değil, oraya ulaşmak koşunun işi.
|
|
498
501
|
|
|
499
502
|
------------------------------------------------------------
|
|
500
503
|
|
|
@@ -75,6 +75,27 @@ grep -rilE "$slugs" $WHITELIST --include="*.swift" --include="*.kt" --include="*
|
|
|
75
75
|
|
|
76
76
|
If `wc -l < $CANDIDATES` exceeds 200, record `fetchWarnings += "feature-name too generic; candidate cap hit"` in `repoEvidence[<repo>]`.
|
|
77
77
|
|
|
78
|
+
**Ranking the candidates (opt-in, `prefs.global.codeGraph.enabled`).** `head -200`
|
|
79
|
+
truncates in grep's walk order, so the cap drops an arbitrary file rather than the
|
|
80
|
+
least relevant one - the failure the warning above names and cannot fix. When a
|
|
81
|
+
graph exists for the repo, rank the set first:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
GRAPH_PATH="$HOME/.claude/knowledge/$(basename "$REPO_ROOT")/code-graph.json"
|
|
85
|
+
node $HOME/.claude/scripts/graph-report.mjs --graph "$GRAPH_PATH" --status \
|
|
86
|
+
|| node $HOME/.claude/scripts/graph-build.mjs --root "$REPO_ROOT" --stack "$STACK" --json
|
|
87
|
+
node $HOME/.claude/scripts/graph-query.mjs "$featureName" --graph "$GRAPH_PATH" --json
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Ranked paths go to the front of `$CANDIDATES`, grep-only hits keep their order
|
|
91
|
+
behind them, everything else is unchanged, and a repo with no graph, an
|
|
92
|
+
unsupported stack or a failing validator gets exactly today's order. It is a sort
|
|
93
|
+
key, never a filter: a file grep found and the graph did not rank is still
|
|
94
|
+
scanned, because a regex extractor's silence is not evidence of absence. Sorting
|
|
95
|
+
rather than detecting is where each tool measured better - the graph beats grep on
|
|
96
|
+
domain words, a feature name is domain words, and it loses on the exact symbols
|
|
97
|
+
the bucket patterns use (`docs/adr/0010-own-code-graph.md`).
|
|
98
|
+
|
|
78
99
|
**13 buckets** (each scanned in parallel with a 30 s timeout per bucket; timeout marks the bucket as `partial`, never blocks the others):
|
|
79
100
|
|
|
80
101
|
| Bucket | Detection (iOS Swift sketch) | Cross-platform variant |
|
|
@@ -29,7 +29,14 @@ report scripts default it from the CWD's basename, and a run happens in a
|
|
|
29
29
|
worktree whose basename need not equal the repo's, so a defaulted path can point
|
|
30
30
|
at a graph that was never built. Pass it.
|
|
31
31
|
|
|
32
|
-
Build
|
|
32
|
+
Build when there is no graph at all, and when `baseCommit` no longer matches
|
|
33
|
+
HEAD. The missing case is the one that matters in practice: `architecture.md`
|
|
34
|
+
and its siblings are written in Phase 7, which is the phase a run is least
|
|
35
|
+
likely to reach, so a repo can have a long history of tasks and an empty
|
|
36
|
+
knowledge directory. The graph must not inherit that. `--status` exits 1 on a
|
|
37
|
+
missing file, and that exit means build, not skip.
|
|
38
|
+
|
|
39
|
+
A build is not usable until
|
|
33
40
|
`validate-code-graph.mjs` exits 0: a graph whose edges point at missing nodes
|
|
34
41
|
truncates traversals silently, so a non-zero exit means skip the injection and
|
|
35
42
|
run Explore as if no graph existed.
|
|
@@ -631,8 +631,12 @@ Phase 0 owns `agent-state.json`. Do not call
|
|
|
631
631
|
|
|
632
632
|
```bash
|
|
633
633
|
node "$HOME/.claude/scripts/phase0-exit-gate.mjs" "$TASK_ID" --input "$ORIGINAL_INPUT"
|
|
634
|
+
node "$HOME/.claude/scripts/usage-report.mjs" --task-id "$TASK_ID" >/dev/null 2>&1 || true
|
|
634
635
|
```
|
|
635
636
|
|
|
637
|
+
The second line reports the run as started: reporting only from Phase 7 reported
|
|
638
|
+
only runs that finish, and few do. Phase 7 upserts the same key over it.
|
|
639
|
+
|
|
636
640
|
It asserts three things, each of which has failed silently in a real run:
|
|
637
641
|
|
|
638
642
|
1. **`agent-state.json` exists.** A run once reported Phase 0 `completed` with only
|
|
@@ -196,7 +196,7 @@ $HOME/.claude/scripts/log-metric.sh "$TASK_ID" 7 task.completed \
|
|
|
196
196
|
duration_ms=$TOTAL_DURATION
|
|
197
197
|
```
|
|
198
198
|
|
|
199
|
-
**Operational reporting.** On by default since v16.8.0; `enabled: false` or `optOut: true` silences it.
|
|
199
|
+
**Operational reporting.** On by default since v16.8.0; `enabled: false` or `optOut: true` silences it. Coarse run metadata only, never prompts, code, diffs, repo names or paths. Fire-and-forget: it no-ops without a token and never blocks the run, so the call is unconditional. Phase 0 already reported this run as started; this upserts the final state over it.
|
|
200
200
|
|
|
201
201
|
```bash
|
|
202
202
|
node $HOME/.claude/scripts/usage-report.mjs --state "$STATE_FILE" >/dev/null 2>&1 || true
|
|
@@ -21,6 +21,6 @@ available. Read the effective `enabledPlugins` and load each enabled toolkit's
|
|
|
21
21
|
`ai-common-toolkit` and `ai-analyst-toolkit` are on everywhere. Nothing enabled
|
|
22
22
|
is a normal state.
|
|
23
23
|
|
|
24
|
-
**multi-agent-toolkit MCP.**
|
|
24
|
+
**multi-agent-toolkit MCP.** 80+ tools for a running app: `ui-inspect`,
|
|
25
25
|
`crash-logs`, `design-check`, `ios-app-store-audit`, `ios-testflight`. Use them
|
|
26
26
|
instead of guessing about on-screen state. Not registered is a silent no-op.
|
|
@@ -37,6 +37,27 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
37
37
|
|
|
38
38
|
export const GRAPH_SCHEMA_VERSION = "1.0.0";
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Stack words the pipeline uses elsewhere, mapped to the rule-file id.
|
|
42
|
+
*
|
|
43
|
+
* The rule files are named after the toolchain (`node`), but the rest of the
|
|
44
|
+
* pipeline names the product surface (`web`), and a caller reasonably types
|
|
45
|
+
* whichever one they were last shown. Aliasing is safe only where the mapping
|
|
46
|
+
* is one-to-one: `backend` is deliberately absent, because it could be node,
|
|
47
|
+
* python or go and picking one would be a guess dressed as support.
|
|
48
|
+
*/
|
|
49
|
+
export const STACK_ALIASES = { web: "node", frontend: "node" };
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Canonical rule-file id for a stack word.
|
|
53
|
+
*
|
|
54
|
+
* @param {string} stack
|
|
55
|
+
* @returns {string}
|
|
56
|
+
*/
|
|
57
|
+
export function resolveStack(stack) {
|
|
58
|
+
return STACK_ALIASES[stack] || stack;
|
|
59
|
+
}
|
|
60
|
+
|
|
40
61
|
/**
|
|
41
62
|
* Load and merge the two rule files for a stack.
|
|
42
63
|
*
|
|
@@ -48,9 +69,15 @@ export const GRAPH_SCHEMA_VERSION = "1.0.0";
|
|
|
48
69
|
* @param {string} [baseDir] - directory holding the two rule dirs
|
|
49
70
|
* @returns {object} merged rules
|
|
50
71
|
*/
|
|
51
|
-
export function loadRules(
|
|
72
|
+
export function loadRules(requested, baseDir = __dirname) {
|
|
73
|
+
const stack = resolveStack(requested);
|
|
52
74
|
const gapPath = join(baseDir, "test-gap-rules", `${stack}.json`);
|
|
53
75
|
const graphPath = join(baseDir, "code-graph-rules", `${stack}.json`);
|
|
76
|
+
if (requested === "backend") {
|
|
77
|
+
throw new Error(
|
|
78
|
+
"'backend' names a role, not a toolchain: pass node, python or go so the right rules load",
|
|
79
|
+
);
|
|
80
|
+
}
|
|
54
81
|
if (!existsSync(gapPath)) throw new Error(`no test-gap rules for stack '${stack}': ${gapPath}`);
|
|
55
82
|
if (!existsSync(graphPath))
|
|
56
83
|
throw new Error(`no code-graph rules for stack '${stack}': ${graphPath}`);
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"stack": "go",
|
|
4
|
+
"description": "Go code-graph rules. Adds what the graph needs on top of test-gap-rules/go.json, which already owns sourceExtensions, excludePathGlobs and the test-path predicates. Definition patterns here are deliberately broader than the test-gap publicApiPatterns: the graph wants unexported declarations too, because Phase 1 narrows scope on the whole package, not just its exported surface.",
|
|
5
|
+
"comments": {
|
|
6
|
+
"line": [
|
|
7
|
+
"//"
|
|
8
|
+
],
|
|
9
|
+
"block": [
|
|
10
|
+
[
|
|
11
|
+
"/*",
|
|
12
|
+
"*/"
|
|
13
|
+
]
|
|
14
|
+
],
|
|
15
|
+
"string": [
|
|
16
|
+
"`",
|
|
17
|
+
"\"",
|
|
18
|
+
"'"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"importSpecifiersAreStrings": true,
|
|
22
|
+
"definitionPatterns": [
|
|
23
|
+
{
|
|
24
|
+
"id": "struct",
|
|
25
|
+
"kind": "struct",
|
|
26
|
+
"regex": "\\btype\\s+([A-Za-z_][A-Za-z0-9_]*)\\s+struct\\b"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "interface",
|
|
30
|
+
"kind": "interface",
|
|
31
|
+
"regex": "\\btype\\s+([A-Za-z_][A-Za-z0-9_]*)\\s+interface\\b"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "type",
|
|
35
|
+
"kind": "type",
|
|
36
|
+
"regex": "\\btype\\s+([A-Za-z_][A-Za-z0-9_]*)\\s+(?!struct\\b|interface\\b)[A-Za-z_*\\[]"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "func",
|
|
40
|
+
"kind": "func",
|
|
41
|
+
"regex": "^func\\s+(?:\\([^)]*\\)\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*[(\\[]"
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"importPatterns": [
|
|
45
|
+
{
|
|
46
|
+
"id": "specifier",
|
|
47
|
+
"regex": "^[ \\t]*(?:import[ \\t]+)?(?:[A-Za-z_.][A-Za-z0-9_]*[ \\t]+)?\"(?:[^\"]*/)?([A-Za-z_][A-Za-z0-9_.-]*)\"[ \\t]*$"
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
"referenceKinds": [
|
|
51
|
+
"struct",
|
|
52
|
+
"interface",
|
|
53
|
+
"type"
|
|
54
|
+
],
|
|
55
|
+
"referenceKindsNote": "func is deliberately absent, for the reason the Swift, Kotlin, Node and Python rules all record: a bare name matched across files is almost never a call to that exact declaration, and Go's short-name convention (New, Run, Get, Close, Error) makes it worse than most. Functions still reach the graph through their defines edge, so they stay findable by name.",
|
|
56
|
+
"ignoredIdentifiers": [
|
|
57
|
+
"package",
|
|
58
|
+
"import",
|
|
59
|
+
"func",
|
|
60
|
+
"type",
|
|
61
|
+
"struct",
|
|
62
|
+
"interface",
|
|
63
|
+
"map",
|
|
64
|
+
"chan",
|
|
65
|
+
"var",
|
|
66
|
+
"const",
|
|
67
|
+
"return",
|
|
68
|
+
"if",
|
|
69
|
+
"else",
|
|
70
|
+
"for",
|
|
71
|
+
"range",
|
|
72
|
+
"switch",
|
|
73
|
+
"case",
|
|
74
|
+
"default",
|
|
75
|
+
"select",
|
|
76
|
+
"go",
|
|
77
|
+
"defer",
|
|
78
|
+
"break",
|
|
79
|
+
"continue",
|
|
80
|
+
"fallthrough",
|
|
81
|
+
"goto",
|
|
82
|
+
"nil",
|
|
83
|
+
"true",
|
|
84
|
+
"false",
|
|
85
|
+
"iota",
|
|
86
|
+
"make",
|
|
87
|
+
"new",
|
|
88
|
+
"len",
|
|
89
|
+
"cap",
|
|
90
|
+
"append",
|
|
91
|
+
"copy",
|
|
92
|
+
"delete",
|
|
93
|
+
"panic",
|
|
94
|
+
"recover",
|
|
95
|
+
"print",
|
|
96
|
+
"println",
|
|
97
|
+
"error",
|
|
98
|
+
"string",
|
|
99
|
+
"bool",
|
|
100
|
+
"byte",
|
|
101
|
+
"rune",
|
|
102
|
+
"int",
|
|
103
|
+
"int8",
|
|
104
|
+
"int16",
|
|
105
|
+
"int32",
|
|
106
|
+
"int64",
|
|
107
|
+
"uint",
|
|
108
|
+
"uint8",
|
|
109
|
+
"uint16",
|
|
110
|
+
"uint32",
|
|
111
|
+
"uint64",
|
|
112
|
+
"float32",
|
|
113
|
+
"float64",
|
|
114
|
+
"complex64",
|
|
115
|
+
"complex128",
|
|
116
|
+
"uintptr",
|
|
117
|
+
"any",
|
|
118
|
+
"comparable",
|
|
119
|
+
"err",
|
|
120
|
+
"ctx",
|
|
121
|
+
"ok"
|
|
122
|
+
],
|
|
123
|
+
"note": "A Go import path is a string literal, so `importSpecifiersAreStrings` applies exactly as it does for JavaScript: the import pass reads a text with comments stripped and strings intact, and the pattern captures the last path segment, which is the package name in the overwhelmingly common case where the two agree. The pattern is anchored to a whole line holding nothing but an optional `import`, an optional alias and the quoted path, because Go has no import keyword on the lines inside a parenthesized block. Without that anchor it matched EVERY double-quoted string in the file and turned each one into a module node. A named import (`sql \"database/sql\"`) and the handful of packages whose name differs from their directory both resolve to the directory name, which is what the engine matches against a file basename anyway. The three type patterns are ordered so `struct` and `interface` claim their shapes before the general `type` alias pattern, which excludes them by lookahead rather than by order alone - order and lookahead together, because a rule file that relies on order silently changes meaning when someone sorts it. `func` is anchored to column 0, which is where every Go function and method declaration lives; the engine's nesting rule then never has to fire for this stack.\n\nUNMEASURED: unlike ios, android, node and python, these rules were never run against a real Go codebase. There was no Go repo and no toolchain on the machine that wrote them. The smoke exercises them on a synthetic fixture, which proves they parse and produce a schema-valid graph, not that they produce a USEFUL one. Treat the first real build as the measurement, and check the hub list before trusting it."
|
|
124
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"stack": "go",
|
|
4
|
+
"description": "Go test gap rules. Sources in *.go; tests are *_test.go beside the code they cover, which is why there is no test directory convention here.",
|
|
5
|
+
"sourceExtensions": [".go"],
|
|
6
|
+
"testPathSuffixes": ["_test.go"],
|
|
7
|
+
"testPathContains": [],
|
|
8
|
+
"excludePathGlobs": ["vendor/", "third_party/", "*.pb.go", "*_generated.go", "*_mock.go"],
|
|
9
|
+
"publicApiPatterns": [
|
|
10
|
+
{
|
|
11
|
+
"id": "exported_func",
|
|
12
|
+
"regex": "^func\\s+([A-Z][A-Za-z0-9_]*)\\s*\\("
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "exported_method",
|
|
16
|
+
"regex": "^func\\s+\\([^)]*\\)\\s+([A-Z][A-Za-z0-9_]*)\\s*\\("
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "exported_type",
|
|
20
|
+
"regex": "^type\\s+([A-Z][A-Za-z0-9_]*)\\s+(?:struct|interface|func|map|\\[|chan|\\*|[A-Za-z])"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"hint": "Cover exported identifiers with a table-driven Test<Name> in the same package's <file>_test.go. An unexported identifier is covered through the exported one that calls it."
|
|
24
|
+
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import { readFileSync } from "node:fs";
|
|
17
17
|
|
|
18
|
-
const ALLOWED_STACK = new Set(["ios", "android", "node", "python"]);
|
|
18
|
+
const ALLOWED_STACK = new Set(["ios", "android", "node", "python", "go"]);
|
|
19
19
|
const ALLOWED_NODE_KIND = new Set(["file", "symbol", "module"]);
|
|
20
20
|
const ALLOWED_EDGE_KIND = new Set(["defines", "imports", "references"]);
|
|
21
21
|
const MAX_REPORTED_DANGLING = 5;
|