@mmerterden/multi-agent-pipeline 12.4.0 → 12.6.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 (70) hide show
  1. package/CHANGELOG.md +169 -0
  2. package/README.md +1 -1
  3. package/docs/features.md +20 -0
  4. package/index.js +7 -1
  5. package/install/_dev-only-files.mjs +1 -0
  6. package/package.json +4 -3
  7. package/pipeline/agents/security-auditor.md +1 -1
  8. package/pipeline/commands/archive-guard.md +5 -5
  9. package/pipeline/commands/multi-agent/SKILL.md +2 -0
  10. package/pipeline/commands/multi-agent/design-check/SKILL.md +287 -0
  11. package/pipeline/commands/multi-agent/help/SKILL.md +45 -5
  12. package/pipeline/commands/multi-agent/refactor/SKILL.md +92 -12
  13. package/pipeline/commands/multi-agent/review/SKILL.md +1 -1
  14. package/pipeline/commands/multi-agent/sync/SKILL.md +119 -12
  15. package/pipeline/commands/multi-agent/test/SKILL.md +1 -1
  16. package/pipeline/commands/sim-test.md +5 -5
  17. package/pipeline/lib/credential-store.sh +32 -0
  18. package/pipeline/lib/extract-conventions.sh +1 -0
  19. package/pipeline/lib/repo-cache.sh +1 -0
  20. package/pipeline/lib/shadow-git.sh +8 -0
  21. package/pipeline/multi-agent-refs/cross-cli-contract.md +3 -3
  22. package/pipeline/multi-agent-refs/phases/phase-0-init.md +9 -1
  23. package/pipeline/multi-agent-refs/phases/phase-3-dev.md +2 -2
  24. package/pipeline/multi-agent-refs/phases/phase-4-review.md +3 -2
  25. package/pipeline/multi-agent-refs/phases/phase-5-test.md +14 -4
  26. package/pipeline/multi-agent-refs/phases.md +2 -0
  27. package/pipeline/multi-agent-refs/prompt-assembly.md +31 -0
  28. package/pipeline/preferences-template.json +18 -1
  29. package/pipeline/schemas/agent-state.schema.json +90 -0
  30. package/pipeline/schemas/design-check-config.schema.json +162 -0
  31. package/pipeline/schemas/learnings-ledger.schema.json +4 -0
  32. package/pipeline/schemas/migrations/state-2.0.0-to-2.1.0.mjs +30 -12
  33. package/pipeline/schemas/prefs.schema.json +161 -5
  34. package/pipeline/schemas/token-budget.json +2 -2
  35. package/pipeline/schemas/triage-corpus.schema.json +5 -1
  36. package/pipeline/scripts/README.md +7 -5
  37. package/pipeline/scripts/classify-plan-safety.mjs +8 -3
  38. package/pipeline/scripts/cost-budget-check.mjs +9 -5
  39. package/pipeline/scripts/eval-mine-corpus.mjs +19 -5
  40. package/pipeline/scripts/fixtures/install-layout.tsv +7 -7
  41. package/pipeline/scripts/gc-worktrees.sh +23 -1
  42. package/pipeline/scripts/learning-curve.mjs +167 -0
  43. package/pipeline/scripts/learnings-ledger.mjs +27 -2
  44. package/pipeline/scripts/lint-mcp-refs.mjs +207 -0
  45. package/pipeline/scripts/memory-load.sh +5 -1
  46. package/pipeline/scripts/render-work-summary.sh +4 -1
  47. package/pipeline/scripts/repo-map.mjs +1 -1
  48. package/pipeline/scripts/smoke-command-inventory.sh +81 -0
  49. package/pipeline/scripts/smoke-commands-skills-parity.sh +1 -1
  50. package/pipeline/scripts/smoke-compliance-skills.sh +4 -4
  51. package/pipeline/scripts/smoke-cross-cli-behavior.sh +12 -2
  52. package/pipeline/scripts/smoke-generate-issue.sh +6 -5
  53. package/pipeline/scripts/smoke-learning-curve.sh +61 -0
  54. package/pipeline/scripts/smoke-per-repo-memory.sh +2 -2
  55. package/pipeline/scripts/smoke-review-readiness.sh +3 -2
  56. package/pipeline/scripts/smoke-schema-validation.sh +19 -5
  57. package/pipeline/scripts/smoke-shadow-git.sh +4 -2
  58. package/pipeline/scripts/triage-memory.mjs +66 -10
  59. package/pipeline/scripts/uninstall.mjs +1 -1
  60. package/pipeline/skills/.skill-manifest.json +24 -8
  61. package/pipeline/skills/.skills-index.json +39 -3
  62. package/pipeline/skills/shared/README.md +10 -6
  63. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +10 -8
  64. package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +248 -0
  65. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +22 -0
  66. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +67 -11
  67. package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +1 -1
  68. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +51 -9
  69. package/pipeline/skills/shared/core/multi-agent-test/SKILL.md +1 -1
  70. package/pipeline/skills/skills-index.md +8 -4
@@ -121,6 +121,96 @@
121
121
  "default": false,
122
122
  "description": "A Figma / skill instruction file is steering phases 3-6."
123
123
  },
124
+ "mode": {
125
+ "type": ["string", "null"],
126
+ "description": "Pipeline mode for this run (e.g. 'dev', 'local', 'design-check'). Absent = full pipeline."
127
+ },
128
+ "designCheck": {
129
+ "type": ["object", "null"],
130
+ "additionalProperties": true,
131
+ "description": "State for a /multi-agent:design-check run. Populated in Phase 0 (mock feasibility, scenario inventory, scope), Phase 2 (Figma variants) and Phase 3 (captures, skips, findings).",
132
+ "properties": {
133
+ "module": { "type": "string", "description": "Module / submodule path being audited." },
134
+ "platform": { "type": "string", "enum": ["ios", "android"] },
135
+ "mock": {
136
+ "type": "object",
137
+ "additionalProperties": true,
138
+ "description": "design_mock_detect result.",
139
+ "properties": {
140
+ "supported": { "description": "true | false | 'debug-only'" },
141
+ "mechanism": { "type": ["string", "null"] },
142
+ "activation": { "type": ["object", "null"] },
143
+ "variantsHint": { "type": "array", "items": { "type": "string" } }
144
+ }
145
+ },
146
+ "inventory": {
147
+ "type": ["object", "null"],
148
+ "additionalProperties": true,
149
+ "description": "design_scenario_inventory result, persisted verbatim. THIS is the audit's target set - Phase 3 iterates it and Phase 4 gates on it. Never re-derive it by reading the repo.",
150
+ "properties": {
151
+ "targetCount": { "type": "integer" },
152
+ "targets": {
153
+ "type": "array",
154
+ "items": { "type": "object", "additionalProperties": true },
155
+ "description": "{ id, kind, label, screen, driver, evidence } per state driver (launch-arg / scenario-case / code-scenario / fixture / deep-link)."
156
+ },
157
+ "groups": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
158
+ "byKind": { "type": "object", "additionalProperties": true },
159
+ "truncated": { "type": "boolean" }
160
+ }
161
+ },
162
+ "scope": {
163
+ "type": ["object", "null"],
164
+ "additionalProperties": true,
165
+ "description": "Resolved run scope: the inventory targets this run must audit. The coverage gate applies to this set, not to the whole inventory - a scoped run is not penalised for out-of-scope targets.",
166
+ "properties": {
167
+ "argument": { "type": ["string", "null"], "description": "Raw $ARGUMENTS as given (empty = whole module, '--resume' = remainder of the last run)." },
168
+ "targetIds": { "type": "array", "items": { "type": "string" } },
169
+ "resumedFrom": { "type": ["string", "null"], "description": "Report dir of the run this scope resumes, when --resume was used." }
170
+ }
171
+ },
172
+ "figmaUrl": { "type": ["string", "null"] },
173
+ "variants": {
174
+ "type": "array",
175
+ "description": "Mapped variants with their Figma spec, capture, and findings.",
176
+ "items": { "type": "object", "additionalProperties": true }
177
+ },
178
+ "captured": {
179
+ "type": "array",
180
+ "description": "Every capture taken, appended and persisted as it happens so a dying run is resumable. One entry per screenshot, keyed to the target that produced it ('<targetId>', or '<targetId>#<sub-label>' for tap-reachable sub-states).",
181
+ "items": { "type": "object", "additionalProperties": true }
182
+ },
183
+ "skipped": {
184
+ "type": "array",
185
+ "description": "Targets that could not be audited, each with a CONCRETE reason. 'Requires a scenario / prefix / launch-arg' is not a reason - that is the work Phase 3 exists to do. A skip without a reason fails the coverage gate.",
186
+ "items": {
187
+ "type": "object",
188
+ "additionalProperties": true,
189
+ "required": ["id", "reason"],
190
+ "properties": {
191
+ "id": { "type": "string" },
192
+ "group": { "type": ["string", "null"] },
193
+ "reason": { "type": "string", "minLength": 1 }
194
+ }
195
+ }
196
+ },
197
+ "coverage": {
198
+ "type": ["object", "null"],
199
+ "additionalProperties": true,
200
+ "description": "design_report coverage verdict for this run. gate='fail' means the run is INCOMPLETE and must be reported as such, with the unaccounted ids and the --resume command.",
201
+ "properties": {
202
+ "gate": { "type": "string", "enum": ["pass", "fail"] },
203
+ "audited": { "type": "integer" },
204
+ "target": { "type": "integer" },
205
+ "pct": { "type": "number" },
206
+ "skippedWithReason": { "type": "integer" },
207
+ "unaccounted": { "type": "integer" },
208
+ "unaccountedIds": { "type": "array", "items": { "type": "string" } }
209
+ }
210
+ },
211
+ "reportDir": { "type": ["string", "null"], "description": "Output dir under ~/DesignChecks." }
212
+ }
213
+ },
124
214
  "identity": {
125
215
  "type": "object",
126
216
  "additionalProperties": false,
@@ -0,0 +1,162 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/mmerterden/multi-agent-pipeline/pipeline/schemas/design-check-config.schema.json",
4
+ "title": "Design Check Configuration",
5
+ "description": "Optional per-project config for /multi-agent:design-check. Stored at ~/.claude/projects/{slug}/design-check-config.json. Never committed. Everything here is optional: design_mock_detect auto-detects mock support and design_scenario_inventory auto-enumerates state drivers at run time; this file only overrides or augments that detection (extra mock switch keys, extra launch args / targets no source signal reveals, explicit variant->Figma-node mappings, status-bar crop, coverage floor, Confluence toggle). Keeps the engine generic — project specifics live here, never in shipped code.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schemaVersion"],
9
+ "properties": {
10
+ "schemaVersion": { "type": "string", "const": "1.0.0" },
11
+ "platform": {
12
+ "type": "string",
13
+ "enum": ["ios", "android"],
14
+ "description": "Override auto platform detection."
15
+ },
16
+ "mock": {
17
+ "type": "object",
18
+ "additionalProperties": false,
19
+ "description": "Mock-mode overrides. Omit to rely purely on auto-detection.",
20
+ "properties": {
21
+ "keys": {
22
+ "type": "array",
23
+ "items": { "type": "string" },
24
+ "description": "Extra UserDefaults / launch-arg switch keys to look for (passed as extra_keys to design_mock_detect)."
25
+ },
26
+ "launchArg": {
27
+ "type": "string",
28
+ "description": "Explicit iOS launch argument to force mock mode, e.g. '-debugMockMode YES'. Overrides the detected one."
29
+ },
30
+ "intentExtra": {
31
+ "type": "string",
32
+ "description": "Explicit Android intent extra, e.g. '--ez mockEnabled true'."
33
+ },
34
+ "buildConfig": {
35
+ "type": "string",
36
+ "description": "Build configuration that compiles the mocks (default 'Debug')."
37
+ }
38
+ }
39
+ },
40
+ "inventory": {
41
+ "type": "object",
42
+ "additionalProperties": false,
43
+ "description": "Augments design_scenario_inventory, which decides the audit's target set. Only for states no source signal can reveal — everything detectable is detected.",
44
+ "properties": {
45
+ "extraLaunchArgs": {
46
+ "type": "array",
47
+ "items": { "type": "string" },
48
+ "description": "Launch args (iOS) / boolean intent-extra keys (Android) that drive a distinct state but appear in no scanned literal."
49
+ },
50
+ "extraTargets": {
51
+ "type": "array",
52
+ "description": "Fully hand-declared targets, for states reachable only by a documented manual step.",
53
+ "items": {
54
+ "type": "object",
55
+ "additionalProperties": false,
56
+ "required": ["id"],
57
+ "properties": {
58
+ "id": { "type": "string", "description": "Stable target id, e.g. 'config:seatmap-3d'." },
59
+ "kind": { "type": "string", "description": "Target kind label (default 'config')." },
60
+ "label": { "type": "string" },
61
+ "screen": { "type": "string", "description": "Screen this target belongs to (groups the coverage report)." },
62
+ "driver": {
63
+ "type": "object",
64
+ "additionalProperties": true,
65
+ "description": "How to reach it: { type: 'manual', note } or any driver shape the runner understands."
66
+ }
67
+ }
68
+ }
69
+ },
70
+ "ignoreTargets": {
71
+ "type": "array",
72
+ "items": { "type": "string" },
73
+ "description": "Target ids to drop from the inventory entirely (dead drivers, states retired in code but still referenced). Unlike a skip, these never enter the coverage gate — use sparingly and prefer a skip with a reason."
74
+ }
75
+ }
76
+ },
77
+ "coverage": {
78
+ "type": "object",
79
+ "additionalProperties": false,
80
+ "description": "Coverage gate tuning. The gate itself is not optional: a target is audited, or skipped with an explicit reason.",
81
+ "properties": {
82
+ "floor": {
83
+ "type": "number",
84
+ "minimum": 0,
85
+ "maximum": 1,
86
+ "description": "Additional hard floor on audited/target ratio (e.g. 0.9). Below it the gate fails even when every skip carries a reason."
87
+ },
88
+ "confirmAbove": {
89
+ "type": "integer",
90
+ "default": 25,
91
+ "description": "Relaunch count (inventory.relaunchCount for the scoped set, NOT the target count) above which Phase 0 asks for confirmation. Relaunches are the run's real cost: one relaunch serves every in-app target on that screen. Whole-module remains the intended default — this threshold estimates cost, it does not invite shrinking the audit."
92
+ }
93
+ }
94
+ },
95
+ "figma": {
96
+ "type": "object",
97
+ "additionalProperties": false,
98
+ "description": "Figma frame + variant mapping overrides.",
99
+ "properties": {
100
+ "url": { "type": "string", "description": "Default Figma MCP URL for this module's frame/page." },
101
+ "frameSize": {
102
+ "type": "object",
103
+ "additionalProperties": false,
104
+ "properties": { "w": { "type": "number" }, "h": { "type": "number" } },
105
+ "description": "Figma frame size the spec coordinates are in (usually auto-read)."
106
+ }
107
+ }
108
+ },
109
+ "variants": {
110
+ "type": "array",
111
+ "description": "Explicit mock-variant -> Figma-node mappings. When present, the picker skips name-similarity guessing for these.",
112
+ "items": {
113
+ "type": "object",
114
+ "additionalProperties": false,
115
+ "required": ["id", "figmaNodeId"],
116
+ "properties": {
117
+ "id": { "type": "string", "description": "Mock variant id (matches a variantsHint / fixture name)." },
118
+ "label": { "type": "string" },
119
+ "figmaNodeId": { "type": "string", "description": "Figma node id for this variant's frame." },
120
+ "activation": {
121
+ "type": "object",
122
+ "additionalProperties": true,
123
+ "description": "How to put the app into this variant (launch arg / UserDefaults values / navigation path)."
124
+ }
125
+ }
126
+ }
127
+ },
128
+ "capture": {
129
+ "type": "object",
130
+ "additionalProperties": false,
131
+ "description": "Capture / comparison tuning.",
132
+ "properties": {
133
+ "cropTopLive": { "type": "number", "description": "Pixels to crop from the top of the live capture (status bar)." },
134
+ "tolerancePx": { "type": "number", "default": 2 },
135
+ "colorTolerance": { "type": "number", "default": 3 },
136
+ "maxDiffPct": { "type": "number", "default": 1.0 }
137
+ }
138
+ },
139
+ "report": {
140
+ "type": "object",
141
+ "additionalProperties": false,
142
+ "description": "Report output + export.",
143
+ "properties": {
144
+ "outputRoot": { "type": "string", "description": "Report root dir (default ~/DesignChecks)." },
145
+ "formats": {
146
+ "type": "array",
147
+ "items": { "type": "string", "enum": ["html", "pdf", "confluence"] },
148
+ "default": ["html", "pdf"]
149
+ },
150
+ "confluence": {
151
+ "type": "object",
152
+ "additionalProperties": false,
153
+ "properties": {
154
+ "enabled": { "type": "boolean", "default": false },
155
+ "space": { "type": "string" },
156
+ "parentId": { "type": "string" }
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+ }
@@ -34,6 +34,10 @@
34
34
  "type": "string",
35
35
  "enum": ["low", "medium", "high"],
36
36
  "description": "How strongly to weight the entry when replaying it."
37
+ },
38
+ "diagnosis": {
39
+ "type": "string",
40
+ "description": "Optional causal 'why' behind the lesson (Reflexion): the root-cause reasoning that turns a detected failure into a prevented one. Rendered in the brief as '(why: ...)'. Absent for plain facts/conventions."
37
41
  }
38
42
  }
39
43
  }
@@ -12,22 +12,40 @@ export default function migrate(data) {
12
12
 
13
13
  out.schemaVersion = "2.1.0";
14
14
 
15
- // If projects[] doesn't exist and scalar fields do, create projects[0] from scalars
16
- if (!Array.isArray(out.projects) && out.project) {
17
- out.projects = [{
18
- name: out.project,
19
- root: out.projectRoot || null,
20
- worktreePath: out.worktreePath || null,
21
- branch: out.branch || null,
22
- baseBranch: out.baseBranch || null,
23
- identity: out.identity || null,
24
- platform: out.remoteType || null,
25
- baseFetchStatus: "fresh",
15
+ // If projects[] doesn't exist and the scalar mirrors do, create projects[0]
16
+ // from them. The item schema sets additionalProperties:false and requires
17
+ // project/projectRoot/branch/baseBranch/identity, so only emit keys the
18
+ // schema declares and only build the entry when every required key is
19
+ // populated - a partial entry would be schema-invalid, which is worse than
20
+ // no entry at all.
21
+ const canMirror =
22
+ !Array.isArray(out.projects) &&
23
+ typeof out.project === "string" &&
24
+ typeof out.projectRoot === "string" &&
25
+ typeof out.branch === "string" &&
26
+ typeof out.baseBranch === "string" &&
27
+ out.identity !== null &&
28
+ typeof out.identity === "object";
29
+
30
+ if (canMirror) {
31
+ const entry = {
32
+ project: out.project,
33
+ projectRoot: out.projectRoot,
34
+ branch: out.branch,
35
+ baseBranch: out.baseBranch,
36
+ identity: out.identity,
37
+ worktreePath: out.worktreePath ?? null,
26
38
  commit: null,
27
39
  pr: null,
28
40
  pushAttempts: 0,
29
41
  buildStatus: null,
30
- }];
42
+ };
43
+ // `platform` and `remoteType` are non-nullable enums: omit rather than null.
44
+ if (typeof out.remoteType === "string") {
45
+ entry.remoteType = out.remoteType;
46
+ entry.platform = out.remoteType;
47
+ }
48
+ out.projects = [entry];
31
49
  }
32
50
 
33
51
  return out;
@@ -5,6 +5,11 @@
5
5
  "description": "User-level preferences. Written to $HOME/.claude/multi-agent-preferences.json. Never committed. Never synced to the open-source repo \u2014 may contain personal data.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
+ "patternProperties": {
9
+ "^_": {
10
+ "description": "Documentation-only blocks shipped in preferences-template.json (_figmaConfigTemplate, _derivedSkillSourcesTemplate, _devToolkitTemplate). Copy-paste examples, never read by the pipeline."
11
+ }
12
+ },
8
13
  "required": [
9
14
  "schemaVersion",
10
15
  "global",
@@ -101,7 +106,7 @@
101
106
  "keychainMapping": {
102
107
  "type": "object",
103
108
  "additionalProperties": false,
104
- "description": "Maps logical token names to actual Keychain item names. Pipeline looks up tokens via `security find-generic-password -s <mapped-name>` (or `pipeline/lib/credential-store.sh get` cross-platform). 12 services covered: jira, bitbucket, bitbucket_token, bitbucket_user, github, confluence, figma, figma_mcp, fortify, firebase, jenkins, npm.",
109
+ "description": "Maps logical token names to actual Keychain item names. Pipeline looks up tokens via `security find-generic-password -s <mapped-name>` (or `pipeline/lib/credential-store.sh get` cross-platform). Covered keys: jira, bitbucket, bitbucket_token, bitbucket_user, github, confluence, figma, figma_pat, figma_user, figma_mcp, fortify, graylog, firebase, jenkins, npm, claude_oauth_token, claude_oauth_token_fallback.",
105
110
  "properties": {
106
111
  "jira": {
107
112
  "type": [
@@ -164,6 +169,34 @@
164
169
  "null"
165
170
  ]
166
171
  },
172
+ "figma_pat": {
173
+ "type": [
174
+ "string",
175
+ "null"
176
+ ],
177
+ "description": "Figma Personal Access Token. Tier 2 of the Figma access chain (REST API) resolves this key."
178
+ },
179
+ "figma_user": {
180
+ "type": [
181
+ "string",
182
+ "null"
183
+ ],
184
+ "description": "Figma account identifier, when the REST flow needs it alongside figma_pat."
185
+ },
186
+ "claude_oauth_token": {
187
+ "type": [
188
+ "string",
189
+ "null"
190
+ ],
191
+ "description": "Claude Code OAuth token used by headless / scheduled runs."
192
+ },
193
+ "claude_oauth_token_fallback": {
194
+ "type": [
195
+ "string",
196
+ "null"
197
+ ],
198
+ "description": "Secondary OAuth token used when the primary is rate-limited or expired."
199
+ },
167
200
  "firebase": {
168
201
  "type": [
169
202
  "string",
@@ -499,8 +532,39 @@
499
532
  }
500
533
  },
501
534
  "defaultJiraKey": {
502
- "type": "string",
503
- "description": "Default Jira project key used for placeholder replacement."
535
+ "type": [
536
+ "string",
537
+ "null"
538
+ ],
539
+ "description": "Default Jira project key used for placeholder replacement. null until /multi-agent:setup onboards a Jira project."
540
+ },
541
+ "modelFallback": {
542
+ "type": "object",
543
+ "additionalProperties": false,
544
+ "description": "Model-tier fallback policy. When the premium tier is unavailable (quota, dispatch error, or an expired premium window) the pipeline drops to fallbackModel instead of failing the phase.",
545
+ "properties": {
546
+ "enabled": {
547
+ "type": "boolean",
548
+ "default": true,
549
+ "description": "Master switch for tier fallback."
550
+ },
551
+ "premiumTierUntil": {
552
+ "type": [
553
+ "string",
554
+ "null"
555
+ ],
556
+ "description": "ISO date after which the premium tier is assumed gone. null = no expiry known."
557
+ },
558
+ "fallbackModel": {
559
+ "type": "string",
560
+ "description": "Model id used when the premium tier is unavailable."
561
+ },
562
+ "onDispatchError": {
563
+ "type": "boolean",
564
+ "default": true,
565
+ "description": "Also fall back when a dispatch call errors, not only on quota."
566
+ }
567
+ }
504
568
  },
505
569
  "hosts": {
506
570
  "type": "object",
@@ -1130,6 +1194,94 @@
1130
1194
  "default": false,
1131
1195
  "description": "v7.0.0+ \u2014 Dynamic trigger-based skill loading. When enabled, the orchestrator reads `~/.claude/skills/.skills-index.json` after Phase 1 analysis and injects only the top-N matched skills into subagent prompts (via `match-skills.mjs`), instead of relying on eager-loaded SKILL.md bodies. Large token win on small tasks; no visible behavior change on big ones where many skills match. Requires either a full install (index is always shipped) or the `install.js --index-only` mode for users who want skill bodies fetched on demand. Off by default in v7.0 \u2014 flip to `true` to opt in; a future v7.x may flip the default once enough telemetry confirms no regression."
1132
1196
  },
1197
+ "derivedSkillSources": {
1198
+ "type": "array",
1199
+ "description": "v11.3.0+. Skills/components in a project that were derived from an upstream marketplace plugin. `/multi-agent:refactor` Step 0b compares each entry's `derivedFromVersion` against the installed upstream version and reports drift (plan band D). Local-only: it may name private marketplaces, so it is never synced into the public repo. Empty array = the drift step is skipped.",
1200
+ "items": {
1201
+ "type": "object",
1202
+ "additionalProperties": false,
1203
+ "required": [
1204
+ "label",
1205
+ "localPath",
1206
+ "upstreamPlugin",
1207
+ "derivedFromVersion"
1208
+ ],
1209
+ "properties": {
1210
+ "label": {
1211
+ "type": "string",
1212
+ "description": "Human name for this derivation, shown in the drift table."
1213
+ },
1214
+ "localPath": {
1215
+ "type": "string",
1216
+ "description": "Repo-relative directory holding our derived copy."
1217
+ },
1218
+ "upstreamMarketplace": {
1219
+ "type": "string",
1220
+ "description": "Installed marketplace name, resolved under ~/.claude/plugins/cache/<marketplace>/."
1221
+ },
1222
+ "upstreamPlugin": {
1223
+ "type": "string",
1224
+ "description": "Upstream plugin name inside that marketplace."
1225
+ },
1226
+ "upstreamSkills": {
1227
+ "type": "array",
1228
+ "items": { "type": "string" },
1229
+ "description": "The upstream skill names we took."
1230
+ },
1231
+ "derivedFromVersion": {
1232
+ "type": "string",
1233
+ "description": "Upstream version we last synced from. Bumped after a drift port is applied."
1234
+ },
1235
+ "upstreamRepoUrl": {
1236
+ "type": "string",
1237
+ "description": "Used only when the marketplace is not installed locally (WebFetch fallback)."
1238
+ }
1239
+ }
1240
+ }
1241
+ },
1242
+ "devToolkit": {
1243
+ "type": "object",
1244
+ "additionalProperties": false,
1245
+ "description": "v12.6.0+. The companion MCP server that serves the pipeline's device and browser tools (dev-toolkit-mcp). Two commands read this: `/multi-agent:refactor` Step 0c researches current MCP practice and audits that repo against it (plan band E), and `/multi-agent:sync` Step 3d ships it when it moved (gates, commit, publish). When absent, both commands fall back to auto-detecting the repo from the `mcpServers` registration in ~/.claude.json / ~/.claude/settings.json, and skip silently when that fails. Never a hardcoded path.",
1246
+ "properties": {
1247
+ "enabled": {
1248
+ "type": "boolean",
1249
+ "default": true,
1250
+ "description": "Set false to skip both the refactor band-E research and the sync ship step."
1251
+ },
1252
+ "label": {
1253
+ "type": "string",
1254
+ "description": "Human name used in reports."
1255
+ },
1256
+ "localPath": {
1257
+ "type": "string",
1258
+ "description": "Working copy of the companion repo. `$HOME` and `~` are expanded. When omitted, resolved from the MCP registration (dirname of the stdio server's script)."
1259
+ },
1260
+ "mcpServerName": {
1261
+ "type": "string",
1262
+ "default": "dev-toolkit",
1263
+ "description": "Registered MCP server name, used for the auto-detect fallback and for telling the user whether a restart or a reinstall is needed after a publish."
1264
+ },
1265
+ "packageName": {
1266
+ "type": "string",
1267
+ "description": "Published package name, e.g. @<scope>/<package>. Read from the repo's package.json when omitted."
1268
+ },
1269
+ "registry": {
1270
+ "type": "string",
1271
+ "enum": [
1272
+ "github-packages",
1273
+ "npmjs",
1274
+ "none"
1275
+ ],
1276
+ "default": "github-packages",
1277
+ "description": "Publish target for Step 3d. `none` means commit + push only, never publish. The concrete URL comes from the repo's publishConfig; the token comes from the `npm` logical Keychain key through a throwaway --userconfig."
1278
+ },
1279
+ "repoUrl": {
1280
+ "type": "string",
1281
+ "description": "https remote, used in reports and when the working copy has to be cloned by the user."
1282
+ }
1283
+ }
1284
+ },
1133
1285
  "recentAccounts": {
1134
1286
  "type": "array",
1135
1287
  "description": "v7.3.0+. Last-used-first LRU of account ids (from account-resolver.sh). Used by autopilot to pick the most recent account when multiple are configured.",
@@ -1217,8 +1369,8 @@
1217
1369
  "items": {
1218
1370
  "type": "string"
1219
1371
  },
1220
- "maxItems": 10,
1221
- "description": "Bitbucket/generic default reviewers (usernames)."
1372
+ "maxItems": 40,
1373
+ "description": "Bitbucket/generic default reviewers (usernames). Real corporate reviewer groups run well past ten members."
1222
1374
  },
1223
1375
  "githubDefaultReviewers": {
1224
1376
  "type": "array",
@@ -1290,6 +1442,10 @@
1290
1442
  "format": "date-time",
1291
1443
  "description": "Timestamp of last task start against this project."
1292
1444
  },
1445
+ "componentDevWorkflow": {
1446
+ "type": "boolean",
1447
+ "description": "Project follows the component (Configuration / View / Modifiers) development workflow, so Phase 3 dispatches the component skills instead of the standard TDD flow."
1448
+ },
1293
1449
  "figmaConfigPath": {
1294
1450
  "type": "string",
1295
1451
  "description": "Path to per-project figma-config.json (for Figma pipeline projects)."
@@ -12,6 +12,6 @@
12
12
  "phase-6-commit": { "max_tokens": 6150, "warn_tokens": 5400 },
13
13
  "phase-7-report": { "max_tokens": 5600, "warn_tokens": 4950 }
14
14
  },
15
- "total_max_tokens": 50000,
16
- "note": "Token estimate = ceil(chars / 4). Per-phase budget rule: warn = current+10% (rounded to nearest 50), max = current+25%. Gives ~6 edit cycles of headroom before warn trips - intentionally quiet under normal maintenance, loud when a phase grows unusually. Only the active phase is loaded (lazy). Recalibrated at v10.0.0 after the validator/consistency/simplifier/lesson gate contracts landed in phases 1-4. Recalibrated again at v10.9.0 after the verify-by-test (Phase 4 Step 3.7), update-check (Phase 0 Step 0.6), immutable-test (Phase 3 GREEN) and redTests re-entry contracts landed - Step 3.7 prose was compressed to a pointer into refs/features/verify-by-test.md before the recalibration."
15
+ "total_max_tokens": 51000,
16
+ "note": "Token estimate = ceil(chars / 4). Per-phase budget rule: warn = current+10% (rounded to nearest 50), max = current+25%. Gives ~6 edit cycles of headroom before warn trips - intentionally quiet under normal maintenance, loud when a phase grows unusually. Only the active phase is loaded (lazy). Recalibrated at v10.0.0 after the validator/consistency/simplifier/lesson gate contracts landed in phases 1-4. Recalibrated again at v10.9.0 after the verify-by-test (Phase 4 Step 3.7), update-check (Phase 0 Step 0.6), immutable-test (Phase 3 GREEN) and redTests re-entry contracts landed - Step 3.7 prose was compressed to a pointer into refs/features/verify-by-test.md before the recalibration. Total bumped 50000 -> 51000 at v12.5.0 after the worktree residue/traversal-prune contract (Phase 0 + Phase 5 heal) and the Reflexion causal-diagnosis contract (Phase 4 lesson memory) landed; the prose was compressed first (161 tokens reclaimed) and every per-phase max still passes - only the aggregate needed room."
17
17
  }
@@ -26,6 +26,10 @@
26
26
  "issue": { "type": "string", "minLength": 1 },
27
27
  "fix": { "type": ["string", "null"] },
28
28
  "reason": { "type": ["string", "null"] },
29
- "reviewer": { "type": ["string", "null"] }
29
+ "reviewer": { "type": ["string", "null"] },
30
+ "file_sha": {
31
+ "type": ["string", "null"],
32
+ "description": "git blob SHA (git hash-object) of `file` at ingest time. On query, a differing current hash flags the hit stale (freshness gate). Null when not computable (file gone / not a git repo)."
33
+ }
30
34
  }
31
35
  }
@@ -1,10 +1,10 @@
1
1
  # Pipeline Scripts - Category Index
2
2
 
3
- The `pipeline/scripts/` directory holds 59 shell scripts + 12 Node.js `.mjs` scripts in a flat layout. v6.0.0 evaluated moving them into `smoke/`, `hooks/`, `runtime/` subdirs and decided against it: each path change would need to propagate through `package.json` globs, `install.js` deploy logic, npm scripts, sibling script calls, and CI workflow references - a 40+ file touch radius with high regression risk for minor polish value.
3
+ The `pipeline/scripts/` directory holds 148 shell scripts + 45 Node.js `.mjs` scripts in a flat layout. v6.0.0 evaluated moving them into `smoke/`, `hooks/`, `runtime/` subdirs and decided against it: each path change would need to propagate through `package.json` globs, `install.js` deploy logic, npm scripts, sibling script calls, and CI workflow references - a 40+ file touch radius with high regression risk for minor polish value.
4
4
 
5
- Instead, this README is the category index. Use it to navigate.
5
+ Instead, this README is a curated category index of the key scripts, not an exhaustive listing. Use it to navigate.
6
6
 
7
- ## Smoke scripts (42 files - `smoke-*.sh`)
7
+ ## Smoke scripts (118 files - `smoke-*.sh`)
8
8
 
9
9
  Validate contracts. Each emits `══ <name> smoke: N passed, M failed ══` on completion (the `smoke-personal-data.sh` + `smoke-token-budget.sh` suites use `PASS:` / `warnings:` variants). Run via `npm test` (chained) or individually.
10
10
 
@@ -57,6 +57,7 @@ Validate contracts. Each emits `══ <name> smoke: N passed, M failed ══`
57
57
  - `smoke-search.sh` - `/multi-agent:search` log search + smart ranking
58
58
  - `smoke-subagent-validators.sh` - Phase 1/2 validator schemas
59
59
  - `smoke-telemetry.sh` - opt-in telemetry contract
60
+ - `smoke-learning-curve.sh` - `learning-curve.mjs` metrics-trend contract
60
61
  - `smoke-validator-contradiction.sh` - Phase 4 approved/blocking contradiction detection
61
62
  - `smoke-pre-commit.sh` - pre-commit hook integration
62
63
 
@@ -81,12 +82,13 @@ Shell scripts invoked during pipeline execution.
81
82
  - `github-ssh-setup.sh` - first-run SSH key setup helper
82
83
  - `sync-parity-check.sh` - manual parity verification helper
83
84
 
84
- ## Node.js helpers (12 `.mjs` files)
85
+ ## Node.js helpers (45 `.mjs` files)
85
86
 
86
87
  - `aggregate-metrics.mjs` - collects telemetry aggregates
88
+ - `learning-curve.mjs` - time-bucketed trend over `metrics.jsonl` (first-pass clean rate, review cycles, rework/task, tokens/task, cache ratio); `--bucket`, `--since`, `--json`, `--markdown`
87
89
  - `eval-triage.mjs` - runs the 10 triage eval fixtures
88
90
  - `gen-skills-index.mjs` - regenerates `pipeline/skills/shared/README.md` skill catalog
89
- - `migrate-prefs.mjs` - preference migration driver (v2.0.0 v2.1.0 → v2.2.0)
91
+ - `migrate-prefs.mjs` - preference migration driver (v2.0.0 through v2.4.0)
90
92
  - `migrate-state.mjs` - generic migration runner (reads `schemas/migrations/`)
91
93
  - `token-budget-report.mjs` - per-phase token usage report
92
94
  - `validate-analysis.mjs`, `validate-planning.mjs`, `validate-reviewer.mjs`, `validate-triage.mjs` - per-phase output validators
@@ -34,8 +34,10 @@
34
34
  // - infrastructure 20 - touches Dockerfile, CI yaml, Gradle wrapper,
35
35
  // package.json resolutions / engines, install.js
36
36
  //
37
- // recommendPause = true when score >= 50 (tuned: any two medium signals or
38
- // any one heavy signal flips it).
37
+ // recommendPause = true when any one heavy signal (weight >= 30) fires, OR two
38
+ // or more medium-and-above signals (weight >= 20) fire, OR the accumulated
39
+ // score reaches 50. A score threshold alone cannot express the first two
40
+ // clauses: the heaviest single rule is 35 and two medium signals can total 45.
39
41
  //
40
42
  // Exit codes: 0 = verdict produced, 1 = malformed input, 2 = usage error.
41
43
 
@@ -168,7 +170,10 @@ if (infraHits.length > 0) {
168
170
 
169
171
  // --- aggregate ---
170
172
  const score = reasons.reduce((s, r) => s + r.weight, 0);
171
- const recommendPause = score >= 50;
173
+ const heavyHits = reasons.filter((r) => r.weight >= 30);
174
+ const mediumOrAboveHits = reasons.filter((r) => r.weight >= 20);
175
+ const recommendPause =
176
+ heavyHits.length >= 1 || mediumOrAboveHits.length >= 2 || score >= 50;
172
177
 
173
178
  let summary;
174
179
  if (recommendPause) {