@mmerterden/multi-agent-pipeline 16.15.0 → 16.16.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 +19 -0
- package/README.md +5 -4
- package/README.tr.md +5 -4
- package/docs/adr/0011-dormant-ci.md +87 -0
- package/docs/adr/README.md +1 -0
- package/docs/architecture.md +2 -2
- package/docs/ecosystem.md +5 -5
- package/install/_common.mjs +0 -1
- package/install/_dev-only-files.mjs +1 -0
- package/install/claude.mjs +14 -4
- package/install/index.mjs +8 -1
- package/package.json +6 -5
- package/pipeline/commands/multi-agent/help/SKILL.md +2 -0
- package/pipeline/commands/multi-agent/steer/SKILL.md +109 -0
- package/pipeline/commands/multi-agent/sync/SKILL.md +5 -7
- package/pipeline/multi-agent-refs/cross-cli-contract.md +4 -4
- package/pipeline/multi-agent-refs/phases/operations.md +23 -0
- package/pipeline/multi-agent-refs/phases.md +38 -0
- package/pipeline/preferences-template.json +2 -10
- package/pipeline/schemas/agent-state.schema.json +35 -0
- package/pipeline/schemas/analysis-spec.schema.json +8 -2
- package/pipeline/schemas/conventions-output.schema.json +2 -13
- package/pipeline/scripts/build-references.mjs +22 -6
- package/pipeline/scripts/code-graph-rules/android.json +4 -21
- package/pipeline/scripts/code-graph-rules/go.json +4 -19
- package/pipeline/scripts/code-graph-rules/node.json +4 -21
- package/pipeline/scripts/feedback-send.mjs +3 -1
- package/pipeline/scripts/gate-linux.sh +62 -0
- package/pipeline/scripts/localize-commands.mjs +1 -2
- package/pipeline/scripts/pre-push-check.sh +6 -4
- package/pipeline/scripts/usage-report.mjs +16 -14
- package/pipeline/scripts/validate-analysis-doc.mjs +46 -27
- package/pipeline/scripts/validate-analysis.mjs +7 -1
- package/pipeline/scripts/validate-complaint-doc.mjs +24 -8
- package/pipeline/scripts/write-state.mjs +71 -12
- package/pipeline/skills/shared/core/multi-agent-steer/SKILL.md +111 -0
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +4 -4
|
@@ -26,6 +26,44 @@ Short: 0-Init -> (1,2 skipped) --------------> 3-Dev -> 4-Review -> 5-Test -
|
|
|
26
26
|
Full or Short is the Phase 0 Step 7.5 question, not a command name. Autopilot never asks and always runs Full.
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
## Phase entry - pending steer (every phase, every mode)
|
|
30
|
+
|
|
31
|
+
Before a phase does any work, read `pendingSteer` from `agent-state.json`. It
|
|
32
|
+
is how a correction reaches a run that is already going: `/multi-agent:steer #N
|
|
33
|
+
"<instruction>"` queues one, and the next phase to start consumes it.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
STEER=$(node -e 'try{const s=require(process.argv[1]);const p=s.pendingSteer;if(p&&p.text&&!p.appliedAt)process.stdout.write(p.text)}catch{}' "$STATE_FILE")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The `catch` is not decoration: a state file that does not exist yet (Phase 0
|
|
40
|
+
before its first write) makes `require` throw, and an unguarded read prints a
|
|
41
|
+
stack trace at every phase entry that reads like a failed phase.
|
|
42
|
+
|
|
43
|
+
Empty → carry on, nothing to do. Non-empty:
|
|
44
|
+
|
|
45
|
+
1. Apply it to this phase's context before starting. It is the user's own
|
|
46
|
+
words; treat it as an instruction from them, arriving now.
|
|
47
|
+
2. Say what changed, in one line, so the correction is visible in the run and
|
|
48
|
+
not only in the state file.
|
|
49
|
+
3. Mark it consumed. The record stays as a trace; `appliedAt` is what stops it
|
|
50
|
+
being read again, and the next steer overwrites all four keys:
|
|
51
|
+
```bash
|
|
52
|
+
printf '{"pendingSteer":{"appliedAt":"%s","appliedPhase":<N>}}' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
53
|
+
| node $HOME/.claude/scripts/write-state.mjs "$STATE_FILE"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`<N>` is this phase's number, substituted literally and unquoted - it is a
|
|
57
|
+
JSON number. Leaving it as an unset shell variable produces
|
|
58
|
+
`"appliedPhase":}`, which is invalid JSON and exits 1.
|
|
59
|
+
4. Record it on the tracker and in the log: `bash $HOME/.claude/scripts/phase-tracker.sh meta <N> steer "applied"`, plus a `Steer applied: <text>` line in `agent-log.md`.
|
|
60
|
+
|
|
61
|
+
Two limits, both deliberate. It is read at phase **entry** only, never mid-phase:
|
|
62
|
+
a phase that changes target halfway discards the work it already did, which is
|
|
63
|
+
the outcome steer exists to avoid. And an instruction that contradicts an
|
|
64
|
+
approved plan halts for the user rather than silently rewriting the plan -
|
|
65
|
+
steering corrects a run, it does not re-authorize one.
|
|
66
|
+
|
|
29
67
|
## Visual Phase Tracker
|
|
30
68
|
|
|
31
69
|
Two channels run in parallel at every phase boundary. Both are required in their target CLIs - skipping either is the #1 source of "I see no progress" complaints.
|
|
@@ -48,12 +48,7 @@
|
|
|
48
48
|
"autoDiff": false,
|
|
49
49
|
"manualNote": false
|
|
50
50
|
},
|
|
51
|
-
"wikiScope": [
|
|
52
|
-
"main",
|
|
53
|
-
"ios",
|
|
54
|
-
"screenshots",
|
|
55
|
-
"index"
|
|
56
|
-
],
|
|
51
|
+
"wikiScope": ["main", "ios", "screenshots", "index"],
|
|
57
52
|
"autopilotReportTimeoutSeconds": 1800,
|
|
58
53
|
"promptLanguage": "en",
|
|
59
54
|
"outputLanguage": "en",
|
|
@@ -185,10 +180,7 @@
|
|
|
185
180
|
"localPath": "plugins/<my-plugin>/skills/workflow",
|
|
186
181
|
"upstreamMarketplace": "<installed-marketplace-name>",
|
|
187
182
|
"upstreamPlugin": "<upstream-plugin-name>",
|
|
188
|
-
"upstreamSkills": [
|
|
189
|
-
"<skill-a>",
|
|
190
|
-
"<skill-b>"
|
|
191
|
-
],
|
|
183
|
+
"upstreamSkills": ["<skill-a>", "<skill-b>"],
|
|
192
184
|
"derivedFromVersion": "0.0.0",
|
|
193
185
|
"upstreamVersionSource": "marketplace.json",
|
|
194
186
|
"upstreamLocalClone": "$HOME/<upstream-repo-working-copy>",
|
|
@@ -104,6 +104,41 @@
|
|
|
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
|
+
"rev": {
|
|
108
|
+
"type": "integer",
|
|
109
|
+
"minimum": 0,
|
|
110
|
+
"description": "Monotonic revision, bumped by write-state.mjs on every successful write. A reader keeps the rev it read and compares before writing back; a changed rev means the record moved underneath it. Optional, so a record written by an earlier version stays valid: treat a missing rev as 0. No schemaVersion bump - the field is additive and nothing needs migrating."
|
|
111
|
+
},
|
|
112
|
+
"pendingSteer": {
|
|
113
|
+
"type": ["object", "null"],
|
|
114
|
+
"additionalProperties": false,
|
|
115
|
+
"required": ["text", "at"],
|
|
116
|
+
"description": "A mid-run instruction from the user, waiting for the next phase boundary to consume it. Written by /multi-agent:steer; a phase consumes it at entry by setting appliedAt, which is what stops it being read again. The record is kept as a trace and overwritten wholesale by the next steer. Never applied mid-phase: changing the target of work already in flight throws that work away.",
|
|
117
|
+
"properties": {
|
|
118
|
+
"text": {
|
|
119
|
+
"type": "string",
|
|
120
|
+
"minLength": 1,
|
|
121
|
+
"maxLength": 4000,
|
|
122
|
+
"description": "The instruction, verbatim as the user typed it."
|
|
123
|
+
},
|
|
124
|
+
"at": {
|
|
125
|
+
"type": "string",
|
|
126
|
+
"format": "date-time",
|
|
127
|
+
"description": "When it was queued."
|
|
128
|
+
},
|
|
129
|
+
"appliedAt": {
|
|
130
|
+
"type": ["string", "null"],
|
|
131
|
+
"format": "date-time",
|
|
132
|
+
"description": "When a phase consumed it. Set alongside clearing the record, so the log keeps the trace."
|
|
133
|
+
},
|
|
134
|
+
"appliedPhase": {
|
|
135
|
+
"type": ["integer", "null"],
|
|
136
|
+
"minimum": 0,
|
|
137
|
+
"maximum": 7,
|
|
138
|
+
"description": "The phase that consumed it."
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
},
|
|
107
142
|
"telemetry": {
|
|
108
143
|
"type": "object",
|
|
109
144
|
"additionalProperties": true,
|
|
@@ -104,7 +104,10 @@
|
|
|
104
104
|
"platforms": {
|
|
105
105
|
"type": "array",
|
|
106
106
|
"minItems": 1,
|
|
107
|
-
"items": {
|
|
107
|
+
"items": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"enum": ["ios", "android", "web", "backend", "mobile", "frontend"]
|
|
110
|
+
},
|
|
108
111
|
"description": "Platforms selected in Phase 0 step 3 (multi-select)."
|
|
109
112
|
},
|
|
110
113
|
"repos": {
|
|
@@ -114,7 +117,10 @@
|
|
|
114
117
|
"additionalProperties": false,
|
|
115
118
|
"required": ["platform", "name", "canPush"],
|
|
116
119
|
"properties": {
|
|
117
|
-
"platform": {
|
|
120
|
+
"platform": {
|
|
121
|
+
"type": "string",
|
|
122
|
+
"enum": ["ios", "android", "web", "backend", "mobile", "frontend"]
|
|
123
|
+
},
|
|
118
124
|
"name": { "type": "string", "minLength": 1 },
|
|
119
125
|
"cloneUrl": { "type": ["string", "null"] },
|
|
120
126
|
"canPush": { "type": "boolean" }
|
|
@@ -66,13 +66,7 @@
|
|
|
66
66
|
"conventionBucket": {
|
|
67
67
|
"type": "object",
|
|
68
68
|
"additionalProperties": false,
|
|
69
|
-
"required": [
|
|
70
|
-
"pattern",
|
|
71
|
-
"example",
|
|
72
|
-
"confidence",
|
|
73
|
-
"evidenceFiles",
|
|
74
|
-
"alternativeCandidates"
|
|
75
|
-
],
|
|
69
|
+
"required": ["pattern", "example", "confidence", "evidenceFiles", "alternativeCandidates"],
|
|
76
70
|
"properties": {
|
|
77
71
|
"pattern": {
|
|
78
72
|
"type": "string",
|
|
@@ -84,12 +78,7 @@
|
|
|
84
78
|
},
|
|
85
79
|
"confidence": {
|
|
86
80
|
"type": "string",
|
|
87
|
-
"enum": [
|
|
88
|
-
"high",
|
|
89
|
-
"medium",
|
|
90
|
-
"low",
|
|
91
|
-
"none"
|
|
92
|
-
],
|
|
81
|
+
"enum": ["high", "medium", "low", "none"],
|
|
93
82
|
"description": "high: 5+ examples; medium: 3-4; low: 1-2 or mixed; none: no evidence."
|
|
94
83
|
},
|
|
95
84
|
"evidenceFiles": {
|
|
@@ -34,7 +34,7 @@ const NONE = "-";
|
|
|
34
34
|
|
|
35
35
|
function argOf(argv, flag) {
|
|
36
36
|
const i = argv.indexOf(flag);
|
|
37
|
-
return i === -1 ? null : argv[i + 1] ?? null;
|
|
37
|
+
return i === -1 ? null : (argv[i + 1] ?? null);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
function readState(path) {
|
|
@@ -102,15 +102,27 @@ function rowsFrom(spec, lang) {
|
|
|
102
102
|
c.url,
|
|
103
103
|
ref || NONE,
|
|
104
104
|
c.embeddedApiTable
|
|
105
|
-
? lang === "tr"
|
|
106
|
-
|
|
105
|
+
? lang === "tr"
|
|
106
|
+
? "API kontratı"
|
|
107
|
+
: "API contract"
|
|
108
|
+
: lang === "tr"
|
|
109
|
+
? "özellik spesifikasyonu"
|
|
110
|
+
: "feature spec",
|
|
107
111
|
OK[lang],
|
|
108
112
|
NONE,
|
|
109
113
|
);
|
|
110
114
|
}
|
|
111
115
|
|
|
112
116
|
for (const j of ev.jira ?? []) {
|
|
113
|
-
push(
|
|
117
|
+
push(
|
|
118
|
+
"Jira",
|
|
119
|
+
j.id,
|
|
120
|
+
j.url ?? NONE,
|
|
121
|
+
NONE,
|
|
122
|
+
lang === "tr" ? "ticket" : "ticket",
|
|
123
|
+
OK[lang],
|
|
124
|
+
j.summary,
|
|
125
|
+
);
|
|
114
126
|
}
|
|
115
127
|
|
|
116
128
|
for (const s of ev.swagger ?? []) {
|
|
@@ -266,13 +278,17 @@ function tableLocators(block) {
|
|
|
266
278
|
const found = [];
|
|
267
279
|
for (const line of block.split(/\r?\n/)) {
|
|
268
280
|
if (!line.trim().startsWith("|")) continue;
|
|
269
|
-
const cells = line
|
|
281
|
+
const cells = line
|
|
282
|
+
.split("|")
|
|
283
|
+
.slice(1, -1)
|
|
284
|
+
.map((c) => c.trim());
|
|
270
285
|
if (cells.length < 3) continue;
|
|
271
286
|
if (/^:?-{3,}:?$/.test(cells[0])) continue;
|
|
272
287
|
const locator = cells[2];
|
|
273
288
|
const source = cells[1];
|
|
274
289
|
if (!locator) continue;
|
|
275
|
-
if (HEADER_CELLS.has(locator.toLowerCase()) && HEADER_CELLS.has(cells[0].toLowerCase()))
|
|
290
|
+
if (HEADER_CELLS.has(locator.toLowerCase()) && HEADER_CELLS.has(cells[0].toLowerCase()))
|
|
291
|
+
continue;
|
|
276
292
|
found.push({ locator, source, raw: line });
|
|
277
293
|
}
|
|
278
294
|
return found;
|
|
@@ -3,19 +3,9 @@
|
|
|
3
3
|
"stack": "android",
|
|
4
4
|
"description": "Kotlin + Java code-graph rules. Adds what the graph needs on top of test-gap-rules/android.json, which already owns sourceExtensions, excludePathGlobs and the test-path predicates. Definition patterns here are deliberately broader than the test-gap publicApiPatterns: the graph wants internal types too, because Phase 1 narrows scope on the whole module, not just its public surface.",
|
|
5
5
|
"comments": {
|
|
6
|
-
"line": [
|
|
7
|
-
|
|
8
|
-
]
|
|
9
|
-
"block": [
|
|
10
|
-
[
|
|
11
|
-
"/*",
|
|
12
|
-
"*/"
|
|
13
|
-
]
|
|
14
|
-
],
|
|
15
|
-
"string": [
|
|
16
|
-
"\"\"\"",
|
|
17
|
-
"\""
|
|
18
|
-
]
|
|
6
|
+
"line": ["//"],
|
|
7
|
+
"block": [["/*", "*/"]],
|
|
8
|
+
"string": ["\"\"\"", "\""]
|
|
19
9
|
},
|
|
20
10
|
"definitionPatterns": [
|
|
21
11
|
{
|
|
@@ -60,14 +50,7 @@
|
|
|
60
50
|
"regex": "^\\s*import\\s+(?:static\\s+)?(?:[A-Za-z_][A-Za-z0-9_]*\\.)+([A-Z][A-Za-z0-9_]*)"
|
|
61
51
|
}
|
|
62
52
|
],
|
|
63
|
-
"referenceKinds": [
|
|
64
|
-
"class",
|
|
65
|
-
"interface",
|
|
66
|
-
"object",
|
|
67
|
-
"enum",
|
|
68
|
-
"record",
|
|
69
|
-
"typealias"
|
|
70
|
-
],
|
|
53
|
+
"referenceKinds": ["class", "interface", "object", "enum", "record", "typealias"],
|
|
71
54
|
"referenceKindsNote": "fun is deliberately absent, for the reason Swift's rules record for func: a bare lowercase name matched across files is almost never a call to that exact declaration, and on a large app the generic ones (invoke, build, create, get) take the top god-node slots purely because each happened to be declared once. Composables are the tempting exception - they are capitalised and behave like types - but they are declared with fun, so including them means including every other fun with them.",
|
|
72
55
|
"ignoredIdentifiers": [
|
|
73
56
|
"this",
|
|
@@ -3,20 +3,9 @@
|
|
|
3
3
|
"stack": "go",
|
|
4
4
|
"description": "Go code-graph rules. Adds what the graph needs on top of test-gap-rules/go.json, which already owns sourceExtensions, excludePathGlobs and the test-path predicates. Definition patterns here are deliberately broader than the test-gap publicApiPatterns: the graph wants unexported declarations too, because Phase 1 narrows scope on the whole package, not just its exported surface.",
|
|
5
5
|
"comments": {
|
|
6
|
-
"line": [
|
|
7
|
-
|
|
8
|
-
]
|
|
9
|
-
"block": [
|
|
10
|
-
[
|
|
11
|
-
"/*",
|
|
12
|
-
"*/"
|
|
13
|
-
]
|
|
14
|
-
],
|
|
15
|
-
"string": [
|
|
16
|
-
"`",
|
|
17
|
-
"\"",
|
|
18
|
-
"'"
|
|
19
|
-
]
|
|
6
|
+
"line": ["//"],
|
|
7
|
+
"block": [["/*", "*/"]],
|
|
8
|
+
"string": ["`", "\"", "'"]
|
|
20
9
|
},
|
|
21
10
|
"importSpecifiersAreStrings": true,
|
|
22
11
|
"definitionPatterns": [
|
|
@@ -47,11 +36,7 @@
|
|
|
47
36
|
"regex": "^[ \\t]*(?:import[ \\t]+)?(?:[A-Za-z_.][A-Za-z0-9_]*[ \\t]+)?\"(?:[^\"]*/)?([A-Za-z_][A-Za-z0-9_.-]*)\"[ \\t]*$"
|
|
48
37
|
}
|
|
49
38
|
],
|
|
50
|
-
"referenceKinds": [
|
|
51
|
-
"struct",
|
|
52
|
-
"interface",
|
|
53
|
-
"type"
|
|
54
|
-
],
|
|
39
|
+
"referenceKinds": ["struct", "interface", "type"],
|
|
55
40
|
"referenceKindsNote": "func is deliberately absent, for the reason the Swift, Kotlin, Node and Python rules all record: a bare name matched across files is almost never a call to that exact declaration, and Go's short-name convention (New, Run, Get, Close, Error) makes it worse than most. Functions still reach the graph through their defines edge, so they stay findable by name.",
|
|
56
41
|
"ignoredIdentifiers": [
|
|
57
42
|
"package",
|
|
@@ -3,20 +3,9 @@
|
|
|
3
3
|
"stack": "node",
|
|
4
4
|
"description": "TypeScript + JavaScript code-graph rules. Adds what the graph needs on top of test-gap-rules/node.json, which already owns sourceExtensions, excludePathGlobs and the test-path predicates. Definition patterns here are deliberately broader than the test-gap publicApiPatterns: the graph wants unexported declarations too, because Phase 1 narrows scope on the whole module, not just its public surface.",
|
|
5
5
|
"comments": {
|
|
6
|
-
"line": [
|
|
7
|
-
|
|
8
|
-
]
|
|
9
|
-
"block": [
|
|
10
|
-
[
|
|
11
|
-
"/*",
|
|
12
|
-
"*/"
|
|
13
|
-
]
|
|
14
|
-
],
|
|
15
|
-
"string": [
|
|
16
|
-
"`",
|
|
17
|
-
"\"",
|
|
18
|
-
"'"
|
|
19
|
-
]
|
|
6
|
+
"line": ["//"],
|
|
7
|
+
"block": [["/*", "*/"]],
|
|
8
|
+
"string": ["`", "\"", "'"]
|
|
20
9
|
},
|
|
21
10
|
"definitionPatterns": [
|
|
22
11
|
{
|
|
@@ -56,13 +45,7 @@
|
|
|
56
45
|
"regex": "(?:\\bfrom|\\brequire\\s*\\(|\\bimport)\\s*\\(?\\s*[\"'](?:[^\"']*[\\/:])?([A-Za-z_$][A-Za-z0-9_$-]*)(?:\\.[A-Za-z0-9]+)*[\"']"
|
|
57
46
|
}
|
|
58
47
|
],
|
|
59
|
-
"referenceKinds": [
|
|
60
|
-
"class",
|
|
61
|
-
"interface",
|
|
62
|
-
"type",
|
|
63
|
-
"enum",
|
|
64
|
-
"const"
|
|
65
|
-
],
|
|
48
|
+
"referenceKinds": ["class", "interface", "type", "enum", "const"],
|
|
66
49
|
"referenceKindsNote": "function is deliberately absent, for the reason Swift's rules record for func: a bare name matched across files is almost never a call to that exact declaration, and the generic ones (run, main, get, parse, handler) would take the top god-node slots purely because each happened to be declared once. Functions still reach the graph through their defines edge, so they stay findable by name. const is present but narrowed to an uppercase-initial declaration at column 0, which is what a component, a singleton or an exported table looks like; a lowercase const is a local and is not a declaration anyone references across files. Measured on this repo (126 sources): including function put `ok` at degree 52, `f` at 38, `tokens` at 15 and `arg` at 12 - local helper names that happen to be declared exactly once, so the ambiguity rule does not catch them either. Excluding it leaves a thin symbol layer, and that is the honest shape of this stack: a JavaScript module's exported unit is usually a function, so the useful graph here is the import graph between files. `graph-affected \"<file>.mjs\"` answers who imports a module, which is the question this stack actually gets asked.",
|
|
67
50
|
"ignoredIdentifiers": [
|
|
68
51
|
"this",
|
|
@@ -115,7 +115,9 @@ function endpointAllowed(endpoint) {
|
|
|
115
115
|
async function main() {
|
|
116
116
|
const text = String(arg("--text", "") ?? "").trim();
|
|
117
117
|
if (!text) {
|
|
118
|
-
process.stderr.write(
|
|
118
|
+
process.stderr.write(
|
|
119
|
+
'usage: feedback-send.mjs --text "<message>" [--kind bug|idea|question]\n',
|
|
120
|
+
);
|
|
119
121
|
process.exit(2);
|
|
120
122
|
}
|
|
121
123
|
if (text.length > TEXT_MAX) {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# gate-linux.sh - run the ci-lite job on a real Ubuntu image, locally.
|
|
3
|
+
#
|
|
4
|
+
# The pre-push gate (pre-push-check.sh) is the primary one, but it runs on the
|
|
5
|
+
# maintainer's macOS workstation against an installed node_modules that has
|
|
6
|
+
# accumulated over months. Two things it cannot answer: does this tree work on
|
|
7
|
+
# Linux, and does `npm ci` from the lockfile into an empty tree still resolve.
|
|
8
|
+
# ci-lite.yml answers both, and has not executed since 2026-07-25.
|
|
9
|
+
#
|
|
10
|
+
# `act` runs that workflow against the same image GitHub would use, so the
|
|
11
|
+
# answer comes from the workflow definition rather than from a second script
|
|
12
|
+
# that would drift away from it.
|
|
13
|
+
#
|
|
14
|
+
# NOT part of `npm run gate` on purpose: act and Docker are not installed
|
|
15
|
+
# everywhere, and a required gate that cannot run is the failure ADR-0011
|
|
16
|
+
# exists to avoid. Run it before a release, or after touching install/,
|
|
17
|
+
# lib/credential-store.sh, or anything path-shaped.
|
|
18
|
+
#
|
|
19
|
+
# Needs network inside the container: the scorecard's dependency metrics ask
|
|
20
|
+
# the registry (advisories and signatures) and report an unreachable registry
|
|
21
|
+
# as a failure rather than a skip, on the grounds that a supply-chain check
|
|
22
|
+
# which goes green without reaching the registry is worse than no check.
|
|
23
|
+
#
|
|
24
|
+
# Exit codes: 0 = the job passed, 1 = the job failed, 2 = tooling missing
|
|
25
|
+
|
|
26
|
+
set -uo pipefail
|
|
27
|
+
|
|
28
|
+
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
29
|
+
cd "$REPO_ROOT" || { echo "FAIL: can't cd to repo root" >&2; exit 2; }
|
|
30
|
+
|
|
31
|
+
if ! command -v act >/dev/null 2>&1; then
|
|
32
|
+
cat >&2 <<'MSG'
|
|
33
|
+
✗ act is not installed - this gate needs it to run the Ubuntu job locally.
|
|
34
|
+
|
|
35
|
+
brew install act # macOS
|
|
36
|
+
# and a running Docker daemon (Docker Desktop, colima, orbstack)
|
|
37
|
+
|
|
38
|
+
Why this is not bundled: it pulls a multi-gigabyte runner image. The rest of
|
|
39
|
+
the gate chain (npm run gate) has no such dependency and stays the default.
|
|
40
|
+
MSG
|
|
41
|
+
exit 2
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
if ! docker info >/dev/null 2>&1; then
|
|
45
|
+
echo "✗ act is installed but no Docker daemon is reachable. Start Docker and retry." >&2
|
|
46
|
+
exit 2
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
echo "→ Running ci-lite on ubuntu-22.04 via act (this pulls an image on first run)"
|
|
50
|
+
echo ""
|
|
51
|
+
|
|
52
|
+
if act workflow_dispatch \
|
|
53
|
+
-W .github/workflows/ci-lite.yml \
|
|
54
|
+
-P ubuntu-latest=catthehacker/ubuntu:act-22.04; then
|
|
55
|
+
echo ""
|
|
56
|
+
echo "✓ ci-lite passed on Linux"
|
|
57
|
+
exit 0
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
echo "" >&2
|
|
61
|
+
echo "✗ ci-lite failed on Linux. A pass on macOS does not cover this." >&2
|
|
62
|
+
exit 1
|
|
@@ -116,8 +116,7 @@ export function localizeCommands(dir, mode) {
|
|
|
116
116
|
// `.split("/").pop()` never split a backslash path, so on Windows this was
|
|
117
117
|
// always false - `restore` exited 0 doing nothing and /multi-agent:sync then
|
|
118
118
|
// mirrored localized descriptions into the repo, the one thing it must prevent.
|
|
119
|
-
const invokedDirectly =
|
|
120
|
-
process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
119
|
+
const invokedDirectly = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
121
120
|
if (invokedDirectly) {
|
|
122
121
|
const args = process.argv.slice(2);
|
|
123
122
|
const mode = args[0];
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
#
|
|
4
4
|
# Purpose:
|
|
5
5
|
# The repo HAS workflows (test.yml, ci-lite.yml), but they have not executed
|
|
6
|
-
# since 2026-07-
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
6
|
+
# since 2026-07-02: every attempt after that day - the last on 2026-07-25 -
|
|
7
|
+
# finished in about four seconds with zero steps recorded, which is a
|
|
8
|
+
# runner/quota block rather than a test failure. Both are now dormant by
|
|
9
|
+
# declaration too (ADR-0011: the push triggers are commented out). So this
|
|
10
|
+
# hook is not a second opinion - it is the only thing standing between a
|
|
11
|
+
# broken commit and npm.
|
|
10
12
|
#
|
|
11
13
|
# It therefore runs the CANONICAL chain, `npm test`, rather than a hand-picked
|
|
12
14
|
# subset. The subset it used to run was missing eight of the eleven steps
|
|
@@ -69,8 +69,7 @@ function readJson(path) {
|
|
|
69
69
|
|
|
70
70
|
function resolveGlobalPrefs() {
|
|
71
71
|
const path =
|
|
72
|
-
process.env.MULTI_AGENT_PREFS ||
|
|
73
|
-
join(homedir(), ".claude", "multi-agent-preferences.json");
|
|
72
|
+
process.env.MULTI_AGENT_PREFS || join(homedir(), ".claude", "multi-agent-preferences.json");
|
|
74
73
|
return readJson(path)?.global ?? {};
|
|
75
74
|
}
|
|
76
75
|
|
|
@@ -130,9 +129,7 @@ function enabledPlugins(state) {
|
|
|
130
129
|
]) {
|
|
131
130
|
for (const name of readEnabledPluginNames(s)) names.add(name.split("@")[0]);
|
|
132
131
|
}
|
|
133
|
-
return Array.from(names)
|
|
134
|
-
.filter(Boolean)
|
|
135
|
-
.slice(0, 20);
|
|
132
|
+
return Array.from(names).filter(Boolean).slice(0, 20);
|
|
136
133
|
}
|
|
137
134
|
|
|
138
135
|
function integrationTags(state) {
|
|
@@ -146,8 +143,7 @@ function integrationTags(state) {
|
|
|
146
143
|
const tool = String(call?.tool || "");
|
|
147
144
|
// Server segment may contain hyphens (mcp__multi-agent-toolkit__ios_tap), so the
|
|
148
145
|
// class must accept them or "multi-agent-toolkit" collapses to "dev".
|
|
149
|
-
const m =
|
|
150
|
-
tool.match(/^mcp__([a-z0-9_-]+?)__/i) || tool.match(/^mcp__([a-z0-9_-]+)/i);
|
|
146
|
+
const m = tool.match(/^mcp__([a-z0-9_-]+?)__/i) || tool.match(/^mcp__([a-z0-9_-]+)/i);
|
|
151
147
|
if (m) apps.add(m[1].replace(/_/g, "-").toLowerCase());
|
|
152
148
|
}
|
|
153
149
|
return Array.from(apps).slice(0, 20);
|
|
@@ -345,7 +341,8 @@ function trackerSummary(state) {
|
|
|
345
341
|
const phs = [];
|
|
346
342
|
const failed = [];
|
|
347
343
|
const models = new Set();
|
|
348
|
-
let startedAt =
|
|
344
|
+
let startedAt =
|
|
345
|
+
typeof tracker?.started_at === "string" && tracker.started_at ? tracker.started_at : null;
|
|
349
346
|
let endedAt = null;
|
|
350
347
|
for (const p of list) {
|
|
351
348
|
const id = p?.id != null && String(p.id) !== "" ? String(p.id) : "?";
|
|
@@ -510,9 +507,7 @@ function buildEvent(state) {
|
|
|
510
507
|
ph: typeof state.currentPhase === "number" ? state.currentPhase : null,
|
|
511
508
|
st: state.status || null,
|
|
512
509
|
hr: state.haltReason || null,
|
|
513
|
-
ri: Array.isArray(state.reviewIterations)
|
|
514
|
-
? state.reviewIterations.length
|
|
515
|
-
: 0,
|
|
510
|
+
ri: Array.isArray(state.reviewIterations) ? state.reviewIterations.length : 0,
|
|
516
511
|
pr: Boolean(state.pr && (state.pr.url || state.pr.number)),
|
|
517
512
|
du: durationSec(state, spend),
|
|
518
513
|
tk: spend.tk,
|
|
@@ -612,10 +607,17 @@ async function main() {
|
|
|
612
607
|
}
|
|
613
608
|
|
|
614
609
|
// Windows-safe entry-point check: compare file URLs, never string-split a path.
|
|
615
|
-
const invokedDirectly =
|
|
616
|
-
process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
610
|
+
const invokedDirectly = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
617
611
|
if (invokedDirectly) {
|
|
618
612
|
main().catch(() => process.exit(0));
|
|
619
613
|
}
|
|
620
614
|
|
|
621
|
-
export {
|
|
615
|
+
export {
|
|
616
|
+
endpointAllowed,
|
|
617
|
+
integrationTags,
|
|
618
|
+
enabledPlugins,
|
|
619
|
+
readEnabledPluginNames,
|
|
620
|
+
trackerSummary,
|
|
621
|
+
buildEvent,
|
|
622
|
+
resolveUser,
|
|
623
|
+
};
|