@mmerterden/multi-agent-pipeline 16.17.0 → 16.17.1
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 +12 -0
- package/package.json +1 -1
- package/pipeline/commands/multi-agent/review/SKILL.md +9 -5
- package/pipeline/multi-agent-refs/_dev-context.md +20 -0
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +5 -4
- package/pipeline/multi-agent-refs/platform-parity.md +25 -6
- package/pipeline/schemas/agent-state.schema.json +30 -0
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,18 @@ Internal file-layout changes that don't affect the slash-command surface are sti
|
|
|
16
16
|
|
|
17
17
|
## [Unreleased]
|
|
18
18
|
|
|
19
|
+
## [16.17.1] - 2026-08-31
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **The platform-parity cross-check shipped with a trigger nothing could satisfy.** 16.17.0 said the step runs when dev-context recorded a counterpart repo "whose stack resolves to the other mobile platform" - naming an `extras[]` / `readonlySiblings[]` entry and a `stack` field on it. None of the three existed: the picker's return value was never persisted anywhere, `agent-state.schema.json` declared no such array, and no phase wrote one. Phase 4 runs hours after Phase 0 and reads state, so the condition could never be true and the step could never fire. It was a feature that passed every gate and did nothing.
|
|
24
|
+
|
|
25
|
+
`state.siblings[]` now exists as the one source: declared in the schema with a closed shape (`name`, `root`, `stack`, `canPush`), written by the dev-context picker at Phase 0, and read by the parity step and nothing else. `stack` is resolved from marker files in the checkout using the table Phase 1 Step 2 already owns, never from the repo name - `my-app-android` is a naming convention, and a wrong stack sends a cross-repo reader at the wrong tree. An entry with no checkout, or an unresolved stack, is skipped rather than guessed at.
|
|
26
|
+
|
|
27
|
+
Two limits are now stated instead of discovered: a standalone `/multi-agent:review <PR-URL>` has no state file, so it has no siblings and skips the step - the counterpart repo is a human's choice in the picker, and inferring one from a PR URL would be a cross-repo read nobody authorized; and nothing is ever cloned, because cloning a repository in order to review a different one is a side effect nobody asked for.
|
|
28
|
+
|
|
29
|
+
The gate is the actual fix. `smoke-platform-parity.sh` asserted only that the four documents referenced each other, which is why it passed on a trigger that was about nothing. It now checks that the field the trigger reads is declared in the schema with the shape it expects AND written by a phase, that all four documents trigger off that same field, and that the name-based stack shortcut is ruled out in writing. Verified against the 16.17.0 tree: both new checks fail on it.
|
|
30
|
+
|
|
19
31
|
## [16.17.0] - 2026-08-31
|
|
20
32
|
|
|
21
33
|
### Added
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mmerterden/multi-agent-pipeline",
|
|
3
|
-
"version": "16.17.
|
|
3
|
+
"version": "16.17.1",
|
|
4
4
|
"description": "8-phase AI development pipeline with full orchestration on Claude Code, Copilot CLI and Codex CLI. Analysis, planning, TDD, CLI-aware parallel review with consensus surfacing + Fable triage, default-FAIL evidence gates, secret + intent guards, per-phase cost ledger, persistent learnings memory, wiki generation, commit automation. Token-preserving uninstall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -203,11 +203,15 @@ Catalog-only - does NOT invoke binaries. For a full scan, use `/multi-agent:te
|
|
|
203
203
|
|
|
204
204
|
### 4b. Platform parity cross-check (advisory, read-only)
|
|
205
205
|
|
|
206
|
-
When
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
206
|
+
When `state.siblings[]` holds a checked-out repo whose `stack` is the other
|
|
207
|
+
mobile platform and the diff touches a screen, a service, a request model or a
|
|
208
|
+
localization file, compare the change against that repo on four axes: endpoints
|
|
209
|
+
called, parameters sent, business rules around the call, localization keys used.
|
|
210
|
+
|
|
211
|
+
That array is written by the Phase 0 dev-context picker, so a standalone review
|
|
212
|
+
of a PR URL - no pipeline run, no state file - has no siblings and skips this
|
|
213
|
+
step. The counterpart repo is a human's choice in the picker; guessing one from
|
|
214
|
+
a PR URL would be a cross-repo read nobody authorized.
|
|
211
215
|
|
|
212
216
|
The counterpart is located through the code graph, capped at 8 files, and is
|
|
213
217
|
**read only** - never edited, staged, committed, pushed or built. Output is its
|
|
@@ -48,6 +48,26 @@ Selects extra repos the pipeline may touch beyond the primary repo(s) - typica
|
|
|
48
48
|
|
|
49
49
|
4. **Empty submit** - user picks nothing → `extras=[]`, primary repo(s) only.
|
|
50
50
|
|
|
51
|
+
5. **Persist the result into `state.siblings[]`.** Everything the picker learned
|
|
52
|
+
about repos this run will NOT modify goes here: every `readonlySiblings[]`
|
|
53
|
+
entry, plus any selected `extras[]` that is not being given a worktree. The
|
|
54
|
+
phases that consume them run hours later and have no access to the picker's
|
|
55
|
+
return value, so a result that is not written here is a result nothing can
|
|
56
|
+
read - Phase 4's platform-parity cross-check reads `state.siblings[]` and
|
|
57
|
+
nothing else.
|
|
58
|
+
|
|
59
|
+
Resolve each entry's `stack` from its local checkout, with the marker table
|
|
60
|
+
in `phases/phase-1-analysis.md` Step 2 (`.xcodeproj` / `Package.swift` →
|
|
61
|
+
`ios`, `build.gradle(.kts)` → `android`, and so on). No checkout, or no
|
|
62
|
+
marker matched → `stack: "unknown"` and `root: null`. Never infer a stack
|
|
63
|
+
from the repo NAME: `my-app-android` is a naming convention, not a marker,
|
|
64
|
+
and a wrong stack sends a cross-repo reader at the wrong tree.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
printf '{"siblings":%s}' "$SIBLINGS_JSON" \
|
|
68
|
+
| node $HOME/.claude/scripts/write-state.mjs "$STATE_FILE"
|
|
69
|
+
```
|
|
70
|
+
|
|
51
71
|
## Web repos - `webRepos`
|
|
52
72
|
|
|
53
73
|
Web repos are rarely submodules of the app repo, so submodule detection never finds them. `prefs.projects[<key>].webRepos[]` (or the pre-rename `frontendRepos[]`) is the primary source for them and is merged into the candidate list alongside the detected submodules. Only `/multi-agent:analysis` used to read this key, which meant the pipeline's own dev-context picker could not offer a web repo at all.
|
|
@@ -127,10 +127,11 @@ Cost ledger: `phase-4.repo_map_emitted bytes=N budget=B cache_hit=true|false` -
|
|
|
127
127
|
|
|
128
128
|
#### Step 1.8 - Platform parity cross-check (advisory, read-only)
|
|
129
129
|
|
|
130
|
-
Runs only when
|
|
131
|
-
`
|
|
132
|
-
screen, service, request model or localization file. Otherwise skip
|
|
133
|
-
no section, no placeholder.
|
|
130
|
+
Runs only when `state.siblings[]` (written by the Phase 0 dev-context picker)
|
|
131
|
+
holds a checked-out repo whose `stack` is the other mobile platform AND the diff
|
|
132
|
+
touches a screen, service, request model or localization file. Otherwise skip
|
|
133
|
+
silently - no section, no placeholder. No siblings entry means no comparison;
|
|
134
|
+
nothing is cloned and no stack is guessed from a repo name.
|
|
134
135
|
|
|
135
136
|
Load `$HOME/.claude/multi-agent-refs/platform-parity.md` and follow it. The
|
|
136
137
|
counterpart repo is **read only**, and parity findings are **never blocking**.
|
|
@@ -17,15 +17,34 @@ it never blocks, never edits, and its silence is never evidence.
|
|
|
17
17
|
|
|
18
18
|
Both conditions, or the step is skipped with no output and no placeholder:
|
|
19
19
|
|
|
20
|
-
1.
|
|
21
|
-
`
|
|
22
|
-
|
|
20
|
+
1. `state.siblings[]` holds an entry with a non-null `root` whose `stack` is the
|
|
21
|
+
other mobile platform - `android` when the run's stack is `ios`, `ios` when
|
|
22
|
+
it is `android`. That array is written by the dev-context picker at Phase 0
|
|
23
|
+
(`_dev-context.md` step 5) and is the only source consulted. `stack` there is
|
|
24
|
+
resolved from marker files in the checkout, never from the repo name.
|
|
23
25
|
2. The diff touches something with a counterpart worth checking: a screen, a
|
|
24
26
|
service/repository/use-case, a request model, or a localization file.
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
```bash
|
|
29
|
+
SIB=$(node -e 'try{const s=require(process.argv[1]);const want=process.argv[2];
|
|
30
|
+
const m=(s.siblings||[]).find(x=>x&&x.root&&x.stack===want);
|
|
31
|
+
if(m)process.stdout.write(JSON.stringify({name:m.name,root:m.root,stack:m.stack}))}catch{}' \
|
|
32
|
+
"$STATE_FILE" "$COUNTERPART_STACK")
|
|
33
|
+
[ -n "$SIB" ] || exit 0 # nothing to compare against; no section, no placeholder
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Consequences worth stating rather than discovering:
|
|
37
|
+
|
|
38
|
+
- **A run with no state file has no siblings, so the step never fires.** A bare
|
|
39
|
+
`/multi-agent:review <PR-URL>` outside a pipeline run is exactly that case.
|
|
40
|
+
This is a limit, not a bug: the counterpart repo is something a human chose in
|
|
41
|
+
the picker, and guessing one from a PR URL would be a cross-repo read nobody
|
|
42
|
+
authorized.
|
|
43
|
+
- **A sibling with `root: null` is skipped**, and the step never clones one.
|
|
44
|
+
Cloning a repository in order to review a different one is a side effect
|
|
45
|
+
nobody asked for.
|
|
46
|
+
- **`stack: "unknown"` is skipped**, because the marker scan found nothing and a
|
|
47
|
+
guess would send the reader at the wrong tree.
|
|
29
48
|
|
|
30
49
|
## Read-only, without exception
|
|
31
50
|
|
|
@@ -104,6 +104,36 @@
|
|
|
104
104
|
"type": ["string", "null"],
|
|
105
105
|
"description": "Set when a phase halts on a hard error (validator failed twice, no subagent returned, dispatch error past fallback, lock irrecoverable). Format '<phase>:<cause>'. Surfaced to the user and cleared on successful resume. See operations.md 'Halt visibility'."
|
|
106
106
|
},
|
|
107
|
+
"siblings": {
|
|
108
|
+
"type": "array",
|
|
109
|
+
"maxItems": 10,
|
|
110
|
+
"description": "Repos the dev-context picker offered that this run does not modify: read-only siblings, plus any extra the user selected. Persisted at Phase 0 because the phases that consume them run much later - Phase 4's platform-parity cross-check reads this and nothing else, so a picker result that is not written here is a step that can never fire.",
|
|
111
|
+
"items": {
|
|
112
|
+
"type": "object",
|
|
113
|
+
"additionalProperties": false,
|
|
114
|
+
"required": ["name", "root", "stack"],
|
|
115
|
+
"properties": {
|
|
116
|
+
"name": {
|
|
117
|
+
"type": "string",
|
|
118
|
+
"description": "Repo name as the picker showed it."
|
|
119
|
+
},
|
|
120
|
+
"root": {
|
|
121
|
+
"type": ["string", "null"],
|
|
122
|
+
"description": "Absolute path to the local checkout, or null when the repo is known but not checked out. A null root is skipped by every consumer: nothing clones a repo to review a different one."
|
|
123
|
+
},
|
|
124
|
+
"stack": {
|
|
125
|
+
"type": "string",
|
|
126
|
+
"enum": ["ios", "android", "node", "python", "go", "unknown"],
|
|
127
|
+
"description": "Resolved from the checkout's marker files by the same table Phase 1 Step 2 uses (.xcodeproj/Package.swift -> ios, build.gradle(.kts) -> android, ...). 'unknown' when no marker matched or there is no checkout - never guessed from the repo name."
|
|
128
|
+
},
|
|
129
|
+
"canPush": {
|
|
130
|
+
"type": "boolean",
|
|
131
|
+
"default": false,
|
|
132
|
+
"description": "Carried from the picker. Informational here: a sibling is read-only to Phase 4 regardless."
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
107
137
|
"rev": {
|
|
108
138
|
"type": "integer",
|
|
109
139
|
"minimum": 0,
|
|
@@ -55,11 +55,12 @@ Skip Phase 0-3 and review a diff only. Input shapes: a PR (`#N`, `repo#N`, GitHu
|
|
|
55
55
|
|
|
56
56
|
It does NOT invoke any binary/tool - the review stage only uses the catalog. For a full scan use `multi-agent-test "store-ready"`.
|
|
57
57
|
|
|
58
|
-
4b. **Platform parity cross-check** (advisory, read-only) - when
|
|
59
|
-
|
|
60
|
-
the other mobile platform
|
|
58
|
+
4b. **Platform parity cross-check** (advisory, read-only) - when `state.siblings[]`
|
|
59
|
+
(written by the Phase 0 dev-context picker) holds a checked-out repo whose `stack` is
|
|
60
|
+
the other mobile platform and the diff touches a screen, a service, a request model
|
|
61
61
|
or a localization file, compare the change against that repo on four axes: endpoints
|
|
62
62
|
called, parameters sent, business rules around the call, localization keys used.
|
|
63
|
+
A standalone review with no state file has no siblings and skips the step.
|
|
63
64
|
|
|
64
65
|
The counterpart is located through the code graph, capped at 8 files, and is **read
|
|
65
66
|
only** - never edited, staged, committed, pushed or built. Output is its own
|