@mmerterden/multi-agent-pipeline 20.23.2 → 20.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/README.md +8 -6
- package/README.tr.md +7 -5
- package/docs/architecture.md +3 -3
- package/docs/ecosystem.md +5 -5
- package/docs/facts.json +7 -6
- package/docs/features.md +9 -4
- package/install/catalog-history.json +1 -1
- package/install/templates/claude-hooks.json +14 -1
- package/install/templates/codex-hooks.json +18 -0
- package/manifest.json +126 -98
- package/package.json +1 -1
- package/pipeline/agents/task-clarifier.md +1 -1
- package/pipeline/commands/multi-agent/analysis-jira/SKILL.md +4 -2
- package/pipeline/commands/multi-agent/deps/SKILL.md +61 -0
- package/pipeline/commands/multi-agent/model/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/postmortem/SKILL.md +90 -0
- package/pipeline/commands/multi-agent/review/SKILL.md +5 -5
- package/pipeline/commands/multi-agent/route-on/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/setup/SKILL.md +7 -5
- package/pipeline/commands/multi-agent/sync/SKILL.md +4 -4
- package/pipeline/commands/multi-agent/testflight-validation/SKILL.md +2 -3
- package/pipeline/contract/fixtures/runs-failed.json +1 -1
- package/pipeline/contract/fixtures/runs-old-schema.json +1 -1
- package/pipeline/contract/fixtures/runs-pr-opened-redacted.json +1 -1
- package/pipeline/contract/fixtures/runs-pr-opened.json +1 -1
- package/pipeline/contract/fixtures/runs-running.json +1 -1
- package/pipeline/lib/analysis-jira-write.sh +70 -0
- package/pipeline/lib/credential-inventory.sh +5 -5
- package/pipeline/lib/decisions.mjs +130 -0
- package/pipeline/lib/fetch-crashlytics.sh +6 -8
- package/pipeline/lib/model-rung.sh +1 -1
- package/pipeline/lib/plan-todos.sh +177 -37
- package/pipeline/lib/post-pr-review.sh +4 -6
- package/pipeline/lib/route-state.sh +1 -1
- package/pipeline/lib/shadow-git.sh +4 -5
- package/pipeline/lib/task-graph.mjs +446 -0
- package/pipeline/lib/tdd-evidence.mjs +579 -0
- package/pipeline/multi-agent-refs/analysis-template-corporate.md +2 -0
- package/pipeline/multi-agent-refs/analysis-template.md +4 -2
- package/pipeline/multi-agent-refs/cross-cli-contract.md +10 -11
- package/pipeline/multi-agent-refs/features/analysis-jira.md +42 -0
- package/pipeline/multi-agent-refs/features/autopilot-decisions.md +34 -0
- package/pipeline/multi-agent-refs/features/cost-analysis.md +4 -0
- package/pipeline/multi-agent-refs/features/dep-bump.md +176 -0
- package/pipeline/multi-agent-refs/features/model-fallback.md +21 -14
- package/pipeline/multi-agent-refs/features/mutation-probe.md +58 -0
- package/pipeline/multi-agent-refs/features/plan-checks.md +67 -0
- package/pipeline/multi-agent-refs/features/plan-todos.md +40 -1
- package/pipeline/multi-agent-refs/features/rework-escalation.md +49 -0
- package/pipeline/multi-agent-refs/features/scope-check.md +1 -0
- package/pipeline/multi-agent-refs/help/en.md +2 -0
- package/pipeline/multi-agent-refs/help/tr.md +2 -0
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-1-plan.md +2 -3
- package/pipeline/multi-agent-refs/phases/phase-2-dev.md +7 -7
- package/pipeline/multi-agent-refs/phases/phase-3-review.md +18 -16
- package/pipeline/multi-agent-refs/phases/phase-5-report.md +3 -3
- package/pipeline/multi-agent-refs/picker-contract.md +3 -2
- package/pipeline/multi-agent-refs/response-shape.md +65 -0
- package/pipeline/multi-agent-refs/tracker-contract.md +6 -6
- package/pipeline/rules/response-shape.md +18 -0
- package/pipeline/schemas/agent-state.schema.json +36 -1
- package/pipeline/schemas/phases.json +4 -0
- package/pipeline/schemas/plan-todos.schema.json +104 -3
- package/pipeline/schemas/planning-output.schema.json +46 -0
- package/pipeline/schemas/prefs.schema.json +122 -3
- package/pipeline/scripts/_code-graph.mjs +2 -2
- package/pipeline/scripts/_jsonl.mjs +151 -0
- package/pipeline/scripts/_plan-overlap.mjs +231 -0
- package/pipeline/scripts/_redact.mjs +124 -10
- package/pipeline/scripts/_transcript-hosts.mjs +406 -0
- package/pipeline/scripts/analysis-story-tree.mjs +153 -8
- package/pipeline/scripts/autopilot-publish.mjs +12 -1
- package/pipeline/scripts/build-stack-plugins.mjs +3 -3
- package/pipeline/scripts/capture-flush.sh +7 -7
- package/pipeline/scripts/code-graph-rules/android.json +1 -1
- package/pipeline/scripts/cost-analyze.mjs +32 -43
- package/pipeline/scripts/cost-table.json +35 -19
- package/pipeline/scripts/decisions-log.mjs +118 -0
- package/pipeline/scripts/dep-bump.mjs +1556 -0
- package/pipeline/scripts/edit-syntax-gate.mjs +362 -0
- package/pipeline/scripts/edit-syntax-gate.sh +32 -0
- package/pipeline/scripts/find-polluter.mjs +281 -0
- package/pipeline/scripts/gate-ledger.mjs +46 -13
- package/pipeline/scripts/gc-abandoned.sh +2 -2
- package/pipeline/scripts/gen-mode-dispatch.mjs +3 -3
- package/pipeline/scripts/git-path.mjs +1 -2
- package/pipeline/scripts/host-hooks.mjs +265 -0
- package/pipeline/scripts/jira-attach.sh +1 -1
- package/pipeline/scripts/keychain.py +3 -3
- package/pipeline/scripts/mutation-probe.mjs +484 -0
- package/pipeline/scripts/output-quality-check.sh +1 -1
- package/pipeline/scripts/phase-tracker.sh +5 -6
- package/pipeline/scripts/phase0-exit-gate.mjs +3 -3
- package/pipeline/scripts/plan-complexity.mjs +259 -0
- package/pipeline/scripts/pre-push-check.sh +3 -6
- package/pipeline/scripts/review-decision-gate.mjs +33 -1
- package/pipeline/scripts/rework-escalation.mjs +264 -0
- package/pipeline/scripts/test-strength.mjs +5 -3
- package/pipeline/scripts/transcript-index.mjs +1131 -0
- package/pipeline/scripts/uninstall.mjs +90 -4
- package/pipeline/scripts/update-issue-progress.sh +4 -6
- package/pipeline/scripts/usage-report.mjs +2 -2
- package/pipeline/scripts/validate-planning.mjs +48 -6
- package/pipeline/skills/.skill-manifest.json +20 -12
- package/pipeline/skills/.skills-index.json +23 -1
- package/pipeline/skills/shared/README.md +5 -3
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +6 -6
- package/pipeline/skills/shared/core/multi-agent-analysis-jira/SKILL.md +4 -2
- package/pipeline/skills/shared/core/multi-agent-deps/SKILL.md +60 -0
- package/pipeline/skills/shared/core/multi-agent-model/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-postmortem/SKILL.md +69 -0
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +5 -5
- package/pipeline/skills/shared/core/multi-agent-route-on/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +7 -5
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +4 -4
- package/pipeline/skills/shared/core/multi-agent-testflight-validation/SKILL.md +2 -3
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +15 -0
- package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +13 -0
- package/pipeline/skills/shared/external/debugging-strategies/SKILL.md +8 -0
- package/pipeline/skills/shared/external/ios-coding-standard/references/rules.yml +2 -2
- package/pipeline/skills/skills-index.md +3 -1
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"properties": {
|
|
28
28
|
"id": {
|
|
29
29
|
"type": "string",
|
|
30
|
-
"pattern": "^
|
|
31
|
-
"description": "Stable identifier
|
|
30
|
+
"pattern": "^[tT][0-9]+$",
|
|
31
|
+
"description": "Stable identifier: t1, t2 when written directly, T1, T2 when converted from a planning-output document. Used in dependency edges and progress lines."
|
|
32
32
|
},
|
|
33
33
|
"task": {
|
|
34
34
|
"type": "string",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"type": "array",
|
|
44
44
|
"default": [],
|
|
45
45
|
"description": "Array of todo IDs that must reach `completed` (or `skipped`) before this one can move to `in_progress`. Empty for parallelizable steps.",
|
|
46
|
-
"items": { "type": "string", "pattern": "^
|
|
46
|
+
"items": { "type": "string", "pattern": "^[tT][0-9]+$" }
|
|
47
47
|
},
|
|
48
48
|
"startedAt": { "type": "string", "format": "date-time" },
|
|
49
49
|
"completedAt": { "type": "string", "format": "date-time" },
|
|
@@ -58,10 +58,111 @@
|
|
|
58
58
|
"enum": ["Reuse", "Add new", "Modify"],
|
|
59
59
|
"description": "Carried from the analysis Section 14 row this step came from (Locked 16). Phase 2 reads it as an instruction: a Reuse step binds the existing file and must not write a new one. Phase 3 checks the diff against it. Absent when the step did not originate from a Files-to-Add row."
|
|
60
60
|
},
|
|
61
|
+
"type": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"enum": ["test", "code", "refactor", "docs", "chore"],
|
|
64
|
+
"description": "Carried from the planning-output task type. A docs or chore todo is exempt from the TDD evidence check (lib/tdd-evidence.mjs)."
|
|
65
|
+
},
|
|
66
|
+
"tdd": {
|
|
67
|
+
"type": "object",
|
|
68
|
+
"additionalProperties": false,
|
|
69
|
+
"description": "TDD evidence, written by plan-todos.sh tdd-red / tdd-green / complete through lib/tdd-evidence.mjs. complete reads it according to prefs.global.tddEvidence.mode.",
|
|
70
|
+
"properties": {
|
|
71
|
+
"red": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"additionalProperties": false,
|
|
74
|
+
"required": ["at", "failed", "total"],
|
|
75
|
+
"description": "The run before the change: at least one failing test, or none when outcome is already-passing.",
|
|
76
|
+
"properties": {
|
|
77
|
+
"at": { "type": "string", "format": "date-time" },
|
|
78
|
+
"failed": { "type": "integer", "minimum": 0 },
|
|
79
|
+
"total": { "type": "integer", "minimum": 1 },
|
|
80
|
+
"tests": {
|
|
81
|
+
"type": "array",
|
|
82
|
+
"items": { "type": "string" },
|
|
83
|
+
"description": "Failing test ids the todo named; every failing id when it named none; empty when the results carry no names."
|
|
84
|
+
},
|
|
85
|
+
"testsUnverifiable": {
|
|
86
|
+
"type": "boolean",
|
|
87
|
+
"description": "The todo named tests (--tests) but the results name no failing test, so the names went unchecked. Recorded in warn and off mode; enforce refuses the run instead."
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"green": {
|
|
92
|
+
"type": "object",
|
|
93
|
+
"additionalProperties": false,
|
|
94
|
+
"required": ["at", "failed", "total"],
|
|
95
|
+
"description": "The run after the change, with no failures.",
|
|
96
|
+
"properties": {
|
|
97
|
+
"at": { "type": "string", "format": "date-time" },
|
|
98
|
+
"failed": { "const": 0 },
|
|
99
|
+
"total": { "type": "integer", "minimum": 1 }
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"outcome": {
|
|
103
|
+
"type": "string",
|
|
104
|
+
"enum": ["red", "verified", "already-passing", "exempt", "unverified"],
|
|
105
|
+
"description": "red: RED recorded. verified: RED then GREEN. already-passing: the behaviour existed and one passing run stands for both. exempt: no evidence needed. unverified: completed in warn mode without evidence; Phase 3 notes it."
|
|
106
|
+
},
|
|
107
|
+
"reason": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"description": "Why the outcome is already-passing, exempt or unverified."
|
|
110
|
+
},
|
|
111
|
+
"selfDeclared": {
|
|
112
|
+
"type": "boolean",
|
|
113
|
+
"description": "The exemption came from the todo itself (complete --no-tests), not from its type, the task type or the test policy. Phase 3 notes it."
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
61
117
|
"estimatedMinutes": {
|
|
62
118
|
"type": "integer",
|
|
63
119
|
"minimum": 1,
|
|
64
120
|
"description": "Optional Phase 1 estimate for the step. Phase 5 compares against actual duration."
|
|
121
|
+
},
|
|
122
|
+
"files": {
|
|
123
|
+
"type": "array",
|
|
124
|
+
"items": { "type": "string" },
|
|
125
|
+
"description": "The planning-output task's files[], carried by plan-todos.sh set. The drift check compares the files a step changed against it."
|
|
126
|
+
},
|
|
127
|
+
"creates": {
|
|
128
|
+
"type": "array",
|
|
129
|
+
"items": { "type": "string" },
|
|
130
|
+
"description": "The planning-output task's creates[], carried by plan-todos.sh set. The drift check counts these files as planned, like files[]."
|
|
131
|
+
},
|
|
132
|
+
"drift": {
|
|
133
|
+
"type": "object",
|
|
134
|
+
"additionalProperties": false,
|
|
135
|
+
"required": ["outside", "notDone", "at"],
|
|
136
|
+
"description": "Set by plan-todos.sh drift when this step ended off-plan: the files it changed outside files[] and the number of planned items it did not do.",
|
|
137
|
+
"properties": {
|
|
138
|
+
"outside": { "type": "array", "items": { "type": "string" } },
|
|
139
|
+
"notDone": { "type": "integer", "minimum": 0 },
|
|
140
|
+
"at": { "type": "string", "format": "date-time" }
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"needsReplan": {
|
|
144
|
+
"type": "object",
|
|
145
|
+
"additionalProperties": false,
|
|
146
|
+
"required": ["because", "at"],
|
|
147
|
+
"description": "Set by plan-todos.sh drift on every pending step that depends, directly or transitively, on a step that drifted. Until confirmedAt is set, task-graph.mjs reports the step as waiting (reason needs-replan) and plan-todos.sh start refuses it; confirm-replan sets confirmedAt.",
|
|
148
|
+
"properties": {
|
|
149
|
+
"because": {
|
|
150
|
+
"type": "string",
|
|
151
|
+
"pattern": "^[tT][0-9]+$",
|
|
152
|
+
"description": "The step that drifted."
|
|
153
|
+
},
|
|
154
|
+
"at": { "type": "string", "format": "date-time" },
|
|
155
|
+
"reason": {
|
|
156
|
+
"type": "string",
|
|
157
|
+
"description": "What the drift was: files outside the plan, planned items not done, or a stated reason."
|
|
158
|
+
},
|
|
159
|
+
"confirmedAt": {
|
|
160
|
+
"type": "string",
|
|
161
|
+
"format": "date-time",
|
|
162
|
+
"description": "When someone confirmed the step's plan still holds."
|
|
163
|
+
},
|
|
164
|
+
"note": { "type": "string", "description": "What the confirmation decided." }
|
|
165
|
+
}
|
|
65
166
|
}
|
|
66
167
|
}
|
|
67
168
|
}
|
|
@@ -50,6 +50,52 @@
|
|
|
50
50
|
"type": "array",
|
|
51
51
|
"items": { "type": "string" },
|
|
52
52
|
"description": "Analysis requirement ids this task implements or tests (BR-<slug>-NN, FG-NN). spec-consistency-gate.mjs reads them, with any id in the title or acceptance criteria, to check that every requirement has a task and a test and that every task traces to a requirement."
|
|
53
|
+
},
|
|
54
|
+
"creates": {
|
|
55
|
+
"type": "array",
|
|
56
|
+
"items": { "type": "string", "minLength": 1 },
|
|
57
|
+
"description": "Files this task brings into existence. validate-planning.mjs fails a plan in which two tasks create the same file, and warns when a task that edits a created file is not ordered after its creator."
|
|
58
|
+
},
|
|
59
|
+
"removes": {
|
|
60
|
+
"type": "array",
|
|
61
|
+
"items": { "type": "string", "minLength": 1 },
|
|
62
|
+
"description": "Files or symbols this task deletes, or renames away (the old name). A name with a '/' or a file extension is a file. validate-planning.mjs warns when another task edits, consumes or creates what this task removes."
|
|
63
|
+
},
|
|
64
|
+
"produces": {
|
|
65
|
+
"type": "array",
|
|
66
|
+
"items": { "type": "string", "minLength": 1 },
|
|
67
|
+
"description": "Interfaces or symbols this task introduces or changes the shape of. validate-planning.mjs warns when a task that consumes one is not ordered after its producer."
|
|
68
|
+
},
|
|
69
|
+
"consumes": {
|
|
70
|
+
"type": "array",
|
|
71
|
+
"items": { "type": "string", "minLength": 1 },
|
|
72
|
+
"description": "Interfaces or symbols this task calls or relies on without changing them. Paired against other tasks' produces and removes."
|
|
73
|
+
},
|
|
74
|
+
"complexity": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"additionalProperties": false,
|
|
77
|
+
"required": ["score"],
|
|
78
|
+
"description": "Grounded size of the task, written by plan-complexity.mjs --write. A task with recommendSplit is split in Phase 1 before approval.",
|
|
79
|
+
"properties": {
|
|
80
|
+
"score": { "type": "integer", "minimum": 1, "maximum": 10 },
|
|
81
|
+
"recommendSplit": {
|
|
82
|
+
"type": "boolean",
|
|
83
|
+
"description": "score is above prefs.global.planComplexity.splitAbove."
|
|
84
|
+
},
|
|
85
|
+
"factors": {
|
|
86
|
+
"type": "object",
|
|
87
|
+
"additionalProperties": false,
|
|
88
|
+
"description": "The counts the score was built from. fanIn, crossModuleEdges and notIndexed are null when no code graph was available.",
|
|
89
|
+
"properties": {
|
|
90
|
+
"files": { "type": "integer", "minimum": 0 },
|
|
91
|
+
"fanIn": { "type": ["integer", "null"], "minimum": 0 },
|
|
92
|
+
"modules": { "type": "integer", "minimum": 0 },
|
|
93
|
+
"crossModuleEdges": { "type": ["integer", "null"], "minimum": 0 },
|
|
94
|
+
"testSurface": { "type": "integer", "minimum": 0 },
|
|
95
|
+
"notIndexed": { "type": ["integer", "null"], "minimum": 0 }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
53
99
|
}
|
|
54
100
|
}
|
|
55
101
|
}
|
|
@@ -568,7 +568,7 @@
|
|
|
568
568
|
"fableEnabled": {
|
|
569
569
|
"type": "boolean",
|
|
570
570
|
"default": false,
|
|
571
|
-
"description": "Whether the fable rung is available at all on Claude Code. Ships OFF: the top rung is the most expensive thing a run can reach, and a cost control that is on by default is not a control. false makes every persona that declares preferredModel: fable dispatch on opus from the first call, with no dispatch attempt on fable and no error to recover from - a cost control, not a fallback. Claude Code only: Copilot CLI does not offer Fable
|
|
571
|
+
"description": "Whether the fable rung is available at all on Claude Code. Ships OFF: the top rung is the most expensive thing a run can reach, and a cost control that is on by default is not a control. false makes every persona that declares preferredModel: fable dispatch on opus from the first call, with no dispatch attempt on fable and no error to recover from - a cost control, not a fallback. Claude Code only: Copilot CLI does not offer Fable, and on Codex CLI the fable rung means gpt-5.6 @ xhigh, which this switch deliberately does not touch. Turning it off also collapses the Phase 3 Claude Code reviewer panel from three to two (Reviewer 1 lands on opus, which Reviewer 2 already holds), and consensus.reviewerCount records 2."
|
|
572
572
|
},
|
|
573
573
|
"onDispatchError": {
|
|
574
574
|
"type": "boolean",
|
|
@@ -577,6 +577,24 @@
|
|
|
577
577
|
}
|
|
578
578
|
}
|
|
579
579
|
},
|
|
580
|
+
"reworkEscalation": {
|
|
581
|
+
"type": "object",
|
|
582
|
+
"additionalProperties": false,
|
|
583
|
+
"description": "Rework model escalation (multi-agent-refs/features/rework-escalation.md). On the last rework round the cap allows, or when review-delta.mjs reports findings still present after a fix, scripts/rework-escalation.mjs moves that one dev dispatch one rung up the model-fallback ladder, as a fresh agent given the findings and the prior report as files. It never escalates against the cost budget (warn or exceeded), never re-upgrades a run that fell back, never reaches fable while modelFallback.fableEnabled is off, and never passes maxTier; a refusal is recorded with its reason.",
|
|
584
|
+
"properties": {
|
|
585
|
+
"enabled": {
|
|
586
|
+
"type": "boolean",
|
|
587
|
+
"default": true,
|
|
588
|
+
"description": "Master switch. On by default: it fires only on the rounds where a failure costs most, moves one rung, and stops at maxTier. false keeps every rework round on the dev dispatch's rung."
|
|
589
|
+
},
|
|
590
|
+
"maxTier": {
|
|
591
|
+
"type": "string",
|
|
592
|
+
"enum": ["fable", "opus", "sonnet", "haiku"],
|
|
593
|
+
"default": "opus",
|
|
594
|
+
"description": "Highest rung an escalation may reach. fable also needs modelFallback.fableEnabled true."
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
},
|
|
580
598
|
"modelRouting": {
|
|
581
599
|
"type": "object",
|
|
582
600
|
"description": "Policy-driven model selection. Ships DISABLED and changes no dispatch behaviour while enabled is false. Full contract and the reason `scope` has no host-session member: pipeline/schemas/route-config.schema.json. Written by /multi-agent:route-on, cleared of its on-state (not its rules) by /multi-agent:route-off, reported by /multi-agent:route-status.",
|
|
@@ -1151,6 +1169,19 @@
|
|
|
1151
1169
|
}
|
|
1152
1170
|
}
|
|
1153
1171
|
},
|
|
1172
|
+
"tddEvidence": {
|
|
1173
|
+
"type": "object",
|
|
1174
|
+
"additionalProperties": false,
|
|
1175
|
+
"description": "Whether completing a plan todo needs a recorded RED run and GREEN run (plan-todos.sh tdd-red / tdd-green). Applies where Phase 2 iterates plan todos (prefs.global.planTodos.enabled); without it Phase 2 walks tasks[] and nothing is recorded. Docs and chore todos, component tasks, test policy none and complete --no-tests are exempt and recorded with a reason.",
|
|
1176
|
+
"properties": {
|
|
1177
|
+
"mode": {
|
|
1178
|
+
"type": "string",
|
|
1179
|
+
"enum": ["off", "warn", "enforce"],
|
|
1180
|
+
"default": "warn",
|
|
1181
|
+
"description": "off: no check. warn: complete warns and records tdd.outcome unverified, which Phase 3 notes. enforce: complete refuses a todo without the evidence."
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
},
|
|
1154
1185
|
"planTodos": {
|
|
1155
1186
|
"type": "object",
|
|
1156
1187
|
"additionalProperties": false,
|
|
@@ -1163,10 +1194,24 @@
|
|
|
1163
1194
|
}
|
|
1164
1195
|
}
|
|
1165
1196
|
},
|
|
1197
|
+
"planComplexity": {
|
|
1198
|
+
"type": "object",
|
|
1199
|
+
"additionalProperties": false,
|
|
1200
|
+
"description": "Grounded complexity score per Phase 1 plan task (pipeline/scripts/plan-complexity.mjs): counted from the files a task touches, their code-graph fan-in, the modules they span and their test surface, never from the planner's opinion. Phase 1 splits a task scoring above the threshold before the plan is approved.",
|
|
1201
|
+
"properties": {
|
|
1202
|
+
"splitAbove": {
|
|
1203
|
+
"type": "integer",
|
|
1204
|
+
"minimum": 1,
|
|
1205
|
+
"maximum": 10,
|
|
1206
|
+
"default": 7,
|
|
1207
|
+
"description": "A task whose score (1-10) is above this gets recommendSplit and is split before plan approval. Lower it to get smaller tasks; 10 never splits."
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
},
|
|
1166
1211
|
"clarifyAmbiguous": {
|
|
1167
1212
|
"type": "object",
|
|
1168
1213
|
"additionalProperties": false,
|
|
1169
|
-
"description": "v8.6+ - Phase 0 Step 9 clarifying-question loop. Before Phase 1 starts, a cheap Haiku classifier scores task ambiguity (0-10) and emits up to N questions if score < threshold. Cost: ~$0.
|
|
1214
|
+
"description": "v8.6+ - Phase 0 Step 9 clarifying-question loop. Before Phase 1 starts, a cheap Haiku classifier scores task ambiguity (0-10) and emits up to N questions if score < threshold. Cost: ~$0.0004 per Haiku call. Off by default - flip on for teams burned by ambiguity-driven rework or when working on cross-team issues where the spec lives in someone else's head.",
|
|
1170
1215
|
"properties": {
|
|
1171
1216
|
"enabled": {
|
|
1172
1217
|
"type": "boolean",
|
|
@@ -1793,6 +1838,75 @@
|
|
|
1793
1838
|
"tailLines": 20
|
|
1794
1839
|
}
|
|
1795
1840
|
},
|
|
1841
|
+
"editSyntaxGate": {
|
|
1842
|
+
"type": "object",
|
|
1843
|
+
"additionalProperties": false,
|
|
1844
|
+
"description": "Parse the file an edit just wrote and tell the model when it no longer parses: swiftc -parse, node --check, python3 py_compile (bytecode to a temp dir), JSON.parse, bash -n, chosen by extension, each under a 2 s deadline. Kotlin and every other extension are skipped. The hook is pipeline/scripts/edit-syntax-gate.sh: PostToolUse on Claude Code (install/templates/claude-hooks.json) and on Codex apply_patch (codex-hooks.json, registered by host-hooks.mjs). Copilot CLI is not registered: as of 1.0.45 it discards postToolUse output. It never blocks: the message arrives as PostToolUse context and the hook exits 0. A parser that is not installed or runs past the deadline means no message.",
|
|
1845
|
+
"properties": {
|
|
1846
|
+
"enabled": {
|
|
1847
|
+
"type": "boolean",
|
|
1848
|
+
"default": false,
|
|
1849
|
+
"description": "false = the hook is a no-op. true = every edited file with a supported extension is parsed after the edit."
|
|
1850
|
+
}
|
|
1851
|
+
},
|
|
1852
|
+
"default": {
|
|
1853
|
+
"enabled": false
|
|
1854
|
+
}
|
|
1855
|
+
},
|
|
1856
|
+
"mutationProbe": {
|
|
1857
|
+
"type": "object",
|
|
1858
|
+
"additionalProperties": false,
|
|
1859
|
+
"description": "Phase 3 optional review signal: pipeline/scripts/mutation-probe.mjs makes small textual mutants (a flipped comparison, a negated boolean literal, a swapped + / -, a changed returned constant) on lines the diff added or changed, runs the test command against each in a disposable worktree, and reports every mutant the tests did not catch as a finding. The caller's checkout is never written.",
|
|
1860
|
+
"properties": {
|
|
1861
|
+
"enabled": {
|
|
1862
|
+
"type": "boolean",
|
|
1863
|
+
"default": false,
|
|
1864
|
+
"description": "false = Phase 3 skips the probe. true = Phase 3 runs it when a test command is known and merges each survivor as an important finding (the tests do not catch that change)."
|
|
1865
|
+
},
|
|
1866
|
+
"maxMutants": {
|
|
1867
|
+
"type": "integer",
|
|
1868
|
+
"minimum": 1,
|
|
1869
|
+
"maximum": 50,
|
|
1870
|
+
"default": 5,
|
|
1871
|
+
"description": "Upper bound on mutants per run. Each one costs a full test-command run, so the bound is the cost bound."
|
|
1872
|
+
},
|
|
1873
|
+
"timeoutSec": {
|
|
1874
|
+
"type": "integer",
|
|
1875
|
+
"minimum": 5,
|
|
1876
|
+
"maximum": 3600,
|
|
1877
|
+
"default": 120,
|
|
1878
|
+
"description": "Deadline for one test-command run. A mutant whose run times out is reported as timed out, not as a survivor."
|
|
1879
|
+
}
|
|
1880
|
+
},
|
|
1881
|
+
"default": {
|
|
1882
|
+
"enabled": false,
|
|
1883
|
+
"maxMutants": 5,
|
|
1884
|
+
"timeoutSec": 120
|
|
1885
|
+
}
|
|
1886
|
+
},
|
|
1887
|
+
"depBump": {
|
|
1888
|
+
"type": "object",
|
|
1889
|
+
"additionalProperties": false,
|
|
1890
|
+
"description": "/multi-agent:deps (multi-agent-refs/features/dep-bump.md): bump one dependency, build and test, fix only the breakage the bump caused, review and open a PR. pipeline/scripts/dep-bump.mjs applies the bump and reads these settings through its config command.",
|
|
1891
|
+
"properties": {
|
|
1892
|
+
"maxFixRounds": {
|
|
1893
|
+
"type": "integer",
|
|
1894
|
+
"minimum": 0,
|
|
1895
|
+
"maximum": 5,
|
|
1896
|
+
"default": 2,
|
|
1897
|
+
"description": "Fix rounds a red build or test run may take after the bump. When the cap is reached and the run is still red, the worktree is rolled back to the recorded base and the run reports what broke instead of committing. 0 means a bump that does not pass as-is is rolled back at once."
|
|
1898
|
+
},
|
|
1899
|
+
"queryRegistry": {
|
|
1900
|
+
"type": "boolean",
|
|
1901
|
+
"default": true,
|
|
1902
|
+
"description": "true = the candidate list asks the package registry for newer versions through the ecosystem's own tool (npm view, git ls-remote, Maven or PyPI metadata). A failed query is noted per package and the list falls back to the manifests, lockfiles and local caches. false = offline listing only."
|
|
1903
|
+
}
|
|
1904
|
+
},
|
|
1905
|
+
"default": {
|
|
1906
|
+
"maxFixRounds": 2,
|
|
1907
|
+
"queryRegistry": true
|
|
1908
|
+
}
|
|
1909
|
+
},
|
|
1796
1910
|
"bulkRead": {
|
|
1797
1911
|
"type": "object",
|
|
1798
1912
|
"additionalProperties": false,
|
|
@@ -1915,7 +2029,7 @@
|
|
|
1915
2029
|
},
|
|
1916
2030
|
"localRepo": {
|
|
1917
2031
|
"type": "string",
|
|
1918
|
-
"description": "Repo that localPath is relative to, e.g. $HOME/multi-agent-plugins. Without it localPath reads as relative to the current repo,
|
|
2032
|
+
"description": "Repo that localPath is relative to, e.g. $HOME/multi-agent-plugins. Without it localPath reads as relative to the current repo, so an entry can silently point at the UPSTREAM tree: nothing else in the config says which checkout holds the derived copies."
|
|
1919
2033
|
},
|
|
1920
2034
|
"upstreamMarketplace": {
|
|
1921
2035
|
"type": "string",
|
|
@@ -2276,6 +2390,11 @@
|
|
|
2276
2390
|
"type": "string",
|
|
2277
2391
|
"default": "Relates",
|
|
2278
2392
|
"description": "The link type between a story and its channel clones, by this site's name."
|
|
2393
|
+
},
|
|
2394
|
+
"dependencyLinkType": {
|
|
2395
|
+
"type": "string",
|
|
2396
|
+
"default": "Blocks",
|
|
2397
|
+
"description": "The link type written from a prerequisite story to the story that depends on it (a `Depends on:` clause in the analysis document), by this site's name. The prerequisite is the link's source."
|
|
2279
2398
|
}
|
|
2280
2399
|
}
|
|
2281
2400
|
},
|
|
@@ -212,8 +212,8 @@ const IMPORT_LINE_RE = /^[ \t]*import[ \t].*$/gm;
|
|
|
212
212
|
*
|
|
213
213
|
* Swift's submodule form - `import class DesignKit.PrimaryButton` - otherwise
|
|
214
214
|
* feeds the `class` definition pattern, which registers the MODULE name as a
|
|
215
|
-
* declared type. On a large Swift app that alone
|
|
216
|
-
*
|
|
215
|
+
* declared type. On a large Swift app that alone lifts module names into the
|
|
216
|
+
* top ten god-nodes. Imports are extracted
|
|
217
217
|
* before this runs, so nothing is lost.
|
|
218
218
|
*
|
|
219
219
|
* @param {string} stripped - output of stripCode
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file _jsonl.mjs - read a JSON-lines file one record at a time, by line number.
|
|
3
|
+
*
|
|
4
|
+
* Session transcripts are append-only logs that can run to hundreds of
|
|
5
|
+
* megabytes, are sometimes cut mid-line by a crash, and occasionally carry a
|
|
6
|
+
* record far larger than the rest (a whole file pasted into a tool result).
|
|
7
|
+
* So the reader streams, stops at a byte cap, refuses a single line over a
|
|
8
|
+
* per-line cap without parsing it, and reports every line it could not use
|
|
9
|
+
* instead of throwing. Line numbers are physical lines of the file, blank ones
|
|
10
|
+
* included, so `file:line` points at what an editor shows.
|
|
11
|
+
*
|
|
12
|
+
* Each yielded row is `{ line, record }` for a parsed object, or
|
|
13
|
+
* `{ line, reason }` for a line that was not used: `parse` (not JSON),
|
|
14
|
+
* `not-object` (JSON, but not an object), `oversized` (over the per-line cap)
|
|
15
|
+
* or `truncated` (the line the byte cap cut through). `stats` is filled in as
|
|
16
|
+
* the file is read.
|
|
17
|
+
*
|
|
18
|
+
* @module pipeline/scripts/_jsonl
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { createReadStream, statSync } from "node:fs";
|
|
22
|
+
|
|
23
|
+
export const DEFAULT_MAX_BYTES = 256 * 1024 * 1024;
|
|
24
|
+
export const DEFAULT_MAX_LINE_BYTES = 8 * 1024 * 1024;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} path
|
|
28
|
+
* @param {{maxBytes?:number, maxLineBytes?:number, stats?:object}} [opts]
|
|
29
|
+
*/
|
|
30
|
+
export async function* readJsonl(path, opts = {}) {
|
|
31
|
+
const maxBytes = opts.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
32
|
+
const maxLineBytes = opts.maxLineBytes ?? DEFAULT_MAX_LINE_BYTES;
|
|
33
|
+
const stats = opts.stats ?? {};
|
|
34
|
+
Object.assign(stats, {
|
|
35
|
+
lines: 0,
|
|
36
|
+
malformed: 0,
|
|
37
|
+
oversized: 0,
|
|
38
|
+
truncated: false,
|
|
39
|
+
unreadable: false,
|
|
40
|
+
bytes: 0,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
let size;
|
|
44
|
+
try {
|
|
45
|
+
const st = statSync(path);
|
|
46
|
+
if (!st.isFile()) throw new Error("not a regular file");
|
|
47
|
+
size = st.size;
|
|
48
|
+
} catch {
|
|
49
|
+
stats.unreadable = true;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const capped = size > maxBytes;
|
|
53
|
+
stats.bytes = Math.min(size, maxBytes);
|
|
54
|
+
if (stats.bytes === 0) return;
|
|
55
|
+
|
|
56
|
+
const classify = (n, text) => {
|
|
57
|
+
let value;
|
|
58
|
+
try {
|
|
59
|
+
value = JSON.parse(text);
|
|
60
|
+
} catch {
|
|
61
|
+
stats.malformed++;
|
|
62
|
+
return { line: n, reason: "parse" };
|
|
63
|
+
}
|
|
64
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
65
|
+
stats.malformed++;
|
|
66
|
+
return { line: n, reason: "not-object" };
|
|
67
|
+
}
|
|
68
|
+
return { line: n, record: value };
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// A line becomes {n, text} or {n, oversized}. Bytes of the current line are
|
|
72
|
+
// kept only while it is within the cap; past it they are dropped as they
|
|
73
|
+
// arrive, so a single huge line costs one chunk of memory, not its length.
|
|
74
|
+
// One line is held back so the last one is known before it is classified:
|
|
75
|
+
// when the byte cap cut the file, that line is incomplete.
|
|
76
|
+
const NL = 0x0a;
|
|
77
|
+
let parts = [];
|
|
78
|
+
let partBytes = 0;
|
|
79
|
+
let over = false;
|
|
80
|
+
let n = 0;
|
|
81
|
+
let held = null;
|
|
82
|
+
const finish = () => {
|
|
83
|
+
n++;
|
|
84
|
+
let line;
|
|
85
|
+
if (over) line = { n, oversized: true };
|
|
86
|
+
else {
|
|
87
|
+
let text = Buffer.concat(parts, partBytes).toString("utf8");
|
|
88
|
+
if (text.endsWith("\r")) text = text.slice(0, -1);
|
|
89
|
+
line = Buffer.byteLength(text) > maxLineBytes ? { n, oversized: true } : { n, text };
|
|
90
|
+
}
|
|
91
|
+
parts = [];
|
|
92
|
+
partBytes = 0;
|
|
93
|
+
over = false;
|
|
94
|
+
return line;
|
|
95
|
+
};
|
|
96
|
+
const emit = (line) => {
|
|
97
|
+
if (line.oversized) {
|
|
98
|
+
stats.oversized++;
|
|
99
|
+
return { line: line.n, reason: "oversized" };
|
|
100
|
+
}
|
|
101
|
+
return line.text.trim() ? classify(line.n, line.text) : null;
|
|
102
|
+
};
|
|
103
|
+
const append = (buf) => {
|
|
104
|
+
if (over || buf.length === 0) return;
|
|
105
|
+
if (partBytes + buf.length > maxLineBytes + 1) {
|
|
106
|
+
over = true;
|
|
107
|
+
parts = [];
|
|
108
|
+
partBytes = 0;
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
parts.push(buf);
|
|
112
|
+
partBytes += buf.length;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const input = createReadStream(path, { start: 0, end: stats.bytes - 1 });
|
|
116
|
+
try {
|
|
117
|
+
for await (const chunk of input) {
|
|
118
|
+
let from = 0;
|
|
119
|
+
for (let at = chunk.indexOf(NL); at !== -1; at = chunk.indexOf(NL, from)) {
|
|
120
|
+
append(chunk.subarray(from, at));
|
|
121
|
+
from = at + 1;
|
|
122
|
+
const line = finish();
|
|
123
|
+
if (held) {
|
|
124
|
+
const row = emit(held);
|
|
125
|
+
if (row) yield row;
|
|
126
|
+
}
|
|
127
|
+
held = line;
|
|
128
|
+
}
|
|
129
|
+
append(chunk.subarray(from));
|
|
130
|
+
}
|
|
131
|
+
} catch {
|
|
132
|
+
stats.unreadable = true;
|
|
133
|
+
}
|
|
134
|
+
if (partBytes > 0 || over) {
|
|
135
|
+
const line = finish();
|
|
136
|
+
if (held) {
|
|
137
|
+
const row = emit(held);
|
|
138
|
+
if (row) yield row;
|
|
139
|
+
}
|
|
140
|
+
held = line;
|
|
141
|
+
}
|
|
142
|
+
stats.lines = n;
|
|
143
|
+
if (!held || (!held.oversized && !held.text.trim())) return;
|
|
144
|
+
if (capped) {
|
|
145
|
+
stats.truncated = true;
|
|
146
|
+
yield { line: held.n, reason: "truncated" };
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const row = emit(held);
|
|
150
|
+
if (row) yield row;
|
|
151
|
+
}
|