@mmerterden/multi-agent-pipeline 19.0.0 → 19.1.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 (60) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +3 -3
  3. package/docs/ecosystem.md +12 -4
  4. package/docs/facts.json +20 -4
  5. package/docs/features.md +1 -1
  6. package/docs/recovery-guide.md +8 -8
  7. package/manifest.json +63 -61
  8. package/package.json +1 -1
  9. package/pipeline/agents/dev-critic.md +4 -4
  10. package/pipeline/commands/multi-agent/SKILL.md +1 -1
  11. package/pipeline/commands/multi-agent/analysis/SKILL.md +6 -6
  12. package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +2 -2
  13. package/pipeline/commands/multi-agent/resume-local/SKILL.md +1 -1
  14. package/pipeline/commands/multi-agent/review/SKILL.md +1 -1
  15. package/pipeline/commands/multi-agent/review-analysis/SKILL.md +1 -1
  16. package/pipeline/lib/model-dispatch.sh +140 -0
  17. package/pipeline/lib/outbound-gate.mjs +14 -0
  18. package/pipeline/multi-agent-refs/_dev-context.md +5 -5
  19. package/pipeline/multi-agent-refs/analysis/evidence.md +2 -2
  20. package/pipeline/multi-agent-refs/analysis/intake.md +6 -6
  21. package/pipeline/multi-agent-refs/analysis/locked.md +27 -0
  22. package/pipeline/multi-agent-refs/analysis/redesign.md +1 -1
  23. package/pipeline/multi-agent-refs/analysis/render.md +9 -9
  24. package/pipeline/multi-agent-refs/analysis/resolve.md +1 -1
  25. package/pipeline/multi-agent-refs/analysis/review.md +2 -2
  26. package/pipeline/multi-agent-refs/analysis/synthesis.md +2 -2
  27. package/pipeline/multi-agent-refs/analysis-template-corporate.md +9 -9
  28. package/pipeline/multi-agent-refs/analysis-template.md +19 -19
  29. package/pipeline/multi-agent-refs/component-dispatch.md +5 -5
  30. package/pipeline/multi-agent-refs/conventions-defaults.md +2 -2
  31. package/pipeline/multi-agent-refs/features/analysis-jira.md +1 -1
  32. package/pipeline/multi-agent-refs/features/doctor.md +1 -1
  33. package/pipeline/multi-agent-refs/features/model-fallback.md +36 -0
  34. package/pipeline/multi-agent-refs/features/review-multi-repo.md +1 -1
  35. package/pipeline/multi-agent-refs/features/url-enrichment.md +1 -1
  36. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +2 -2
  37. package/pipeline/multi-agent-refs/phases/phase-3-review.md +2 -2
  38. package/pipeline/rules/figma-pipeline.md +8 -8
  39. package/pipeline/schemas/analysis-output.schema.json +1 -1
  40. package/pipeline/schemas/analysis-spec.schema.json +2 -2
  41. package/pipeline/schemas/figma-project-config.schema.json +1 -1
  42. package/pipeline/schemas/prefs.schema.json +2 -2
  43. package/pipeline/schemas/secret-patterns.json +124 -0
  44. package/pipeline/scripts/build-references.mjs +2 -2
  45. package/pipeline/scripts/bulk-read.sh +10 -1
  46. package/pipeline/scripts/cost-table.json +8 -1
  47. package/pipeline/scripts/doctor.mjs +1 -1
  48. package/pipeline/scripts/gen-facts.mjs +112 -7
  49. package/pipeline/scripts/phase-tracker.sh +5 -5
  50. package/pipeline/scripts/pre-commit-check.sh +30 -1
  51. package/pipeline/scripts/scan-skills.sh +26 -0
  52. package/pipeline/scripts/validate-analysis-doc.mjs +201 -26
  53. package/pipeline/scripts/verify-citations.mjs +1 -1
  54. package/pipeline/scripts/write-state.mjs +32 -0
  55. package/pipeline/skills/.skill-manifest.json +5 -5
  56. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +6 -6
  57. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +6 -6
  58. package/pipeline/skills/shared/core/multi-agent/SKILL.md +14 -13
  59. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +1 -1
  60. package/pipeline/skills/shared/external/signal-community/SKILL.md +8 -1
@@ -68,9 +68,9 @@ template_version: v3
68
68
 
69
69
  `status` is the publication claim; absent means `draft`. Under `final` an unresolved `AS-NN` or open Section 20 row is an ERROR, under `draft` it is counted. `/multi-agent:analysis-resolve` flips it, after a confirmation.
70
70
 
71
- `profile` names the template the document was rendered against (Locked 32) and `platform: none` marks the stack-optional render (Locked 35). Both are read by `validate-analysis-doc.mjs`, which applies a different contract per profile: without the key a corporate document would be judged against the global rules and its backbone would read as a pile of Locked 2 violations.
71
+ `profile` names the template the document was rendered against (Locked 31) and `platform: none` marks the stack-optional render (Locked 34). Both are read by `validate-analysis-doc.mjs`, which applies a different contract per profile: without the key a corporate document would be judged against the global rules and its backbone would read as a pile of Locked 2 violations.
72
72
 
73
- Phase 1 compares `evidence_digest` against an existing document to decide whether to reuse it (Locked 27). Phase 3 reads `platform` to verify file match, `mode` to know which section set to expect, and both `evidence_digest` and `base_commit` to judge freshness: the digest says the evidence changed, `base_commit` says the repo moved. Phase 2 parses the block but gates only on `template_version`.
73
+ Phase 1 compares `evidence_digest` against an existing document to decide whether to reuse it (Locked 26). Phase 3 reads `platform` to verify file match, `mode` to know which section set to expect, and both `evidence_digest` and `base_commit` to judge freshness: the digest says the evidence changed, `base_commit` says the repo moved. Phase 2 parses the block but gates only on `template_version`.
74
74
 
75
75
  ## Layer headings (A / B / C)
76
76
 
@@ -155,7 +155,7 @@ Citation: feature definition cites the primary Confluence spec page (`[Confluenc
155
155
 
156
156
  ## 2. Hedefler ve Karşı Hedefler / Goals and Non-Goals
157
157
 
158
- Never omitted. Locked 13 - both columns mandatory. A row in only one column is not accepted.
158
+ Never omitted. Locked 14 - both columns mandatory. A row in only one column is not accepted.
159
159
 
160
160
  ```markdown
161
161
  ## 2. Hedefler ve Karşı Hedefler <!-- TR -->
@@ -230,7 +230,7 @@ Confluence dispatch wraps each mermaid block in `<ac:structured-macro ac:name="m
230
230
 
231
231
  ## 4. Kullanıcı Hikayeleri / User Stories
232
232
 
233
- Never omitted. Locked 12 - Gherkin format mandatory. **Source-story completeness gate**: Gherkin scenarios cover the key happy/error/edge paths, but a `### 4.0 Kaynak Hikaye Izlenebilirligi / Source Story Traceability` table MUST list EVERY source-spec user-story ID (US-1..US-N) verbatim, each mapped to where it is covered (a Gherkin scenario or another section). Condensing into a few Gherkin scenarios is allowed ONLY if this table preserves the full source ID set; no source user story may be silently dropped. A missing source ID is a blocker and emits a Section 20 Risk row.
233
+ Never omitted. Locked 13 - Gherkin format mandatory. **Source-story completeness gate**: Gherkin scenarios cover the key happy/error/edge paths, but a `### 4.0 Kaynak Hikaye Izlenebilirligi / Source Story Traceability` table MUST list EVERY source-spec user-story ID (US-1..US-N) verbatim, each mapped to where it is covered (a Gherkin scenario or another section). Condensing into a few Gherkin scenarios is allowed ONLY if this table preserves the full source ID set; no source user story may be silently dropped. A missing source ID is a blocker and emits a Section 20 Risk row.
234
234
 
235
235
  ```markdown
236
236
  ## 4. Kullanıcı Hikayeleri <!-- TR -->
@@ -266,7 +266,7 @@ Then <expected outcome>
266
266
 
267
267
  ### 4.4 İş Kuralları / Business Rules
268
268
 
269
- Locked 31 - the traceability spine. Every business rule is deterministic and testable (same input always yields the same outcome) and carries a stable id `BR-<slug>-NN`.
269
+ Locked 30 - the traceability spine. Every business rule is deterministic and testable (same input always yields the same outcome) and carries a stable id `BR-<slug>-NN`.
270
270
 
271
271
  **The rule statement itself is written in EARS**, not free prose. EARS (Easy Approach to Requirements Syntax, IEEE RE'09) constrains the sentence to a fixed clause order and a small keyword set, which is what removes the ambiguity a prose rule carries. Five patterns, use the one that fits:
272
272
 
@@ -290,7 +290,7 @@ Each scenario in 4.1-4.3 cites the matching `BR-<slug>-NN` id (and, when a serve
290
290
 
291
291
  ### 4.5 Mevcut Davranış / Current Behaviour (OPTIONAL)
292
292
 
293
- `redesign: true` only. Locked 37; contract and checks in `analysis/redesign.md`. Evidence and certainty are derived from `repoEvidence`, never graded by the writer.
293
+ `redesign: true` only. Locked 36; contract and checks in `analysis/redesign.md`. Evidence and certainty are derived from `repoEvidence`, never graded by the writer.
294
294
 
295
295
  ```markdown
296
296
  | Id | Davranış / Behaviour | Kanıt / Evidence | Kesinlik / Certainty |
@@ -311,7 +311,7 @@ Each scenario in 4.1-4.3 cites the matching `BR-<slug>-NN` id (and, when a serve
311
311
 
312
312
  ## 5. Tasarım Referansı / Design Reference
313
313
 
314
- Per-platform projection. Omitted if no Figma URL AND no Code Connect mapping. Locked 17 + 18.
314
+ Per-platform projection. Omitted if no Figma URL AND no Code Connect mapping. Locked 18 + 19.
315
315
 
316
316
  ```markdown
317
317
  ## 5. Tasarım Referansı <!-- TR -->
@@ -319,7 +319,7 @@ Per-platform projection. Omitted if no Figma URL AND no Code Connect mapping. Lo
319
319
 
320
320
  ### 5.1 Frame galerisi / Frame gallery
321
321
 
322
- Drill into every Figma variant (Locked 18). When a section URL is given, list all of its child frames.
322
+ Drill into every Figma variant (Locked 19). When a section URL is given, list all of its child frames.
323
323
 
324
324
  | Frame ID | Varyant / Variant | Boyut / Dimensions | Görüntü / Screenshot | Ayırt edici / Distinctive | Code Connect |
325
325
  |---|---|---|---|---|---|
@@ -388,7 +388,7 @@ Locked 11 - existence is resolved against `evidence.codeConnect[]` (the Code Con
388
388
 
389
389
  Completeness audit over the **whole component surface**, not just the part this screen happens to touch. Phase 1b.2 resolves each Code Connect-bound instance to its main component (component set) and reads the full variant axis, so the "all values" column is the component's real axis rather than what the instance revealed. Every value gets exactly one disposition; an unmapped remainder is a Section 20 Risk.
390
390
 
391
- Without the traversal this table could only ever list properties the instance already used, which made "used subset" unverifiable against anything (Locked 29). Phase 2+ cannot re-fetch (Locked 30), so a value missed here is missed for the whole run.
391
+ Without the traversal this table could only ever list properties the instance already used, which made "used subset" unverifiable against anything (Locked 28). Phase 2+ cannot re-fetch (Locked 29), so a value missed here is missed for the whole run.
392
392
 
393
393
  | Bileşen / Component | Eksen / Axis | Tüm değerler / All values | Bu ekranda / Used here | Karar / Disposition | Not / Note |
394
394
  |---|---|---|---|---|---|
@@ -454,11 +454,11 @@ Per-platform projection translates token names: iOS uses `.Spacing.spacingN` enu
454
454
  | <name> | Lottie | no | Figma + motion spec | new, istisna gerekçesi / exception rationale: <reason> |
455
455
  ```
456
456
 
457
- Locked 14 - SVG default for new assets. Lottie or optimized PNG accepted with explicit rationale in the Notes column.
457
+ Locked 15 - SVG default for new assets. Lottie or optimized PNG accepted with explicit rationale in the Notes column.
458
458
 
459
459
  ## 9. API Kontratları / API Contracts
460
460
 
461
- Never omitted (if any service is consumed). Locked 16 - response variants exhaustive. **Service-completeness gate**: do NOT rely solely on the supplied Swagger/Confluence inputs - reconcile against the canonical Api Contract page(s) for this screen. Every endpoint listed on the screen's Api Contract page MUST appear in 9.1 (or be explicitly tagged out-of-scope with a reason). An uncovered contract endpoint is a blocker and emits a Section 20 Risk row.
461
+ Never omitted (if any service is consumed). Locked 17 - response variants exhaustive. **Service-completeness gate**: do NOT rely solely on the supplied Swagger/Confluence inputs - reconcile against the canonical Api Contract page(s) for this screen. Every endpoint listed on the screen's Api Contract page MUST appear in 9.1 (or be explicitly tagged out-of-scope with a reason). An uncovered contract endpoint is a blocker and emits a Section 20 Risk row.
462
462
 
463
463
  ```markdown
464
464
  ## 9. API Kontratları <!-- TR -->
@@ -587,11 +587,11 @@ Per-platform projection (both modes):
587
587
  | <event_name> | <param1, param2> | 4.1 happy path / BR-<slug>-01 | <Firebase schema / repo / Confluence> | new |
588
588
  | <event_name> | <params> | <story or BR id> | <source> | reuse |
589
589
 
590
- Each event's Trigger cites the user story (Section 4) or business rule (`BR-` id, Section 4.4) it fires on, so the analytics plan traces to behavior (Locked 31).
590
+ Each event's Trigger cites the user story (Section 4) or business rule (`BR-` id, Section 4.4) it fires on, so the analytics plan traces to behavior (Locked 30).
591
591
  PII redaction: <list of fields hashed before telemetry>
592
592
  ```
593
593
 
594
- Locked 11 - direct-match events emit reuse rows. Locked 17 - PII fields (email, phone, national ID) hashed before emit.
594
+ Locked 11 - direct-match events emit reuse rows. PII fields (email, phone, national ID) are hashed before emit.
595
595
 
596
596
  ## 12. Deeplink ve Push Notification / Deeplink and Push
597
597
 
@@ -646,7 +646,7 @@ Never omitted. Per-platform projection from Phase 1c conventions.
646
646
 
647
647
  ### 13.1 Kavram tablosu / Concept table
648
648
 
649
- Locked 21 - platform-agnostic concept layer rendered with repo conventions.
649
+ Locked 22 - platform-agnostic concept layer rendered with repo conventions.
650
650
 
651
651
  | Kavram / Concept | Karşılığı / Realization | Confidence | Evidence |
652
652
  |---|---|---|---|
@@ -695,7 +695,7 @@ Locked 21 - platform-agnostic concept layer rendered with repo conventions.
695
695
 
696
696
  ### 13.6 SwiftUI Preview block (iOS projection, SwiftUI views only)
697
697
 
698
- Per Locked 29 - SwiftUI views ship with `#Preview` macro (Swift 5.9+) or `PreviewProvider` (legacy). UIKit view controllers are exempt from this section. Pass B detects the view kind via `import SwiftUI` + `: View` protocol conformance against `evidence.repoEvidence[<repo>].buckets.uiComponents`; if a feature has only UIKit `UIViewController` artefacts, this subsection is omitted with note `(N/A: UIKit-only feature)`.
698
+ Per Locked 27 - SwiftUI views ship with `#Preview` macro (Swift 5.9+) or `PreviewProvider` (legacy). UIKit view controllers are exempt from this section. Pass B detects the view kind via `import SwiftUI` + `: View` protocol conformance against `evidence.repoEvidence[<repo>].buckets.uiComponents`; if a feature has only UIKit `UIViewController` artefacts, this subsection is omitted with note `(N/A: UIKit-only feature)`.
699
699
 
700
700
  One preview entry per variant matters for Xcode Canvas and snapshot test alignment. Preview macro convention is read from `conventions[<repo>].previewMacro` (Phase 1c) - the renderer picks `#Preview` for Swift 5.9+ repos and `PreviewProvider` for legacy ones.
701
701
 
@@ -769,7 +769,7 @@ Per-platform projection.
769
769
 
770
770
  ### 15.1 Birim testleri / Unit tests (kural bazlı / rule-driven)
771
771
 
772
- Locked 31 - one sub-table per business rule from Section 4.4. Enumerate the cases: happy, boundary (min/max, off-by-one), error/failure, empty/nil. Each row is Given / When / Then plus a framework-correct test name that traces back to the `BR-` id. Boundary rows collapse into one parameterized test.
772
+ Locked 30 - one sub-table per business rule from Section 4.4. Enumerate the cases: happy, boundary (min/max, off-by-one), error/failure, empty/nil. Each row is Given / When / Then plus a framework-correct test name that traces back to the `BR-` id. Boundary rows collapse into one parameterized test.
773
773
 
774
774
  Framework per platform (from Phase 1c conventions; these are the modern defaults): iOS Swift Testing (`@Test`, `@Suite`, `@Test(arguments:)` for boundary tables, `#expect` / `#require`); Android JUnit5 + MockK (`coEvery` / `coVerify`) + Turbine (`flow.test { awaitItem() }`) + coroutines-test (`runTest`, `StandardTestDispatcher`); Backend pytest (parametrize); Web Vitest.
775
775
 
@@ -782,7 +782,7 @@ Framework per platform (from Phase 1c conventions; these are the modern defaults
782
782
  | error | <failure state> | <action> | <error/emission> | `func rule_failure_emitsError()` |
783
783
  | empty/nil | <empty state> | <action> | <default/guard> | `func rule_empty_returnsIdle()` |
784
784
 
785
- (Repeat one sub-table per rule. A rule with no unit-test row fails the dispatch gate, Locked 31.)
785
+ (Repeat one sub-table per rule. A rule with no unit-test row fails the dispatch gate, Locked 30.)
786
786
 
787
787
  ### 15.2 Görsel regresyon / Snapshot tests
788
788
 
@@ -832,7 +832,7 @@ Framework: iOS XCUITest (`waitForExistence`, identifier-driven); Android Compose
832
832
 
833
833
  The scenarios a person runs by hand. Same format the pipeline already posts as the Jira test-scenario comment (`/multi-agent:resume-local`), defined once and read by both. Omitted entirely when the feature has none (Locked 2); never rendered as an empty table.
834
834
 
835
- Each row is executable by someone who did not write the feature: no "verify it works", no implied setup. Cover the happy path, at least one boundary, and every failure mode that reaches the user - the same four-way split Locked 31 requires of unit tests.
835
+ Each row is executable by someone who did not write the feature: no "verify it works", no implied setup. Cover the happy path, at least one boundary, and every failure mode that reaches the user - the same four-way split Locked 30 requires of unit tests.
836
836
 
837
837
  | Senaryo / Scenario | Ön koşul / Precondition | Adımlar / Steps | Beklenen / Expected | BR ID |
838
838
  |---|---|---|---|---|
@@ -983,7 +983,7 @@ These row types are exactly what `build-references.mjs` emits, and the example i
983
983
  - **Erişim / Access** is `ok` or `erişilemedi (<reason>)`. A source that was declared but could not be fetched still gets a row. Dropping it hides the gap: the reader sees a document that never mentions the API contract and assumes there was none, rather than knowing it was unreachable.
984
984
  - **Serbest metin** rows carry what the user stated in conversation that no fetched source contains, quoted verbatim, with the decision it settled in the `Rol` column. Scope decisions made in chat are evidence; leaving them out is how a document loses the reason it excluded something.
985
985
 
986
- **Coverage gate (Locked 34).** Before the document is emitted, the validator compares this table against the evidence record. Every entry in `evidence.figma[]`, `evidence.confluence[]`, `evidence.jira[]`, `evidence.swagger[]`, `evidence.repo[]`, `evidence.standards[]`, `evidence.firebase[]`, `evidence.documents[]`, `evidence.outside[]`, `evidence.freeText[]` and every entry in `evidence.fetchErrors[]` must appear as a row. A source that shaped the document but is missing from References fails the dispatch gate, and a row with no matching evidence entry fails it too - an invented reference is worse than a missing one.
986
+ **Coverage gate (Locked 33).** Before the document is emitted, the validator compares this table against the evidence record. Every entry in `evidence.figma[]`, `evidence.confluence[]`, `evidence.jira[]`, `evidence.swagger[]`, `evidence.repo[]`, `evidence.standards[]`, `evidence.firebase[]`, `evidence.documents[]`, `evidence.outside[]`, `evidence.freeText[]` and every entry in `evidence.fetchErrors[]` must appear as a row. A source that shaped the document but is missing from References fails the dispatch gate, and a row with no matching evidence entry fails it too - an invented reference is worse than a missing one.
987
987
 
988
988
  ## 22. Sözlük / Glossary
989
989
 
@@ -1,4 +1,4 @@
1
- # Component Dispatch (Phase 3 short-circuit)
1
+ # Component Dispatch (Phase 2 short-circuit)
2
2
 
3
3
  <!-- toc -->
4
4
  - [Entry conditions](#entry-conditions)
@@ -17,7 +17,7 @@ This doc is referenced from `$HOME/.claude/multi-agent-refs/phases/phase-2-dev.m
17
17
 
18
18
  ## Entry conditions
19
19
 
20
- Phase 3 checks, in order:
20
+ Phase 2 checks, in order:
21
21
 
22
22
  1. `agent-state.json` has `taskType: "component"` (set by Phase 0 Step 7).
23
23
  2. `agent-state.json` has a non-null `figmaUrl`.
@@ -35,7 +35,7 @@ naming the missing field and pointing at `phase0-exit-gate.mjs`.
35
35
  > publish and no component review. Spacing came out `16` where the frame said
36
36
  > `Spacing/12`, and half the branch's commits were rework.
37
37
  >
38
- > The Phase 0 exit gate now prevents reaching Phase 3 in that state at all; this
38
+ > The Phase 0 exit gate now prevents reaching Phase 2 in that state at all; this
39
39
  > halt is the second line of defence. A component task that cannot be dispatched as
40
40
  > one must fail loudly, because the generic path produces artefacts that look
41
41
  > finished and are not.
@@ -109,7 +109,7 @@ When multi-repo mode is active (`state.projects.length > 1`), the dispatch layer
109
109
  - View / Configuration / Modifiers / Preview → `repos.components`
110
110
  - Wiki markdown → `repos.wiki` (if `wiki.mode === submodule` or `separate-repo`) or `repos.components/.wiki/components/` (if `wiki.mode === in-repo`)
111
111
 
112
- Multi-agent does **not** split the component task into per-repo Phase 3 runs - the dispatch layer sequences the plugin invocation so token writes to `common` precede component reads in `components`. If the plugin skill is not repo-graph aware, dispatch runs it against `repos.components` and performs the `common` token/key writes itself around the call.
112
+ Multi-agent does **not** split the component task into per-repo Phase 2 runs - the dispatch layer sequences the plugin invocation so token writes to `common` precede component reads in `components`. If the plugin skill is not repo-graph aware, dispatch runs it against `repos.components` and performs the `common` token/key writes itself around the call.
113
113
 
114
114
  ## Failure + resume
115
115
 
@@ -118,7 +118,7 @@ On failure (the plugin skill returns an unrecoverable build/test error, or the d
118
118
  1. The dispatch layer persists `{error, buildLog, testLog}` to `state.phases["3"].errors[]`.
119
119
  2. Multi-agent increments `state.phases["3"].retryCount`.
120
120
  3. Retry re-invokes the plugin skill (it is idempotent on an existing component; it reconciles rather than duplicating). There is no bundled `phase-<N>` resume anymore.
121
- 4. Hard kill at `retryCount === 3` → surface the errors to the user, halt Phase 3. Do not loop indefinitely.
121
+ 4. Hard kill at `retryCount === 3` → surface the errors to the user, halt Phase 2. Do not loop indefinitely.
122
122
 
123
123
  ## Short-run behaviour
124
124
 
@@ -154,7 +154,7 @@ Apply platform default + open Section 20 risk row
154
154
  | Backend | not applicable | | |
155
155
  | Web | not applicable | (Storybook stories live in `.stories.tsx` files, tracked under C2 conventions) | |
156
156
 
157
- Detection: scan up to 10 SwiftUI view files in the candidate set; majority pick wins. Confidence `high` if 5+ matching examples, `medium` if 3-4, `low` if 2, `none` if 0 SwiftUI views found (Pass B then omits Section 13.6 with `(N/A: UIKit-only feature)` note per Locked 29).
157
+ Detection: scan up to 10 SwiftUI view files in the candidate set; majority pick wins. Confidence `high` if 5+ matching examples, `medium` if 3-4, `low` if 2, `none` if 0 SwiftUI views found (Pass B then omits Section 13.6 with `(N/A: UIKit-only feature)` note per Locked 28).
158
158
 
159
159
  ## C7 - Dependency Injection
160
160
 
@@ -191,4 +191,4 @@ When the team standardizes on a different default, update this file directly and
191
191
  - Locked 22: convention extraction mandatory (Phase 1c)
192
192
  - Locked 23: convention fallback opens a risk row
193
193
  - Locked 24: every Pass B cell carries a footnote
194
- - Locked 28: SwiftUI Preview block mandatory for iOS SwiftUI views (Section 13.6, C8 convention)
194
+ - Locked 27: SwiftUI Preview block mandatory for iOS SwiftUI views (Section 13.6, C8 convention)
@@ -50,7 +50,7 @@ No forward check can see the second one, and it is the failure mode of building
50
50
  a tree from a model's reading rather than from the document's own ids.
51
51
 
52
52
  The atom is `BR-<slug>-NN` in the global profile and `FG-NN` in the corporate
53
- one; the group is the `BR-<slug>` prefix, or `UC-NN`. Locked 31 guarantees both
53
+ one; the group is the `BR-<slug>` prefix, or `UC-NN`. Locked 30 guarantees both
54
54
  exist, which is why the tree can be derived rather than invented.
55
55
 
56
56
  `coverageOf()` is exported and tested directly. The planner cannot emit an
@@ -228,7 +228,7 @@ is countable.
228
228
 
229
229
  The reason it exists: every registered server sends its tool list on every turn,
230
230
  the user adds them one at a time, and nobody ever sees the running total - our
231
- own toolkit contributes 99 tools by itself. This is the same argument that made
231
+ own toolkit contributes 115 tools by itself. This is the same argument that made
232
232
  the pre-run context budget a measured number rather than an intention.
233
233
 
234
234
  It only reports. It never disables a server, never blocks and never warns: how
@@ -6,6 +6,7 @@
6
6
  - [Turning the fable rung off](#turning-the-fable-rung-off)
7
7
  - [Triggers (checked in this order)](#triggers-checked-in-this-order)
8
8
  - [Logging](#logging)
9
+ - [Routing at dispatch](#routing-at-dispatch)
9
10
  - [Non-goals](#non-goals)
10
11
  - [Codex CLI](#codex-cli)
11
12
  <!-- /toc -->
@@ -176,6 +177,41 @@ and one agent-log line so Phase 5 reports show which phases ran degraded.
176
177
  The cost ledger prices the dispatch at the model actually used (the tracker's
177
178
  per-phase `model` field already carries the override).
178
179
 
180
+ ## Routing at dispatch
181
+
182
+ `$HOME/.claude/lib/model-dispatch.sh` turns `prefs.global.modelRouting` into a
183
+ rung. Two call sites ask it: the subagent dispatch contract in
184
+ `skills/shared/core/multi-agent/SKILL.md`, and `bulk-read.sh`.
185
+
186
+ Routing is consulted only when the phase named no model. A per-dispatch
187
+ `PHASE_MODEL_OVERRIDE` is a decision the phase made for a reason - Phase 3 sets
188
+ Reviewer 3 to sonnet so the three reviewers disagree - and a policy able to
189
+ overrule it would turn a deliberate choice into a suggestion.
190
+
191
+ Two limits are enforced by the script rather than written here and hoped for:
192
+
193
+ - **A rule may not turn the fable rung on.** With `modelFallback.fableEnabled`
194
+ false, a rule preferring `fable` falls past it to the next rung. Otherwise
195
+ `/multi-agent:model off` would have a second door.
196
+ - **A subagent stays inside the Anthropic ladder.** Subagent dispatch belongs to
197
+ the host, so a rule preferring a non-Anthropic rung cannot be honoured there;
198
+ the script says so on stderr and moves on rather than substituting an
199
+ Anthropic rung and leaving the user believing a rule worked that never could.
200
+ `bulk-read.sh` is a call site the pipeline makes itself, so the same rung IS
201
+ allowed there. That asymmetry is why `/multi-agent:route-status` reports scope
202
+ per call site instead of one on/off.
203
+
204
+ The script never fails and never returns an empty rung: a missing preferences
205
+ file, a missing `jq`, unparseable JSON, an out-of-scope call site and a rung
206
+ absent from `cost-table.json` all return the caller's default and exit 0. A
207
+ router that can die would turn every call site into a place the run can die, for
208
+ a feature that ships disabled.
209
+
210
+ `cost-table.json` rows carry a `provider`, which is what tells the two layers
211
+ apart. `smoke-model-dispatch.sh` drives the script and also asserts that the two
212
+ call sites invoke it: a correct router nothing calls is the same outage with
213
+ better internals.
214
+
179
215
  ## Non-goals
180
216
 
181
217
  - No automatic re-upgrade mid-run (a run that fell back stays fallen back -
@@ -1,4 +1,4 @@
1
- # Phase 4 Review - Multi-Repo Mode
1
+ # Phase 3 Review - Multi-Repo Mode
2
2
 
3
3
  > Loaded on demand from `phases/phase-3-review.md`. Conditional on
4
4
  > `state.projects[].length > 1`; a single-repo run (the common case) never needs
@@ -77,7 +77,7 @@ Two entry shapes reach this step, and `metadata.source` says which:
77
77
  - Issue list / single issue: if `issueInstanceId` in URL → `GET /ssc/api/v1/projectVersions/<versionId>/issues?q=issueInstanceId:"<id>"&fields=id,issueName,friority,fullFileName,lineNumber,analyzer,kingdom`. Otherwise fetch top open issue of the version.
78
78
  - Issue details + recommendation: `GET /ssc/api/v1/issueDetails/<issueId>` → description, recommendation, code snippet (if the artifact is retained).
79
79
  - Store as `state.fortifyFinding = { host, versionId, versionName, projectName, issueId?, issueName?, severity, friority, kingdom, analyzer, file, line, description, recommendation, codeSnippet?, fetchedAt }`.
80
- - Phase 2 Planning injects `state.fortifyFinding` into the architectural-review prompt under a **Known Security Finding** section, and the generated task breakdown includes a task addressing the finding (naming convention: `sec(fortify-<issueId>): <issueName> at <file>:<line>`).
80
+ - Phase 1 Plan injects `state.fortifyFinding` into the architectural-review prompt under a **Known Security Finding** section, and the generated task breakdown includes a task addressing the finding (naming convention: `sec(fortify-<issueId>): <issueName> at <file>:<line>`).
81
81
  - Soft-fail: 401 → re-run Token Save Flow for `fortify`; 403 → warn and skip; network timeout → respect VPN fallback (skip enrichment, mark `state.fortifyFinding = { skipped: "vpn-unreachable" }`).
82
82
 
83
83
  ##### Step 1b.3 - Graylog deep fetch (runs when `type == "graylog"` present in `state.contextLinks[]`)
@@ -196,7 +196,7 @@ Phase 1 and Phase 2 pre-flights BLOCK on `analysis/<feature-slug>-<platform>.md`
196
196
 
197
197
  `analysisPhase.forceFull` (default `false`) overrides the skip row, for a small change that must still leave a spec behind. `analysisPhase.mode` sets depth: `auto` is Lite on the skip-row shape and Full otherwise; `full` / `lite` pin it.
198
198
 
199
- A fresh document is also skipped when one already exists for this feature and platform AND its front-matter `evidence_digest` still matches (Locked 27 cache); record `docStatus = "reused"`.
199
+ A fresh document is also skipped when one already exists for this feature and platform AND its front-matter `evidence_digest` still matches (Locked 26 cache); record `docStatus = "reused"`.
200
200
 
201
201
  **How it runs.** Load `$HOME/.claude/multi-agent-refs/analysis/` on demand, in order: `locked.md` (the 31 binding decisions) then `evidence.md`, `synthesis.md`, `render.md`. Intake is NOT re-asked; platform, repos and account come from Phase 0 state. Autopilot auto-approves the Phase 2a convention preview and writes the local file, because it may not ask.
202
202
 
@@ -208,7 +208,7 @@ Progress line: `→ analysis doc: <produced|reused|not-applicable> (<N> platform
208
208
 
209
209
  #### Output contract
210
210
 
211
- Two artefacts, both read downstream: `state.analysis` (the object below, for Phase 1 decomposition) and the Step 4 document (Phase 1 pre-flight + Phase 2's sole design source, Locked 30).
211
+ Two artefacts, both read downstream: `state.analysis` (the object below, for Phase 1 decomposition) and the Step 4 document (Phase 1 pre-flight + Phase 2's sole design source, Locked 29).
212
212
 
213
213
  Phase 1 produces an object conforming to `$HOME/.claude/schemas/analysis-output.schema.json` and persists it to `state.analysis`. Required fields (exact names per the schema): `stack` (detected stack identifier + primary language), `touchedAreas[]` (path + why), `risks[]` (existing-code hazards/observations the planner must respect - each `{risk, severity, mitigation}`; use an empty array when none), `summary` (one-paragraph human-readable). Phase 1 reads this object as its sole input - see `phase-1-plan.md`'s Input contract.
214
214
 
@@ -1,6 +1,6 @@
1
1
  ### Phase 3: Review (parallel + triage + user test)
2
2
 
3
- > **TLDR** - Three-stage review. Stage 1: deterministic gates (build + lint + test + secret scan) that MUST pass. Stage 2: 3 reviewers in parallel per host, second slot **CLI-aware** (Claude Code Fable + Opus + Sonnet; Copilot CLI GPT-5.4 + Opus + Sonnet). Stage 3: Fable triage (Opus on Copilot CLI) filters raw findings for false positives and out-of-scope items. Only triage-accepted blocking items loop back to Phase 2.
3
+ > **TLDR** - Two review stages over evidence Phase 2 already produced: the deterministic gates (build + lint + test + secret scan) are the Phase 2 exit gate now, and this phase inherits `.build.log` and `.test.log` rather than building again. Stage 1: 3 reviewers in parallel per host, second slot **CLI-aware** (Claude Code Fable + Opus + Sonnet; Copilot CLI GPT-5.4 + Opus + Sonnet). Stage 2: Fable triage (Opus on Copilot CLI) filters raw findings for false positives and out-of-scope items. The user test step closes the phase. Only triage-accepted blocking items loop back to Phase 2.
4
4
 
5
5
  <!-- progress-contract: applied -->
6
6
  Progress emission per `$HOME/.claude/multi-agent-refs/progress-contract.md` - lines for each gate, each reviewer dispatch + finish, triage start, triage verdict, fix dispatch.
@@ -617,7 +617,7 @@ Log: "Phase 3: Review - raw={N1+N2+N3} accepted={Na} deferred={Nd} rejected={N
617
617
  bash $HOME/.claude/scripts/phase-tracker.sh tokens 4 <input_count> <output_count> [cached_count]
618
618
  ```
619
619
 
620
- The optional 4th `cached_count` is the prompt-cache-read token count when the host reports it (Anthropic `cache_read_input_tokens`); it defaults to 0 and is priced at the cheaper `cacheReadPerMtok` rate in the Phase 5 cost ledger. The tracker accumulates the totals additively, so multiple calls in the same phase compound. The render output then shows live cost on the active phase tile (e.g. `Phase 3 Dev 2m 14s · 12.4k tok`). This satisfies the contract in `$HOME/.claude/multi-agent-refs/tracker-contract.md` and the `smoke-tracker-tokens-invocation.sh` enforcement gate. Skipping this call is the #1 cause of "I can't see how much it cost" complaints.
620
+ The optional 4th `cached_count` is the prompt-cache-read token count when the host reports it (Anthropic `cache_read_input_tokens`); it defaults to 0 and is priced at the cheaper `cacheReadPerMtok` rate in the Phase 5 cost ledger. The tracker accumulates the totals additively, so multiple calls in the same phase compound. The render output then shows live cost on the active phase tile (e.g. `Phase 2 Dev 2m 14s · 12.4k tok`). This satisfies the contract in `$HOME/.claude/multi-agent-refs/tracker-contract.md` and the `smoke-tracker-tokens-invocation.sh` enforcement gate. Skipping this call is the #1 cause of "I can't see how much it cost" complaints.
621
621
 
622
622
  Contract and rationale: `progress-contract.md` -> Token telemetry forwarding.
623
623
 
@@ -4,7 +4,7 @@ Skill set lives in the marketplace plugins - `ai-ios-toolkit` (iOS/SwiftUI) an
4
4
 
5
5
  ### MUST: Figma access - 3-tier fallback chain (BLOCKING, pipeline-wide)
6
6
 
7
- When any task references a Figma frame (URL, node ID, or free-text "from the design"), the pipeline MUST resolve a Figma ground-truth artefact via the chain below before any UI line is written, every phase that consumes or verifies the reference (Phase 0 intake, Phase 1 analysis, Phase 2 planning, Phase 3 dev, Phase 4 review, Phase 5 manual test, Phase 7 channels). Skipping the chain has cost multiple rebuild rounds across projects.
7
+ When any task references a Figma frame (URL, node ID, or free-text "from the design"), the pipeline MUST resolve a Figma ground-truth artefact via the chain below before any UI line is written, every phase that consumes or verifies the reference (Phase 0 intake, Phase 1 plan, Phase 2 dev, Phase 3 review, Phase 4 commit, Phase 5 report). Skipping the chain has cost multiple rebuild rounds across projects.
8
8
 
9
9
  The three tiers run in strict priority order. A tier is "available" when its required credentials / inputs are present AND a probe call succeeds. Lower tiers are tried only when the higher tier is unreachable.
10
10
 
@@ -75,12 +75,12 @@ The 3-tier fallback chain above governs Figma access in `/multi-agent:analysis`
75
75
  | Phase | Sub-command examples | Figma MCP allowed | Figma REST allowed | Reason |
76
76
  |---|---|---|---|---|
77
77
  | Analysis Phase 1 | `/multi-agent:analysis` Phase 1 fetch | yes | yes (Tier 2 fallback) | Single source of design ground truth |
78
- | Plan (Phase 2) | `/multi-agent`, `/multi-agent:local`, `/multi-agent:autopilot`, `/multi-agent:local-autopilot` | no | no | Plan reads analysis doc Section 14 + Section 6 |
79
- | Dev (Phase 3) | every mode that runs Phase 3 (8 modes total) | no | no | Reads analysis doc + Code Connect mapping |
80
- | Review (Phase 4) | `/multi-agent:review`, every full-pipeline mode | no | no | Reviewer cites analysis doc Section 21 References |
81
- | Test (Phase 5) | `/multi-agent:test`, `/multi-agent:manual-test`, every full mode | no | no | Variant list comes from analysis Section 13.6 + 15.2 |
82
- | Commit (Phase 6) | every mode | no | no | PR body links to analysis doc URL, no design fetch |
83
- | Report (Phase 7) | `/multi-agent:channels`, every full mode | no | no | Channels embed analysis doc, no design fetch |
78
+ | Plan (Phase 1) | `/multi-agent`, `/multi-agent:local`, `/multi-agent:autopilot`, `/multi-agent:local-autopilot` | no | no | Plan reads analysis doc Section 14 + Section 6 |
79
+ | Dev (Phase 2) | every mode that runs Phase 2 (8 modes total) | no | no | Reads analysis doc + Code Connect mapping |
80
+ | Review (Phase 3) | `/multi-agent:review`, every full-pipeline mode | no | no | Reviewer cites analysis doc Section 21 References |
81
+ | Test (inside Phase 3) | `/multi-agent:test`, `/multi-agent:manual-test`, every full mode | no | no | Variant list comes from analysis Section 13.6 + 15.2 |
82
+ | Commit (Phase 4) | every mode | no | no | PR body links to analysis doc URL, no design fetch |
83
+ | Report (Phase 5) | `/multi-agent:channels`, every full mode | no | no | Channels embed analysis doc, no design fetch |
84
84
 
85
85
  ### Halt condition
86
86
 
@@ -200,7 +200,7 @@ Provider interfaces are defined inline in the plugin skill sets - see the `ai-
200
200
  |---------|--------|---------------|
201
201
  | Registry CLI | `registry.enabled` | Manual component name/path input |
202
202
  | Projects V2 Board | `board.enabled` | No board column updates |
203
- | Wiki | `wiki.enabled` | Phase 7 wiki skipped |
203
+ | Wiki | `wiki.enabled` | Phase 5 wiki skipped |
204
204
  | Confluence | `confluence.enabled` | Phase 7a confluence skipped |
205
205
  | Figma MCP | `figma.mcpEnabled` | REST API fallback |
206
206
 
@@ -72,7 +72,7 @@
72
72
  "docStatus": {
73
73
  "type": "string",
74
74
  "enum": ["produced", "reused", "not-applicable"],
75
- "description": "v15.17+ - what Phase 1 Step 4 did about the analysis document. `produced` wrote it, `reused` matched an existing evidence_digest (Locked 27), `not-applicable` means the task needs none (bugfix/chore with no Figma reference). Phase 2 and Phase 3 pre-flights branch on this instead of guessing from the filesystem, and `not-applicable` is a legitimate skip rather than an abort."
75
+ "description": "v15.17+ - what Phase 1 Step 4 did about the analysis document. `produced` wrote it, `reused` matched an existing evidence_digest (Locked 26), `not-applicable` means the task needs none (bugfix/chore with no Figma reference). Phase 2 and Phase 3 pre-flights branch on this instead of guessing from the filesystem, and `not-applicable` is a legitimate skip rather than an abort."
76
76
  },
77
77
  "docPath": {
78
78
  "type": "array",
@@ -55,7 +55,7 @@
55
55
  "type": "string",
56
56
  "enum": ["global", "corporate"],
57
57
  "default": "global",
58
- "description": "Analysis profile chosen at Phase 0 Step 1b (Locked 32). 'global' renders analysis-template.md (23-section development handoff); 'corporate' renders analysis-template-corporate.md (IG/UC/FG requirements document). Both read the same evidence; only the projection differs."
58
+ "description": "Analysis profile chosen at Phase 0 Step 1b (Locked 31). 'global' renders analysis-template.md (23-section development handoff); 'corporate' renders analysis-template-corporate.md (IG/UC/FG requirements document). Both read the same evidence; only the projection differs."
59
59
  },
60
60
  "options": {
61
61
  "type": "object",
@@ -701,7 +701,7 @@
701
701
  },
702
702
  "freeText": {
703
703
  "type": "array",
704
- "description": "User statements made in conversation that no fetched source carries, recorded so Section 21 can cite the decisions they settled (Locked 34).",
704
+ "description": "User statements made in conversation that no fetched source carries, recorded so Section 21 can cite the decisions they settled (Locked 33).",
705
705
  "items": {
706
706
  "type": "object",
707
707
  "additionalProperties": false,
@@ -276,7 +276,7 @@
276
276
  "ui": {
277
277
  "type": "object",
278
278
  "additionalProperties": false,
279
- "description": "UI interaction systems. Consumed by the figma-navigation / figma-overlays / figma-bottom-sheets convention skills and by Phase 4 review. When a section is absent or its mode is 'native', the pipeline uses the platform's stock system (SwiftUI NavigationStack/.sheet on iOS, Compose Navigation/dialogs on Android). Set mode 'custom' to route to a project-supplied system by the type names below.",
279
+ "description": "UI interaction systems. Consumed by the figma-navigation / figma-overlays / figma-bottom-sheets convention skills and by Phase 3 review. When a section is absent or its mode is 'native', the pipeline uses the platform's stock system (SwiftUI NavigationStack/.sheet on iOS, Compose Navigation/dialogs on Android). Set mode 'custom' to route to a project-supplied system by the type names below.",
280
280
  "properties": {
281
281
  "navigationSystem": {
282
282
  "type": "object",
@@ -911,7 +911,7 @@
911
911
  },
912
912
  "analysisProfiles": {
913
913
  "type": "array",
914
- "description": "v16.6+ - which analysis standards the /multi-agent:analysis Step 1b picker offers (Locked 32). Listing one value auto-resolves the step instead of asking a question whose answer is already settled. Omitted means both are offered.",
914
+ "description": "v16.6+ - which analysis standards the /multi-agent:analysis Step 1b picker offers (Locked 31). Listing one value auto-resolves the step instead of asking a question whose answer is already settled. Omitted means both are offered.",
915
915
  "items": {
916
916
  "type": "string",
917
917
  "enum": ["global", "corporate"]
@@ -2045,7 +2045,7 @@
2045
2045
  "mcpSurface": {
2046
2046
  "type": "object",
2047
2047
  "additionalProperties": false,
2048
- "description": "v17.6.0+ - how many MCP servers this host has registered. Every registered server's tool list is charged against the context window on EVERY turn, and the user adds them one at a time without ever seeing the running total; our own toolkit contributes 99 tools by itself. `doctor` reports the count, it never disables anything.",
2048
+ "description": "v17.6.0+ - how many MCP servers this host has registered. Every registered server's tool list is charged against the context window on EVERY turn, and the user adds them one at a time without ever seeing the running total; our own toolkit contributes 115 tools by itself. `doctor` reports the count, it never disables anything.",
2049
2049
  "properties": {
2050
2050
  "infoAbove": {
2051
2051
  "type": "integer",
@@ -0,0 +1,124 @@
1
+ {
2
+ "$comment": "Provider shapes for smoke-secret-parity.sh, which proves both secret surfaces block the same set. Each row is SPLIT into a prefix, a filler character and a length, and the gate joins them at run time. No complete credential-shaped string is stored here, so the scanners this file exists to test do not block the file itself - which is exactly what happened when the samples were stored whole and the commit hook refused them. Adding a provider means adding a row here first; the gate then fails until both surfaces catch it.",
3
+ "providers": [
4
+ {
5
+ "id": "github-pat",
6
+ "prefix": "ghp_",
7
+ "fill": "A",
8
+ "len": 36,
9
+ "suffix": ""
10
+ },
11
+ {
12
+ "id": "github-fine-grained",
13
+ "prefix": "github_pat_",
14
+ "fill": "A",
15
+ "len": 66,
16
+ "suffix": ""
17
+ },
18
+ {
19
+ "id": "slack",
20
+ "prefix": "xoxb-",
21
+ "fill": "A",
22
+ "len": 16,
23
+ "suffix": ""
24
+ },
25
+ {
26
+ "id": "aws",
27
+ "prefix": "AKIA",
28
+ "fill": "A",
29
+ "len": 16,
30
+ "suffix": ""
31
+ },
32
+ {
33
+ "id": "google",
34
+ "prefix": "AIza",
35
+ "fill": "A",
36
+ "len": 35,
37
+ "suffix": ""
38
+ },
39
+ {
40
+ "id": "npm",
41
+ "prefix": "npm_",
42
+ "fill": "A",
43
+ "len": 36,
44
+ "suffix": ""
45
+ },
46
+ {
47
+ "id": "gitlab",
48
+ "prefix": "glpat-",
49
+ "fill": "A",
50
+ "len": 20,
51
+ "suffix": ""
52
+ },
53
+ {
54
+ "id": "stripe",
55
+ "prefix": "sk_live_",
56
+ "fill": "A",
57
+ "len": 20,
58
+ "suffix": ""
59
+ },
60
+ {
61
+ "id": "anthropic",
62
+ "prefix": "sk-ant-",
63
+ "fill": "A",
64
+ "len": 36,
65
+ "suffix": ""
66
+ },
67
+ {
68
+ "id": "openai",
69
+ "prefix": "sk-proj-",
70
+ "fill": "A",
71
+ "len": 36,
72
+ "suffix": ""
73
+ },
74
+ {
75
+ "id": "perplexity",
76
+ "prefix": "pplx-",
77
+ "fill": "A",
78
+ "len": 36,
79
+ "suffix": ""
80
+ },
81
+ {
82
+ "id": "huggingface",
83
+ "prefix": "hf_",
84
+ "fill": "A",
85
+ "len": 32,
86
+ "suffix": ""
87
+ },
88
+ {
89
+ "id": "figma-pat",
90
+ "prefix": "figd_",
91
+ "fill": "A",
92
+ "len": 24,
93
+ "suffix": ""
94
+ },
95
+ {
96
+ "id": "figma-mcp",
97
+ "prefix": "figu_",
98
+ "fill": "A",
99
+ "len": 24,
100
+ "suffix": ""
101
+ },
102
+ {
103
+ "id": "service-account",
104
+ "prefix": "\"type\": \"service_",
105
+ "fill": "",
106
+ "len": 0,
107
+ "suffix": "account\""
108
+ },
109
+ {
110
+ "id": "url-with-credentials",
111
+ "prefix": "https://user:",
112
+ "fill": "s",
113
+ "len": 12,
114
+ "suffix": "@example.com/x.git"
115
+ },
116
+ {
117
+ "id": "bearer-header",
118
+ "prefix": "Authorization: Bearer ",
119
+ "fill": "A",
120
+ "len": 24,
121
+ "suffix": ""
122
+ }
123
+ ]
124
+ }
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // build-references.mjs - deterministic Section 21 References table for an
3
- // /multi-agent:analysis document (Locked 34).
3
+ // /multi-agent:analysis document (Locked 33).
4
4
  //
5
5
  // The references table used to be prose the model filled in. That lists what an
6
6
  // author remembers consulting, which is a different set from what the run
@@ -364,7 +364,7 @@ function main() {
364
364
  const problems = check(spec, docPath, lang);
365
365
  if (problems.length) {
366
366
  for (const p of problems) process.stderr.write(`${p}\n`);
367
- process.stderr.write(`\n${problems.length} references coverage failure(s)\n`);
367
+ process.stderr.write(`\n${problems.length} references coverage failure(s) (Locked 33)\n`);
368
368
  process.exitCode = 1;
369
369
  return;
370
370
  }
@@ -43,7 +43,7 @@ while [ $# -gt 0 ]; do
43
43
  --file) FILE="${2:?--file needs a value}"; shift 2 ;;
44
44
  --question) QUESTION="${2:?--question needs a value}"; shift 2 ;;
45
45
  --phase) PHASE="${2:?--phase needs a value}"; shift 2 ;;
46
- --model) MODEL="${2:?--model needs a value}"; shift 2 ;;
46
+ --model) MODEL="${2:?--model needs a value}"; MODEL_EXPLICIT=1; shift 2 ;;
47
47
  --timeout) TIMEOUT="${2:?--timeout needs a value}"; shift 2 ;;
48
48
  --json) AS_JSON=1; shift ;;
49
49
  -h|--help) sed -n '2,30p' "$0"; exit 0 ;;
@@ -90,6 +90,15 @@ if command -v jq >/dev/null 2>&1; then
90
90
  done
91
91
  fi
92
92
  [ -n "$MODEL" ] || MODEL="${PREF_MODEL:-haiku}"
93
+
94
+ # bulk-read is one of the two call sites the pipeline makes itself, so routing
95
+ # may send it outside the Anthropic ladder - unlike a subagent, nothing here
96
+ # belongs to the host. An explicit --model still wins: a caller that named a
97
+ # rung asked for that rung.
98
+ if [ -z "${MODEL_EXPLICIT:-}" ] && [ -x "$HERE/../lib/model-dispatch.sh" ]; then
99
+ MODEL=$("$HERE/../lib/model-dispatch.sh" bulk-read \
100
+ --phase "$PHASE" --default "$MODEL" 2>/dev/null) || MODEL="${PREF_MODEL:-haiku}"
101
+ fi
93
102
  case "$TIMEOUT" in "" ) TIMEOUT="$PREF_TIMEOUT" ;; esac
94
103
  case "$TIMEOUT" in ""|*[!0-9]*|0) TIMEOUT=60 ;; esac
95
104