@mmerterden/multi-agent-pipeline 20.21.0 → 20.23.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 (69) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/docs/adr/0010-own-code-graph.md +3 -2
  3. package/docs/adr/0017-change-impact-from-the-graph.md +69 -0
  4. package/docs/adr/README.md +1 -0
  5. package/docs/ecosystem.md +6 -5
  6. package/docs/facts.json +5 -4
  7. package/docs/features.md +2 -2
  8. package/install/_usage-lifecycle.mjs +44 -0
  9. package/install/catalog-history.json +1 -1
  10. package/install/index.mjs +2 -0
  11. package/install/templates/copilot-instructions.md +1 -0
  12. package/manifest.json +72 -65
  13. package/package.json +1 -1
  14. package/pipeline/commands/multi-agent/create-jira/SKILL.md +1 -1
  15. package/pipeline/commands/multi-agent/review/SKILL.md +2 -1
  16. package/pipeline/commands/multi-agent/setup/SKILL.md +9 -18
  17. package/pipeline/commands/multi-agent/update/SKILL.md +3 -3
  18. package/pipeline/lib/json-file-lock.mjs +24 -2
  19. package/pipeline/multi-agent-refs/_dev-context.md +15 -0
  20. package/pipeline/multi-agent-refs/analysis/evidence.md +6 -4
  21. package/pipeline/multi-agent-refs/analysis/locked.md +1 -1
  22. package/pipeline/multi-agent-refs/cross-cli-contract.md +2 -0
  23. package/pipeline/multi-agent-refs/features/analysis-jira.md +5 -0
  24. package/pipeline/multi-agent-refs/features/code-graph.md +66 -2
  25. package/pipeline/multi-agent-refs/features/product-context.md +238 -0
  26. package/pipeline/multi-agent-refs/features/usage-reporting.md +40 -23
  27. package/pipeline/multi-agent-refs/generate-issue.md +11 -1
  28. package/pipeline/multi-agent-refs/knowledge.md +20 -0
  29. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +9 -14
  30. package/pipeline/multi-agent-refs/phases/phase-2-dev.md +1 -1
  31. package/pipeline/multi-agent-refs/phases/phase-3-review.md +7 -6
  32. package/pipeline/multi-agent-refs/platform-parity.md +23 -9
  33. package/pipeline/multi-agent-refs/setup/token-save-flow.md +24 -0
  34. package/pipeline/schemas/agent-state.schema.json +31 -0
  35. package/pipeline/schemas/analysis-spec.schema.json +14 -0
  36. package/pipeline/schemas/code-graph.schema.json +13 -2
  37. package/pipeline/schemas/diff-risk.schema.json +2 -1
  38. package/pipeline/schemas/prefs.schema.json +84 -4
  39. package/pipeline/scripts/_code-graph.mjs +188 -6
  40. package/pipeline/scripts/_diff-impact.mjs +319 -0
  41. package/pipeline/scripts/_supertypes.mjs +133 -0
  42. package/pipeline/scripts/analysis-story-body.mjs +24 -8
  43. package/pipeline/scripts/capture-flush.sh +62 -4
  44. package/pipeline/scripts/code-graph-rules/android.json +1 -0
  45. package/pipeline/scripts/code-graph-rules/ios.json +5 -0
  46. package/pipeline/scripts/code-graph-rules/node.json +1 -0
  47. package/pipeline/scripts/code-graph-rules/python.json +1 -0
  48. package/pipeline/scripts/diff-risk-score.mjs +80 -9
  49. package/pipeline/scripts/git-path.mjs +21 -0
  50. package/pipeline/scripts/graph-affected.mjs +168 -36
  51. package/pipeline/scripts/graph-confirm.mjs +140 -0
  52. package/pipeline/scripts/graph-query.mjs +16 -4
  53. package/pipeline/scripts/learnings-ledger.mjs +3 -2
  54. package/pipeline/scripts/product-context.mjs +550 -0
  55. package/pipeline/scripts/smoke-cross-cli-behavior.sh +39 -1
  56. package/pipeline/scripts/symbol-existence-gate.mjs +21 -9
  57. package/pipeline/scripts/test-gap-scan.mjs +4 -1
  58. package/pipeline/scripts/test-strength.mjs +7 -8
  59. package/pipeline/scripts/usage-identity.mjs +1 -1
  60. package/pipeline/scripts/usage-register.mjs +181 -15
  61. package/pipeline/scripts/validate-code-graph.mjs +3 -2
  62. package/pipeline/scripts/validate-diff-risk.mjs +1 -0
  63. package/pipeline/scripts/valven-gate.mjs +9 -1
  64. package/pipeline/scripts/valven-port.mjs +16 -5
  65. package/pipeline/scripts/valven-split.mjs +6 -4
  66. package/pipeline/skills/.skill-manifest.json +4 -4
  67. package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +5 -1
  68. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +11 -9
  69. package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +3 -3
@@ -134,9 +134,11 @@ Phase 0 Step 1b catalogued every typed external link from the task description i
134
134
  → context injection: total=<N>, fetched=<n>, pending=<n>, by-type={swagger:F/P, confluence:F/P, ...}
135
135
  ```
136
136
 
137
- #### Step 2 - Stack Detection
137
+ #### Step 1.6 - Product context (advisory, read-only)
138
+
139
+ `product-context.mjs refs` names the product's other repos (not the ones developed here); exit 2 = skip. Read them at their latest branch, pinned to a sha, via `repo_*`, and record each join key as `implemented`, `not-yet-implemented` or `diverges` in `state.productContext`. Never blocking. Steps: `$HOME/.claude/multi-agent-refs/features/product-context.md`.
138
140
 
139
- Two questions shared one answer here, and the shared answer was wrong on both.
141
+ #### Step 2 - Stack Detection
140
142
 
141
143
  **What the repo is BUILT WITH** - one owner, no marker list in this file:
142
144
 
@@ -146,22 +148,15 @@ bash $HOME/.claude/lib/stack-detect.sh --json "$PROJECT_ROOT"
146
148
 
147
149
  Persist `.stacks` as `state.stacks[]` (a subset of `ios android web backend`,
148
150
  always in that order) and `.why` as `state.stackWhy`. Empty is a real
149
- answer meaning no marker matched; `stackWhy` separates that from a directory that
150
- could not be read, and a caller that cannot tell those apart treats an unreadable
151
- repo as a language-free one. `state.stacks[]` routes toolkits, through
151
+ answer (no marker matched); `stackWhy` separates it from an unreadable directory,
152
+ which must never read as a language-free repo. `state.stacks[]` routes toolkits, through
152
153
  `pluginsForStacks()` in `$HOME/.claude/scripts/_stack-routing.mjs`. Do not derive
153
154
  plugin names here.
154
155
 
155
- `AndroidManifest.xml` sits at `<module>/src/main/`, depth 4 in a multi-module app,
156
- so the owner goes to depth 5 for that one file. It prunes submodules (a vendored
157
- checkout ships its own `Package.swift`) and checks the root first so traversal
158
- order cannot decide.
159
-
160
156
  **What the code is WRITTEN IN** - a different axis with its own field.
161
157
  `state.detectedStack[]` stays the language answer (`ios`, `python`, `node`, `go`,
162
158
  `docker`, `monorepo`): `graph-build.mjs --stack` takes `ios|android|node|python|go`
163
- and has no notion of `web` or `backend`. Folding the two together is what made a
164
- Gradle-built JVM service read as an Android app.
159
+ and has no notion of `web` or `backend`, so the two never fold together.
165
160
 
166
161
  This informs:
167
162
 
@@ -173,9 +168,9 @@ This informs:
173
168
 
174
169
  Gated by `prefs.global.repoMap.enabled` (default: `false`). When enabled, runs `$HOME/.claude/scripts/repo-map.mjs` and injects the budgeted result into each Explore prompt as `${REPO_MAP}`. Aider-style: deterministic, no embeddings, sub-second, advisory only. Full wiring (helper invocation, properties, when-to-enable): `$HOME/.claude/multi-agent-refs/features/repo-map.md`.
175
170
 
176
- #### Step 2.6 - Code Graph Injection (advisory, opt-in)
171
+ #### Step 2.6 - Code Graph Injection (advisory)
177
172
 
178
- Gated by `prefs.global.codeGraph.enabled` (default: `false`). With a rule file for `detectedStack`, Phase 1 refreshes the graph and queries it; `graph-affected` feeds `analysis.touchedAreas[]`. Zero API cost, read-only. Commands and measurements: `$HOME/.claude/multi-agent-refs/features/code-graph.md`.
173
+ Gated by `prefs.global.codeGraph.enabled` (default: `true`). With a rule file for `detectedStack`, Phase 1 refreshes the graph and queries it; `graph-affected` feeds `analysis.touchedAreas[]`. Zero API cost, read-only. Commands and measurements: `$HOME/.claude/multi-agent-refs/features/code-graph.md`.
179
174
 
180
175
  **A valid result REPLACES the opening sweep** rather than sitting beside it: Explore starts from those files and walks outward, with no broad `Glob`/`Grep` pass first - running both pays twice, and the second re-derives what the graph said. No graph (missing rule, stale, or disabled) leaves the previous behaviour untouched; a task naming a symbol or path is still grepped directly.
181
176
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## Phase 2 Pre-flight (BLOCKING, v9.0.0)
6
6
 
7
- Per Locked decision 30, Phase 2 Dev consumes the analysis document as the sole design source. **Figma** MCP / REST / URL fetches are forbidden; the toolkit MCP is not, and Steps 3.4 and 3.55 call it.
7
+ Per Locked decision 30, Phase 2 Dev consumes the analysis document as the sole design source. **Figma** MCP / REST / URL fetches are forbidden; the toolkit MCP is not (Steps 3.4, 3.55; `repo_read` checks `state.productContext` at its sha, copying nothing).
8
8
 
9
9
  Pre-flight steps (run in order, abort on failure).
10
10
 
@@ -55,9 +55,9 @@ Cost ledger: `phase-4.repo_map_emitted bytes=N budget=B cache_hit=true|false` -
55
55
 
56
56
  #### Step 1.8 - Platform parity cross-check (advisory, read-only)
57
57
 
58
- Runs when a counterpart app repo resolves AND the diff touches a screen,
59
- service, request model or localization file. Resolution is automatic and
60
- mirrored (ios ↔ android); the order and the learned pref live in the contract.
58
+ Runs when a counterpart resolves AND the diff touches a screen, service,
59
+ request model or localization file. Counterparts: `state.productContext`
60
+ first, else mirrored (ios ↔ android); order and pref live in the contract.
61
61
  No counterpart, or an unattended run facing several, skips silently - no
62
62
  section, no placeholder, run unaffected.
63
63
 
@@ -66,13 +66,14 @@ counterpart repo is **read only**, and parity findings are **never blocking**.
66
66
 
67
67
  #### Step 1.75 - Diff Risk Scoring (advisory)
68
68
 
69
- Before dispatching reviewers, run the deterministic diff risk scorer and inject the top-N files into each reviewer's prompt as a priority hint. **Advisory only - never gates the pipeline.** Heuristic, zero LLM, runs in well under a second.
69
+ Before dispatching reviewers, run the deterministic diff risk scorer and inject the top-N files into each reviewer's prompt as a priority hint. **Advisory only - never gates the pipeline.** Zero LLM, sub-second.
70
70
 
71
- Score the diff **once, in full** (no `--top`): Steps 1.76/1.77 need every scored file (a shrinking test file ranked 20th; whether ANY file is high-stakes). The top-N hint is derived from that report, not a second git walk.
71
+ Score the diff **once, in full** (no `--top`); Steps 1.76/1.77 need every file, the top-N hint derives from it, and `--graph` adds `dependents_reached`.
72
72
 
73
73
  ```bash
74
+ GRAPH_PATH="$HOME/.claude/knowledge/${PROJECT_ROOT##*/}/code-graph.json"
74
75
  RISK_FULL=$(node $HOME/.claude/scripts/diff-risk-score.mjs \
75
- --base "$BASE_BRANCH" --head HEAD \
76
+ --base "$BASE_BRANCH" --head HEAD --graph "$GRAPH_PATH" \
76
77
  --task-id "$TASK_ID" 2>/dev/null)
77
78
  echo "$RISK_FULL" | node $HOME/.claude/scripts/validate-diff-risk.mjs - >/dev/null 2>&1 || RISK_FULL=""
78
79
 
@@ -14,13 +14,13 @@ description: "Internal - cross-platform parity cross-check for Phase 4 and /mu
14
14
  - [Severity](#severity)
15
15
  <!-- /toc -->
16
16
 
17
- A feature that exists on iOS and Android is written twice, and the two copies
18
- drift. The drift is invisible from inside one repo: the iOS diff is
17
+ A feature that exists on several platforms is written more than once, and the
18
+ copies drift. The drift is invisible from inside one repo: the iOS diff is
19
19
  self-consistent, the tests pass, and nobody notices that the Android screen
20
20
  calls a different endpoint, sends one parameter fewer, or reads a localization
21
21
  key that no longer exists on the other side.
22
22
 
23
- This step reads the counterpart repo and reports what differs. It is advisory:
23
+ This step reads the counterpart repositories and reports what differs. It is advisory:
24
24
  it never blocks, never edits, and its silence is never evidence.
25
25
 
26
26
  ## When it runs
@@ -29,6 +29,17 @@ The counterpart is **resolved automatically**. Nothing is asked in the normal
29
29
  case, because the answer is the same on every run of the same project and a
30
30
  question asked every time is a question people learn to dismiss.
31
31
 
32
+ **Source 0, when present, replaces the rest.** If Phase 1 recorded
33
+ `state.productContext[]` (`features/product-context.md`), every entry without
34
+ an `error` is a counterpart: the product's other app, web and backend
35
+ repositories, read at the sha Phase 1 pinned, through `repo_grep` /
36
+ `repo_read` / `repo_find_keys`. No local checkout is needed and none is read,
37
+ so the comparison is against the latest integration branch rather than
38
+ whatever a sibling directory has checked out. A `stale` entry is still
39
+ compared, and the section header says it is stale. Resources entries are
40
+ compared on axis 4 only.
41
+
42
+ Without product context, the mirrored-platform resolution below applies.
32
43
  Resolution order, first hit wins. `<slug>` is the primary project's prefs key,
33
44
  and `MIRROR` is `android` when the run's stack is `ios`, `ios` when it is
34
45
  `android` - the check runs in both directions and neither platform is the
@@ -38,7 +49,7 @@ default one.
38
49
  |---|---|---|
39
50
  | 1 | `--with <path\|owner/repo>` (`/multi-agent:review` only) | An explicit override for a one-off. Remembered like any other resolution. |
40
51
  | 2 | `prefs.projects[<slug>].counterpartRoots[]` | What a previous run learned. Verified before use: the path must still exist and its markers must still resolve to `MIRROR`, otherwise the entry is dropped and detection continues at 3. A remembered answer that has gone stale is worse than none. |
41
- | 3 | Sibling checkouts of the primary repo | List the primary checkout's **parent directory**, depth 1, keep the git repos, resolve each one's stack from its marker files. This matches how the repos are actually laid out - a product's iOS and Android checkouts sit next to each other - and it is one directory listing, not a scan of `$HOME`. |
52
+ | 3 | Sibling checkouts of the primary repo | List the primary checkout's **parent directory**, depth 1, keep the git repos, resolve each one's stack from its marker files, and keep only those whose name core matches the primary's (`product-context.mjs suggest`: platform and version words dropped, so `acme-shop-ios-new` keeps `acme-shop-android` and drops an unrelated repo that merely has Android markers). For a run inside a worktree, the parent is that of the primary repository's main checkout, not of the worktree. One directory listing, not a scan of `$HOME`. |
42
53
  | 4 | `state.siblings[]` | Anything the Phase 0 dev-context picker recorded, in case the counterpart is a submodule or a repo the user selected by hand. |
43
54
 
44
55
  Then:
@@ -64,9 +75,9 @@ The second condition is the diff: it has to touch something with a counterpart
64
75
  worth checking - a screen, a service/repository/use-case, a request model, or a
65
76
  localization file. Otherwise the step is skipped even when a counterpart exists.
66
77
 
67
- Nothing is ever cloned. A counterpart that is not checked out locally is
68
- skipped: cloning a repository in order to review a different one is a side
69
- effect nobody asked for.
78
+ Nothing is cloned into a workspace. Product-context counterparts live in the
79
+ `repo_*` tools' read-only cache, which has no working tree; a fallback
80
+ counterpart that is not checked out locally is skipped.
70
81
 
71
82
  ## Read-only, without exception
72
83
 
@@ -80,8 +91,11 @@ for a human, not a task for the pipeline.
80
91
 
81
92
  ## Locating the counterpart, deterministically
82
93
 
83
- Do not grep the other repo by hand and do not read it broadly. Use the code
84
- graph, which is LLM-free and token-budgeted:
94
+ Do not read the other repo broadly. For a product-context counterpart, look the
95
+ diff's identifiers up with one `repo_find_keys` call at the recorded sha (paths,
96
+ endpoint strings, request type names, localization keys), then `repo_read` the
97
+ hit files. For a local fallback counterpart, use the code graph, which is
98
+ LLM-free and token-budgeted:
85
99
 
86
100
  ```bash
87
101
  SIB_STACK=android # or ios, mirrored
@@ -1,5 +1,11 @@
1
1
  ## Token Save Flow (reusable)
2
2
 
3
+ <!-- toc -->
4
+ - [Token Save Flow (reusable)](#token-save-flow-reusable)
5
+ - [Step 3.5 - Host Prompt (embedded in Token Save Flow)](#step-35---host-prompt-embedded-in-token-save-flow)
6
+ - [Step 3.7 - Repository read binding (embedded in Token Save Flow)](#step-37---repository-read-binding-embedded-in-token-save-flow)
7
+ <!-- /toc -->
8
+
3
9
  This flow is used in TWO contexts:
4
10
  - **Setup Step 4** - for missing tokens during first-time onboarding
5
11
  - **Phase 0 token pre-check** - when a token is missing or expired (401/403) at runtime
@@ -199,3 +205,21 @@ Test host → `hosts.graylogTest` (`{GRAYLOG_TEST_HOST}`); skipped means product
199
205
  **Re-run / update**: `/multi-agent:setup hosts` re-opens the prompt to edit values without touching tokens.
200
206
 
201
207
  Team-sharing note: Hosts are the only corporate metadata the pipeline needs. Tokens live in macOS Keychain (encrypted, never on disk inside the repo). Preferences file lives in `~/.claude/` - fully outside the repo clone. A teammate cloning the repo runs `/multi-agent:setup` once; host + token prompts run naturally together.
208
+
209
+ ## Step 3.7 - Repository read binding (embedded in Token Save Flow)
210
+
211
+ Runs inline when the saved token can read git repositories over https (a
212
+ GitHub PAT, a Bitbucket Data Center HTTP access token). The multi-agent-toolkit
213
+ `repo_*` tools send a token only to a host bound to it here, and nothing else
214
+ in the pipeline writes this binding: a product map or a planner's request
215
+ naming a credential never creates one.
216
+
217
+ ```
218
+ Repository hosts this token may be sent to for read-only clone and fetch
219
+ (comma-separated, empty for none). Suggested: {github.com | prefs.global.hosts.bitbucket}
220
+ ___
221
+ ```
222
+
223
+ Save the answer, lowercased, to `prefs.global.credentialHosts.{logical-key}`
224
+ (an array of hostnames). Empty leaves the key unbound. `/multi-agent:setup hosts`
225
+ re-opens it.
@@ -551,6 +551,37 @@
551
551
  }
552
552
  }
553
553
  },
554
+ "productContext": {
555
+ "type": "array",
556
+ "maxItems": 20,
557
+ "description": "What Phase 1 read from the other repositories of the product (every product repo except the ones this run develops), each at its pinned sha through the toolkit's repo_* tools. Written by scripts/product-context.mjs record; read by Phase 1 planning, Phase 2 dev and Phase 3 platform parity. See multi-agent-refs/features/product-context.md.",
558
+ "items": {
559
+ "type": "object",
560
+ "required": ["remote", "slug"],
561
+ "properties": {
562
+ "remote": { "type": "string" },
563
+ "slug": { "type": "string" },
564
+ "stack": { "type": "string" },
565
+ "branch": { "type": "string" },
566
+ "sha": { "type": "string" },
567
+ "stale": { "type": "boolean" },
568
+ "error": { "type": "string" },
569
+ "keys": {
570
+ "type": "array",
571
+ "items": {
572
+ "type": "object",
573
+ "required": ["key", "status"],
574
+ "properties": {
575
+ "key": { "type": "string" },
576
+ "status": { "enum": ["implemented", "not-yet-implemented", "diverges", "error"] },
577
+ "hits": { "type": "array" },
578
+ "note": { "type": "string" }
579
+ }
580
+ }
581
+ }
582
+ }
583
+ }
584
+ },
554
585
  "siblings": {
555
586
  "type": "array",
556
587
  "maxItems": 10,
@@ -610,6 +610,20 @@
610
610
  }
611
611
  }
612
612
  },
613
+ "repoShas": {
614
+ "type": "array",
615
+ "description": "Every repo read through the toolkit's repo_* tools at its latest integration branch, with the sha it was read at. Sorted shas are an evidence_digest input, so a new commit invalidates the cache.",
616
+ "items": {
617
+ "type": "object",
618
+ "required": ["remote", "branch", "sha"],
619
+ "properties": {
620
+ "remote": { "type": "string" },
621
+ "branch": { "type": "string" },
622
+ "sha": { "type": "string" },
623
+ "stale": { "type": "boolean" }
624
+ }
625
+ }
626
+ },
613
627
  "repoEvidence": {
614
628
  "type": "object",
615
629
  "description": "Phase 1b deep-scan result keyed by repo name. Each value holds the 13-bucket catalogue.",
@@ -8,7 +8,8 @@
8
8
  "additionalProperties": false,
9
9
  "properties": {
10
10
  "schemaVersion": {
11
- "const": "1.0.0"
11
+ "enum": ["1.0.0", "1.1.0", "1.2.0"],
12
+ "description": "1.1.0 adds endLine on symbol nodes; 1.2.0 adds inherits edges (subtype to supertype). Older graphs stay readable; diff mapping falls back to file level for a 1.0.0 graph."
12
13
  },
13
14
  "stack": {
14
15
  "type": "string",
@@ -51,6 +52,16 @@
51
52
  "name": { "type": "string" },
52
53
  "path": { "type": ["string", "null"] },
53
54
  "line": { "type": "integer", "minimum": 1 },
55
+ "startLine": {
56
+ "type": "integer",
57
+ "minimum": 1,
58
+ "description": "First line of the declaration when attribute or decorator lines sit directly above it; absent when it equals line."
59
+ },
60
+ "endLine": {
61
+ "type": "integer",
62
+ "minimum": 1,
63
+ "description": "Last line indented deeper than the declaration, plus a closing bracket at its own indentation when one follows. Absent in graphs built before schema 1.1.0."
64
+ },
54
65
  "symbolKind": { "type": "string" },
55
66
  "nested": {
56
67
  "type": "boolean",
@@ -70,7 +81,7 @@
70
81
  "properties": {
71
82
  "from": { "type": "string" },
72
83
  "to": { "type": "string" },
73
- "kind": { "type": "string", "enum": ["defines", "imports", "references"] }
84
+ "kind": { "type": "string", "enum": ["defines", "imports", "references", "inherits"] }
74
85
  }
75
86
  }
76
87
  },
@@ -64,7 +64,8 @@
64
64
  "complexity_delta",
65
65
  "ui_critical",
66
66
  "migration",
67
- "test_lines_removed"
67
+ "test_lines_removed",
68
+ "dependents_reached"
68
69
  ]
69
70
  },
70
71
  "weight": { "type": "number" },
@@ -1273,9 +1273,14 @@
1273
1273
  "type": "string",
1274
1274
  "description": "Shared ingest token the receiving endpoint validates. May be left empty here and supplied via env MULTI_AGENT_USAGE_TOKEN instead. Local prefs only, never synced."
1275
1275
  },
1276
+ "declaredLogin": {
1277
+ "type": "string",
1278
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,38}$",
1279
+ "description": "The GitHub username the person typed at setup or update (usage-register.mjs --login). Registered as declared when this machine cannot prove that account; the server issues a token only when it is a collaborator (the repository owner registers with a proof). Forgotten when the server refuses it. Local prefs only, never synced."
1280
+ },
1276
1281
  "reportAs": {
1277
1282
  "type": "string",
1278
- "description": "Preferred GitHub login when more than one account is logged in to gh. It is checked first, and used only if that account can read accessRepo; otherwise the next logged-in account with access is used. Local prefs only, never synced."
1283
+ "description": "Preferred GitHub login when more than one account is logged in to gh. usage-register.mjs --login sets it to the declared name, and removes it with that name when the server refuses the name. It is checked first, and used only if that account can read accessRepo; otherwise the next logged-in account with access is used. Local prefs only, never synced."
1279
1284
  },
1280
1285
  "accessRepo": {
1281
1286
  "type": "string",
@@ -1320,6 +1325,23 @@
1320
1325
  "type": "string",
1321
1326
  "description": "ISO time of the registration that issued the stored token."
1322
1327
  },
1328
+ "claimNonce": {
1329
+ "type": "string",
1330
+ "pattern": "^[0-9a-f]{8,32}$",
1331
+ "description": "Random suffix of the machine label a declared registration is sent with, so nobody else can name the label and replace this machine's token. Written once by usage-register.mjs."
1332
+ },
1333
+ "claimFailedAt": {
1334
+ "type": "string",
1335
+ "description": "ISO time a declared registration last failed for a reason other than a refusal of the name (rate limit, cap, endpoint down). The claim is not sent again for a day unless the name is typed again."
1336
+ },
1337
+ "reportAsBefore": {
1338
+ "type": "string",
1339
+ "description": "The reportAs a declared name replaced, put back when that name is refused."
1340
+ },
1341
+ "registeredClaim": {
1342
+ "type": "boolean",
1343
+ "description": "true when the stored token was issued for a declared login rather than a proven one. A proof found later, for any login with access, replaces it; a declared name never replaces a proven registration."
1344
+ },
1323
1345
  "registeredFallback": {
1324
1346
  "type": "boolean",
1325
1347
  "description": "Deprecated (20.17.0). true marked a registration made with an account that cannot read accessRepo; it is not treated as pinned and is replaced once an account with access is found."
@@ -1463,15 +1485,69 @@
1463
1485
  }
1464
1486
  }
1465
1487
  },
1488
+ "credentialHosts": {
1489
+ "type": "object",
1490
+ "description": "Hosts each credential-store key may be sent to by the multi-agent-toolkit repo_* tools for read-only clone and fetch over https. Written by /multi-agent:setup (Token Save Flow Step 3.7) with the person present; product-context.mjs learn refuses a repository credential whose host is not listed here, and the toolkit refuses to send a token anywhere else.",
1491
+ "additionalProperties": {
1492
+ "type": "array",
1493
+ "items": { "type": "string", "pattern": "^[a-z0-9.-]+$" },
1494
+ "maxItems": 20
1495
+ }
1496
+ },
1497
+ "products": {
1498
+ "type": "object",
1499
+ "description": "Products and the repositories each is made of, for the planning step that reads every repository of the product except the ones a run develops (scripts/product-context.mjs, toolkit repo_* tools). Keyed by product name; a project joins one through projects[<key>].product, or implicitly when one of its developed remotes is listed here. Written by product-context.mjs learn after the user confirms; read-only to everything else.",
1500
+ "propertyNames": { "pattern": "^[A-Za-z0-9._-]{1,64}$" },
1501
+ "additionalProperties": {
1502
+ "type": "object",
1503
+ "additionalProperties": false,
1504
+ "required": ["repos"],
1505
+ "properties": {
1506
+ "repos": {
1507
+ "type": "array",
1508
+ "minItems": 1,
1509
+ "maxItems": 20,
1510
+ "items": {
1511
+ "type": "object",
1512
+ "additionalProperties": false,
1513
+ "required": ["stack", "remote"],
1514
+ "properties": {
1515
+ "stack": { "enum": ["ios", "android", "web", "backend", "resources"] },
1516
+ "remote": {
1517
+ "type": "string",
1518
+ "description": "https://, ssh:// or user@host:path remote, without credentials."
1519
+ },
1520
+ "branch": {
1521
+ "type": "string",
1522
+ "description": "Override for the branch rule (develop when the remote has it, else its default branch)."
1523
+ },
1524
+ "credential": {
1525
+ "type": "string",
1526
+ "description": "Logical credential-store key of a read token for this host, accepted only when global.credentialHosts already binds the key to this https host. Omit to use git's own credential helper or SSH agent."
1527
+ },
1528
+ "authScheme": {
1529
+ "enum": ["basic", "bearer"],
1530
+ "description": "basic for GitHub, bearer for Bitbucket Data Center HTTP access tokens."
1531
+ },
1532
+ "username": {
1533
+ "type": "string",
1534
+ "description": "Username for basic auth. Default x-access-token."
1535
+ }
1536
+ }
1537
+ }
1538
+ }
1539
+ }
1540
+ }
1541
+ },
1466
1542
  "codeGraph": {
1467
1543
  "type": "object",
1468
1544
  "additionalProperties": false,
1469
- "description": "v16.1+ - deterministic, LLM-free code graph (symbols, imports, references) built by pipeline/scripts/graph-build.mjs into ~/.claude/knowledge/<project>/code-graph.json. Phase 1 Step 2.6 queries it to narrow the Explore starting set; Phase 5 Step 3 refreshes it after the branch changed code. Zero API cost, zero runtime dependencies (ADR-0004), read-only on the repo. Off by default: with it off the pipeline behaves exactly as it did before. Measured on a 4,300-file Swift app at a fixed 30k retrieval budget, it roughly doubled coverage at under half the token cost on domain-word searches, and was marginally worse than plain grep when the task already names an exact symbol.",
1545
+ "description": "v16.1+ - deterministic, LLM-free code graph (symbols, imports, references) built by pipeline/scripts/graph-build.mjs into ~/.claude/knowledge/<project>/code-graph.json. Phase 1 Step 2.6 queries it to narrow the Explore starting set; Phase 5 Step 3 refreshes it after the branch changed code. Zero API cost, zero runtime dependencies (ADR-0004), read-only on the repo. On by default; with it off no graph is built or refreshed. Measured on a 4,300-file Swift app at a fixed 30k retrieval budget, it roughly doubled coverage at under half the token cost on domain-word searches, and was marginally worse than plain grep when the task already names an exact symbol.",
1470
1546
  "properties": {
1471
1547
  "enabled": {
1472
1548
  "type": "boolean",
1473
- "default": false,
1474
- "description": "Master switch. When false, Phase 1 Step 2.6 and the Phase 5 refresh are skipped entirely and no graph file is written."
1549
+ "default": true,
1550
+ "description": "Master switch, on by default. When false, Phase 1 Step 2.6 and the Phase 5 refresh are skipped and no graph is built or refreshed; a graph file already on disk is still read by Phase 3 and the analysis ranking."
1475
1551
  },
1476
1552
  "autoRefresh": {
1477
1553
  "type": "boolean",
@@ -2338,6 +2414,10 @@
2338
2414
  }
2339
2415
  }
2340
2416
  },
2417
+ "product": {
2418
+ "type": "string",
2419
+ "description": "Name of the global.products entry this project belongs to. Planning reads every repository of that product except the ones the run develops. Written by product-context.mjs learn."
2420
+ },
2341
2421
  "counterpartRoots": {
2342
2422
  "type": "array",
2343
2423
  "items": {