@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 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.13.0",
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
- Stack plugin skills: call directly as ai-<stack>-toolkit:<skill>. Catalog:
151
- ai-<stack>-toolkit:help. Active plugins per repo: /multi-agent:stack.
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 - powered by the toolkit MCP design_* tools):
206
-
207
- /multi-agent:design-check Whole-module design audit: pick repo+module, gate on mock support,
208
- enumerate every state driver (launch args, scenario cases, scenario codes,
209
- fixtures, deep links) into a countable target set, build Debug in a worktree,
210
- drive each target in mock mode via idb, compare each vs its Figma frame
211
- (padding/width/spacing/font/color/component/localization), and export a
212
- report (HTML+PDF, ~/DesignChecks).
213
- /multi-agent:design-check <screen> Scope to one screen's targets (e.g. boarding-pass).
214
- /multi-agent:design-check <screen@var> Scope to a single variant (e.g. boarding-pass@expired).
215
- /multi-agent:design-check <figma-url...> Scope to the frame(s) those URLs name.
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
- Stack plugin skill'leri: doğrudan ai-<stack>-toolkit:<skill> olarak çağrılır
425
- (örn. ai-ios-toolkit:create-component). Lokal kopya ve sarmalayıcı yok. Katalog:
426
- ai-<stack>-toolkit:help. Aktif plugin'ler repo bazında: /multi-agent:stack.
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 - multi-agent-toolkit-mcp design_* tool'larıyla):
490
+ Design Check (mock-mod vs Figma, yalnızca lokal):
481
491
 
482
- /multi-agent:design-check Tüm modül tasarım denetimi: repo+modül seç, mock desteğini geçitle,
483
- her state sürücüsünü (launch arg, senaryo case'leri, senaryo kodları,
484
- fixture'lar, deep link'ler) sayılabilir bir hedef kümesine çıkar,
485
- worktree'de Debug derle, her hedefi mock modda idb ile sür, her birini
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...> URL'lerin işaret ettiği frame'lere kapsa.
491
- /multi-agent:design-check --resume Bu repo+modül için son koşunun denetlenmemiş kalanını denetle.
492
- # KAPSAM GEÇİDİ: her hedef ya denetlenir ya da SOMUT bir gerekçeyle atlanır. Başka her durum geçidi
493
- # düşürür ve koşu eksik hedef id'leriyle EKSİK raporlanır - kısmi denetim asla temiz görünmez.
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 only when `baseCommit` no longer matches HEAD. A build is not usable until
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. Sends command, duration, tokens, outcome - never a repo name or path. One POST per run, fire-and-forget, coarse run metadata only - no prompts, code, diffs, or absolute paths. The script no-ops when `usageLog.enabled` is not true or no token resolves, so the call is unconditional and never blocks the run.
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.** 83 tools for a running app: `ui-inspect`,
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.
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "stack": {
14
14
  "type": "string",
15
- "enum": ["ios", "android", "node", "python"]
15
+ "enum": ["ios", "android", "node", "python", "go"]
16
16
  },
17
17
  "root": {
18
18
  "type": "string",
@@ -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(stack, baseDir = __dirname) {
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;