opencode-swarm 7.135.0 → 7.135.2
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/.opencode/skills/swarm-pr-feedback/SKILL.md +24 -21
- package/.opencode/skills/swarm-pr-review/SKILL.md +55 -42
- package/dist/agents/explorer.d.ts +1 -1
- package/dist/background/candidate-contract.d.ts +113 -0
- package/dist/background/candidate-parser.d.ts +8 -29
- package/dist/background/pr-review-trigger-contract.d.ts +197 -0
- package/dist/background/workspace-snapshot.d.ts +26 -0
- package/dist/cli/{config-doctor-j1x63x87.js → config-doctor-pj3yvxdb.js} +2 -2
- package/dist/cli/{core-fvqp2gnn.js → core-4va9z2ng.js} +2 -2
- package/dist/cli/{curation-policy-xg9zw0ep.js → curation-policy-b2caf3v3.js} +5 -5
- package/dist/cli/{curator-drift-9q6f85bm.js → curator-drift-2ta83hss.js} +2 -2
- package/dist/cli/{curator-llm-factory-4mat6cq5.js → curator-llm-factory-9983528d.js} +26 -26
- package/dist/cli/{curator-yc06cmnz.js → curator-r1s2mwf2.js} +26 -26
- package/dist/cli/{dispatch-gxbeb2ps.js → dispatch-wtk6dxky.js} +2 -2
- package/dist/cli/{evidence-summary-service-0cczdmjv.js → evidence-summary-service-feby8ny0.js} +5 -5
- package/dist/cli/{explorer-jp0nen03.js → explorer-15gqzxh8.js} +1 -1
- package/dist/cli/{gate-evidence-8zrwdj69.js → gate-evidence-84b03mb4.js} +3 -3
- package/dist/cli/guardrail-explain-pwxvg5n0.js +57 -0
- package/dist/cli/{guardrail-log-7egxrxap.js → guardrail-log-yzpz8b7t.js} +4 -4
- package/dist/cli/{hive-promoter-7s3gzajy.js → hive-promoter-586dhndj.js} +26 -26
- package/dist/cli/{index-nrvs08pr.js → index-02cqjna3.js} +6 -6
- package/dist/cli/{index-grxkbbc5.js → index-2pxk6qg4.js} +1 -1
- package/dist/cli/{index-crw4n55v.js → index-340chr1e.js} +1612 -166
- package/dist/cli/{index-g0raqavt.js → index-3m7kpnqf.js} +2 -2
- package/dist/cli/{index-z085b4ka.js → index-3xbx305x.js} +1 -1
- package/dist/cli/{index-b4z7s917.js → index-73dfyfhq.js} +1 -1
- package/dist/cli/{index-ens0gt38.js → index-7pnvf04s.js} +8 -8
- package/dist/cli/{index-x88w0cp5.js → index-8gw8p7yh.js} +3 -3
- package/dist/cli/{index-e8w62fgs.js → index-9c3gf2wd.js} +1 -1
- package/dist/cli/{index-vf6e8ame.js → index-b0eva00y.js} +2 -2
- package/dist/cli/{index-2ghkk9ve.js → index-bfd4xfpp.js} +3 -3
- package/dist/cli/{index-6kmmdtdp.js → index-bs4m85v1.js} +2 -2
- package/dist/cli/{index-86wdrskb.js → index-ckdva4rs.js} +5 -5
- package/dist/cli/{index-96b78s7p.js → index-cwdy5vw9.js} +28 -28
- package/dist/cli/{index-44zgqvqh.js → index-d5psymdk.js} +4 -4
- package/dist/cli/{index-vxv732ex.js → index-dg1njnms.js} +7 -4
- package/dist/cli/{index-yhba8swx.js → index-dzyjb33e.js} +1 -1
- package/dist/cli/{index-xg9repnx.js → index-ey29aap6.js} +1 -1
- package/dist/cli/{index-q9mq5tv3.js → index-g3meqrsg.js} +2 -2
- package/dist/cli/{index-7an2qn0s.js → index-hdy2nd5h.js} +4 -4
- package/dist/cli/{index-4yv2rn8q.js → index-hvrrq3f3.js} +1 -1
- package/dist/cli/{index-337r9pkr.js → index-j0kte6gd.js} +1 -1
- package/dist/cli/{index-ctvnb76s.js → index-j92ser8n.js} +1 -1
- package/dist/cli/{index-997d6p42.js → index-jgkjxpdh.js} +4 -4
- package/dist/cli/{index-13xxjfhn.js → index-m0ynxkaa.js} +3 -3
- package/dist/cli/{index-a4972t0f.js → index-qqkw794p.js} +3 -3
- package/dist/cli/{index-b7p0mp2q.js → index-ryqnn3nv.js} +1 -1
- package/dist/cli/{index-ny93srnd.js → index-vg3yx648.js} +1 -1
- package/dist/cli/{index-qe3v54nb.js → index-y111zefa.js} +64 -23
- package/dist/cli/{index-3nasn371.js → index-y6a7gjtj.js} +126 -42
- package/dist/cli/index.js +26 -26
- package/dist/cli/{knowledge-escalator-d76a090n.js → knowledge-escalator-w7by1m8m.js} +6 -6
- package/dist/cli/{knowledge-events-3mcg1a0e.js → knowledge-events-n8xxsay3.js} +4 -4
- package/dist/cli/{knowledge-link-etrfntkd.js → knowledge-link-mm1w967j.js} +3 -3
- package/dist/cli/{knowledge-store-xtt4sede.js → knowledge-store-xzmvfnx3.js} +4 -4
- package/dist/cli/{knowledge-validator-h1k7ymtd.js → knowledge-validator-5q1amsq6.js} +7 -7
- package/dist/cli/{pending-delegations-4z3tkkct.js → pending-delegations-mt5gs1bt.js} +3 -3
- package/dist/cli/{pr-subscriptions-29j794kd.js → pr-subscriptions-v5xd3j6d.js} +3 -3
- package/dist/cli/{scan-cursor-stztfv7d.js → scan-cursor-1q1zmt4b.js} +5 -5
- package/dist/cli/{schema-tbq2srrh.js → schema-8mtsfspm.js} +1 -1
- package/dist/cli/{scope-persistence-7wydjzx8.js → scope-persistence-h2fpgxww.js} +2 -2
- package/dist/cli/{skill-generator-02q1qdf0.js → skill-generator-54xbjgak.js} +8 -8
- package/dist/cli/{workspace-snapshot-9h5za0s6.js → workspace-snapshot-jmyamqnv.js} +6 -1
- package/dist/cli/{worktree-collision-ownership-dtwr0xj4.js → worktree-collision-ownership-15hymf1y.js} +5 -5
- package/dist/commands/registry.d.ts +1 -1
- package/dist/hooks/pr-workflow-gate.d.ts +30 -4
- package/dist/hooks/repo-graph-builder.d.ts +18 -0
- package/dist/hooks/scope-guard.d.ts +11 -4
- package/dist/index.js +395 -391
- package/dist/scope/scope-binding.d.ts +68 -0
- package/dist/session/snapshot-reader.d.ts +6 -0
- package/dist/state.d.ts +118 -0
- package/dist/tools/convene-council.d.ts +3 -3
- package/dist/tools/dispatch-lanes.d.ts +65 -7
- package/dist/tools/repo-graph/builder.d.ts +20 -2
- package/dist/tools/repo-graph/incremental.d.ts +18 -1
- package/dist/tools/repo-graph/ontology.d.ts +8 -0
- package/dist/tools/repo-graph/types.d.ts +62 -1
- package/dist/tools/repo-graph.d.ts +1 -1
- package/dist/tools/submit-phase-council-verdicts.d.ts +3 -3
- package/dist/tools/write-final-council-evidence.d.ts +3 -3
- package/dist/tools/write-pr-review-trigger-eval.d.ts +34 -63
- package/dist/turbo/lean/lane-scope.d.ts +120 -0
- package/dist/turbo/lean/runner.d.ts +66 -4
- package/package.json +1 -1
- package/dist/cli/guardrail-explain-zmbxwh3s.js +0 -57
|
@@ -5,13 +5,13 @@ import {
|
|
|
5
5
|
getProfileForFile,
|
|
6
6
|
isCommandAvailable,
|
|
7
7
|
tool
|
|
8
|
-
} from "./index-
|
|
8
|
+
} from "./index-m0ynxkaa.js";
|
|
9
9
|
import {
|
|
10
10
|
detectStraySwarmDirs,
|
|
11
11
|
readDoctorArtifact,
|
|
12
12
|
removeStraySwarmDir,
|
|
13
13
|
runConfigDoctor
|
|
14
|
-
} from "./index-
|
|
14
|
+
} from "./index-j92ser8n.js";
|
|
15
15
|
import {
|
|
16
16
|
EvaluationTaskV1Schema,
|
|
17
17
|
GateAuditManifestV1Schema,
|
|
@@ -66,7 +66,7 @@ import {
|
|
|
66
66
|
saveTaskSetSnapshot,
|
|
67
67
|
transientBackoff,
|
|
68
68
|
validateProjectRoot
|
|
69
|
-
} from "./index-
|
|
69
|
+
} from "./index-7pnvf04s.js";
|
|
70
70
|
import {
|
|
71
71
|
withTimeout
|
|
72
72
|
} from "./index-4905hd2m.js";
|
|
@@ -92,20 +92,20 @@ import {
|
|
|
92
92
|
retireSkill,
|
|
93
93
|
selectCandidateEntries,
|
|
94
94
|
syncBundledProjectSkillsIfMissingAsync
|
|
95
|
-
} from "./index-
|
|
95
|
+
} from "./index-qqkw794p.js";
|
|
96
96
|
import {
|
|
97
97
|
readRecentEscalations
|
|
98
|
-
} from "./index-
|
|
98
|
+
} from "./index-ckdva4rs.js";
|
|
99
99
|
import {
|
|
100
100
|
alreadyCuratedThisGeneration,
|
|
101
101
|
claimNextScanBatch,
|
|
102
102
|
resolveCohortId
|
|
103
|
-
} from "./index-
|
|
103
|
+
} from "./index-b0eva00y.js";
|
|
104
104
|
import {
|
|
105
105
|
CURATOR_INIT_PROMPT,
|
|
106
106
|
CURATOR_PHASE_PROMPT,
|
|
107
107
|
READ_ONLY_LANE_GUIDANCE
|
|
108
|
-
} from "./index-
|
|
108
|
+
} from "./index-dg1njnms.js";
|
|
109
109
|
import {
|
|
110
110
|
getGlobalEventBus
|
|
111
111
|
} from "./index-c8s9a3zh.js";
|
|
@@ -123,7 +123,7 @@ import {
|
|
|
123
123
|
listActive,
|
|
124
124
|
subscribe,
|
|
125
125
|
unsubscribe
|
|
126
|
-
} from "./index-
|
|
126
|
+
} from "./index-3xbx305x.js";
|
|
127
127
|
import {
|
|
128
128
|
appendUnactionable,
|
|
129
129
|
quarantineEntry,
|
|
@@ -133,11 +133,11 @@ import {
|
|
|
133
133
|
validateActionability,
|
|
134
134
|
validateActionableFields,
|
|
135
135
|
validateLesson
|
|
136
|
-
} from "./index-
|
|
136
|
+
} from "./index-jgkjxpdh.js";
|
|
137
137
|
import {
|
|
138
138
|
authorizeCuration,
|
|
139
139
|
buildConfigFingerprintInput
|
|
140
|
-
} from "./index-
|
|
140
|
+
} from "./index-3m7kpnqf.js";
|
|
141
141
|
import {
|
|
142
142
|
cohortConfigFingerprint
|
|
143
143
|
} from "./index-rtry5xyf.js";
|
|
@@ -170,7 +170,7 @@ import {
|
|
|
170
170
|
transactFile,
|
|
171
171
|
transactKnowledge,
|
|
172
172
|
wordBigrams
|
|
173
|
-
} from "./index-
|
|
173
|
+
} from "./index-02cqjna3.js";
|
|
174
174
|
import {
|
|
175
175
|
MAX_EVENT_LOG_ENTRIES,
|
|
176
176
|
_internals,
|
|
@@ -180,7 +180,7 @@ import {
|
|
|
180
180
|
recordHiveKnowledgeEvent,
|
|
181
181
|
recordKnowledgeEvent,
|
|
182
182
|
resolveKnowledgeEventsPath
|
|
183
|
-
} from "./index-
|
|
183
|
+
} from "./index-8gw8p7yh.js";
|
|
184
184
|
import {
|
|
185
185
|
resolveHiveDataDir,
|
|
186
186
|
resolveHiveEventsPath,
|
|
@@ -196,16 +196,16 @@ import {
|
|
|
196
196
|
resolveLinkDir,
|
|
197
197
|
sanitizeLinkId,
|
|
198
198
|
writeLinkPointer
|
|
199
|
-
} from "./index-
|
|
199
|
+
} from "./index-dzyjb33e.js";
|
|
200
200
|
import {
|
|
201
201
|
atomicWriteFile
|
|
202
|
-
} from "./index-
|
|
202
|
+
} from "./index-ey29aap6.js";
|
|
203
203
|
import {
|
|
204
204
|
exports_loader,
|
|
205
205
|
init_loader,
|
|
206
206
|
loadPluginConfig,
|
|
207
207
|
loadPluginConfigWithMeta
|
|
208
|
-
} from "./index-
|
|
208
|
+
} from "./index-9c3gf2wd.js";
|
|
209
209
|
import {
|
|
210
210
|
RetrospectiveEvidenceSchema
|
|
211
211
|
} from "./index-k5jrywpr.js";
|
|
@@ -228,7 +228,7 @@ import {
|
|
|
228
228
|
package_default,
|
|
229
229
|
resolveAutoReviewConfig,
|
|
230
230
|
stripKnownSwarmPrefix
|
|
231
|
-
} from "./index-
|
|
231
|
+
} from "./index-j0kte6gd.js";
|
|
232
232
|
import {
|
|
233
233
|
KNOWLEDGE_SCHEMA_VERSION,
|
|
234
234
|
OUTCOME_BLOCK_THRESHOLD,
|
|
@@ -244,7 +244,7 @@ import {
|
|
|
244
244
|
clearAllScopes,
|
|
245
245
|
clearScopeBindingFromDisk,
|
|
246
246
|
clearScopeBindings
|
|
247
|
-
} from "./index-
|
|
247
|
+
} from "./index-vg3yx648.js";
|
|
248
248
|
import {
|
|
249
249
|
appendLedgerEvent,
|
|
250
250
|
computePlanHash,
|
|
@@ -266,12 +266,12 @@ import {
|
|
|
266
266
|
import {
|
|
267
267
|
findByBatchId,
|
|
268
268
|
readDelegations
|
|
269
|
-
} from "./index-
|
|
269
|
+
} from "./index-hdy2nd5h.js";
|
|
270
270
|
import {
|
|
271
271
|
readSwarmFileAsync,
|
|
272
272
|
safeHook,
|
|
273
273
|
validateSwarmPath
|
|
274
|
-
} from "./index-
|
|
274
|
+
} from "./index-bfd4xfpp.js";
|
|
275
275
|
import {
|
|
276
276
|
tryAcquireLock,
|
|
277
277
|
withEvidenceLock
|
|
@@ -287,11 +287,6 @@ import {
|
|
|
287
287
|
exports_external,
|
|
288
288
|
init_zod
|
|
289
289
|
} from "./index-bpmtbmy9.js";
|
|
290
|
-
import {
|
|
291
|
-
bunSpawn,
|
|
292
|
-
bunWrite,
|
|
293
|
-
mergeEnvForChild
|
|
294
|
-
} from "./index-qe3v54nb.js";
|
|
295
290
|
import {
|
|
296
291
|
criticalWarn,
|
|
297
292
|
error,
|
|
@@ -314,12 +309,19 @@ import {
|
|
|
314
309
|
resolveIsExactSingleChildCommitAsync,
|
|
315
310
|
resolveIsWorkingTreeClean,
|
|
316
311
|
resolveIsWorkingTreeCleanAsync,
|
|
312
|
+
resolvePrFeedbackTrackingCandidatesAsync,
|
|
317
313
|
resolvePrReviewDiffStats,
|
|
318
314
|
resolvePrReviewDiffStatsAsync,
|
|
319
315
|
resolvePrWorkflowRevisionDigest,
|
|
320
316
|
resolveRemoteRefsContainingHead,
|
|
321
|
-
resolveRemoteRefsContainingHeadAsync
|
|
322
|
-
|
|
317
|
+
resolveRemoteRefsContainingHeadAsync,
|
|
318
|
+
switchPrFeedbackTrackingCandidateAsync
|
|
319
|
+
} from "./index-y6a7gjtj.js";
|
|
320
|
+
import {
|
|
321
|
+
bunSpawn,
|
|
322
|
+
bunWrite,
|
|
323
|
+
mergeEnvForChild
|
|
324
|
+
} from "./index-y111zefa.js";
|
|
323
325
|
import {
|
|
324
326
|
__require,
|
|
325
327
|
__toCommonJS,
|
|
@@ -338,6 +340,914 @@ init_zod();
|
|
|
338
340
|
import * as fsp2 from "fs/promises";
|
|
339
341
|
import * as path4 from "path";
|
|
340
342
|
|
|
343
|
+
// src/background/candidate-contract.ts
|
|
344
|
+
var CANDIDATE_SEVERITIES = [
|
|
345
|
+
"INFO",
|
|
346
|
+
"LOW",
|
|
347
|
+
"MEDIUM",
|
|
348
|
+
"HIGH",
|
|
349
|
+
"CRITICAL"
|
|
350
|
+
];
|
|
351
|
+
var CANDIDATE_CONFIDENCES = ["HIGH", "MEDIUM", "LOW"];
|
|
352
|
+
var CANDIDATE_FIELD_COUNT = 9;
|
|
353
|
+
var CLEAN_FIELD_COUNT = 4;
|
|
354
|
+
var CLEAN_COVERAGE_SCOPE_MIN_CHARS = 12;
|
|
355
|
+
var CLEAN_EVIDENCE_MIN_CHARS = 20;
|
|
356
|
+
var CANDIDATE_DIAGNOSTIC_PREVIEW_CHARS = 160;
|
|
357
|
+
var CANDIDATE_FIELDS = {
|
|
358
|
+
base_explorer: [
|
|
359
|
+
"candidate_id",
|
|
360
|
+
"lane",
|
|
361
|
+
"severity",
|
|
362
|
+
"category",
|
|
363
|
+
"file_line",
|
|
364
|
+
"claim",
|
|
365
|
+
"evidence_summary",
|
|
366
|
+
"impact_context",
|
|
367
|
+
"confidence"
|
|
368
|
+
],
|
|
369
|
+
micro_lane: [
|
|
370
|
+
"candidate_id",
|
|
371
|
+
"micro_lane",
|
|
372
|
+
"severity",
|
|
373
|
+
"category",
|
|
374
|
+
"file_line",
|
|
375
|
+
"claim",
|
|
376
|
+
"invariant_violated",
|
|
377
|
+
"evidence_summary",
|
|
378
|
+
"confidence"
|
|
379
|
+
]
|
|
380
|
+
};
|
|
381
|
+
function candidateHeaderFamily(fields) {
|
|
382
|
+
if (fields[0]?.trim() !== "[CANDIDATE]")
|
|
383
|
+
return null;
|
|
384
|
+
const normalized = fields.slice(1).map((field) => field.trim());
|
|
385
|
+
for (const family of ["base_explorer", "micro_lane"]) {
|
|
386
|
+
const canonicalHeaderFields = CANDIDATE_FIELDS[family].map((name) => name === "file_line" ? "file:line" : name);
|
|
387
|
+
if (normalized.length === canonicalHeaderFields.length && canonicalHeaderFields.every((name, index) => normalized[index] === name)) {
|
|
388
|
+
return family;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
function removeCandidateCodeFences(text) {
|
|
394
|
+
const lines = [];
|
|
395
|
+
let inFence = false;
|
|
396
|
+
for (const rawLine of text.split(`
|
|
397
|
+
`)) {
|
|
398
|
+
if (rawLine.trimStart().startsWith("```")) {
|
|
399
|
+
inFence = !inFence;
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
if (!inFence)
|
|
403
|
+
lines.push(rawLine);
|
|
404
|
+
}
|
|
405
|
+
return lines.join(`
|
|
406
|
+
`);
|
|
407
|
+
}
|
|
408
|
+
function analyzeCandidateLaneContext(context) {
|
|
409
|
+
const issues = [];
|
|
410
|
+
const baseBound = Boolean(context.expected_lane || context.expected_lanes);
|
|
411
|
+
const microBound = Boolean(context.expected_micro_lane || context.expected_micro_lanes);
|
|
412
|
+
if (context.expected_lanes) {
|
|
413
|
+
if (!context.expected_lane) {
|
|
414
|
+
issues.push({
|
|
415
|
+
field: "expected_lane",
|
|
416
|
+
message: "expected_lane is required when expected_lanes is supplied"
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
if (new Set(context.expected_lanes).size !== context.expected_lanes.length) {
|
|
420
|
+
issues.push({
|
|
421
|
+
field: "expected_lanes",
|
|
422
|
+
message: "expected_lanes must contain unique lane identities"
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
if (context.expected_lane && !context.expected_lanes.includes(context.expected_lane)) {
|
|
426
|
+
issues.push({
|
|
427
|
+
field: "expected_lanes",
|
|
428
|
+
message: "expected_lanes must contain expected_lane"
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
if (context.expected_micro_lanes) {
|
|
433
|
+
if (!context.expected_micro_lane) {
|
|
434
|
+
issues.push({
|
|
435
|
+
field: "expected_micro_lane",
|
|
436
|
+
message: "expected_micro_lane is required when expected_micro_lanes is supplied"
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
if (new Set(context.expected_micro_lanes).size !== context.expected_micro_lanes.length) {
|
|
440
|
+
issues.push({
|
|
441
|
+
field: "expected_micro_lanes",
|
|
442
|
+
message: "expected_micro_lanes must contain unique lane identities"
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
if (context.expected_micro_lane && !context.expected_micro_lanes.includes(context.expected_micro_lane)) {
|
|
446
|
+
issues.push({
|
|
447
|
+
field: "expected_micro_lanes",
|
|
448
|
+
message: "expected_micro_lanes must contain expected_micro_lane"
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (!context.expected_family && baseBound && microBound) {
|
|
453
|
+
issues.push({
|
|
454
|
+
field: "expected_family",
|
|
455
|
+
message: "base and micro ownership fields cannot be combined without one expected_family"
|
|
456
|
+
});
|
|
457
|
+
} else if (context.expected_family === "base_explorer" && microBound) {
|
|
458
|
+
issues.push({
|
|
459
|
+
field: "expected_family",
|
|
460
|
+
message: "base_explorer ownership cannot include expected_micro_lane fields"
|
|
461
|
+
});
|
|
462
|
+
} else if (context.expected_family === "micro_lane" && baseBound) {
|
|
463
|
+
issues.push({
|
|
464
|
+
field: "expected_family",
|
|
465
|
+
message: "micro_lane ownership cannot include expected_lane fields"
|
|
466
|
+
});
|
|
467
|
+
} else if (!context.expected_family && baseBound) {
|
|
468
|
+
issues.push({
|
|
469
|
+
field: "expected_family",
|
|
470
|
+
message: "base ownership fields require expected_family base_explorer"
|
|
471
|
+
});
|
|
472
|
+
} else if (!context.expected_family && microBound) {
|
|
473
|
+
issues.push({
|
|
474
|
+
field: "expected_family",
|
|
475
|
+
message: "micro ownership fields require expected_family micro_lane"
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
return issues;
|
|
479
|
+
}
|
|
480
|
+
function splitPipeFields(line) {
|
|
481
|
+
const fields = [];
|
|
482
|
+
let current = "";
|
|
483
|
+
for (let index = 0;index < line.length; index++) {
|
|
484
|
+
const char = line[index];
|
|
485
|
+
if (char === "\\" && index + 1 < line.length && line[index + 1] === "|") {
|
|
486
|
+
current += "|";
|
|
487
|
+
index++;
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
if (char === "|") {
|
|
491
|
+
fields.push(current);
|
|
492
|
+
current = "";
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
current += char;
|
|
496
|
+
}
|
|
497
|
+
fields.push(current);
|
|
498
|
+
return fields;
|
|
499
|
+
}
|
|
500
|
+
function candidateDiagnosticPreview(value) {
|
|
501
|
+
const normalized = value.replace(/[\r\n\t]+/g, " ").trim();
|
|
502
|
+
if (normalized.length <= CANDIDATE_DIAGNOSTIC_PREVIEW_CHARS)
|
|
503
|
+
return normalized;
|
|
504
|
+
return `${normalized.slice(0, CANDIDATE_DIAGNOSTIC_PREVIEW_CHARS)}...`;
|
|
505
|
+
}
|
|
506
|
+
function isCandidateSeverity(value) {
|
|
507
|
+
return value !== null && value !== undefined && CANDIDATE_SEVERITIES.includes(value);
|
|
508
|
+
}
|
|
509
|
+
function isCandidateConfidence(value) {
|
|
510
|
+
return value !== null && value !== undefined && CANDIDATE_CONFIDENCES.includes(value);
|
|
511
|
+
}
|
|
512
|
+
function isCandidateLookingShortRow(fields, _context, explicitCandidateMarker = false) {
|
|
513
|
+
if (explicitCandidateMarker || fields[0]?.trim() === "[CANDIDATE]")
|
|
514
|
+
return true;
|
|
515
|
+
return fields.length >= 2;
|
|
516
|
+
}
|
|
517
|
+
function analyzeCandidateFields(fields, family) {
|
|
518
|
+
const names = CANDIDATE_FIELDS[family];
|
|
519
|
+
const trimmed = fields.map((field) => field.trim());
|
|
520
|
+
const values = {};
|
|
521
|
+
const issues = [];
|
|
522
|
+
if (trimmed.length !== CANDIDATE_FIELD_COUNT) {
|
|
523
|
+
issues.push({
|
|
524
|
+
field: "row",
|
|
525
|
+
message: `Expected exactly ${CANDIDATE_FIELD_COUNT} candidate fields, received ${trimmed.length}`
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
for (let index = 0;index < names.length; index++) {
|
|
529
|
+
const name = names[index];
|
|
530
|
+
const value = trimmed[index]?.trim() || null;
|
|
531
|
+
values[name] = value;
|
|
532
|
+
if (value === null) {
|
|
533
|
+
issues.push({
|
|
534
|
+
field: name,
|
|
535
|
+
message: `Missing required field: ${name}`
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
const severity = values.severity;
|
|
540
|
+
if (severity !== null && !isCandidateSeverity(severity)) {
|
|
541
|
+
issues.push({
|
|
542
|
+
field: "severity",
|
|
543
|
+
message: `Invalid severity: ${candidateDiagnosticPreview(severity)}; expected ${CANDIDATE_SEVERITIES.join("|")}`
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
const confidence = values.confidence;
|
|
547
|
+
if (confidence !== null && !isCandidateConfidence(confidence)) {
|
|
548
|
+
issues.push({
|
|
549
|
+
field: "confidence",
|
|
550
|
+
message: `Invalid confidence: ${candidateDiagnosticPreview(confidence)}; expected ${CANDIDATE_CONFIDENCES.join("|")}`
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
return {
|
|
554
|
+
valid: issues.length === 0,
|
|
555
|
+
issues,
|
|
556
|
+
values,
|
|
557
|
+
candidateId: values.candidate_id,
|
|
558
|
+
workflowLane: family === "base_explorer" ? values.lane : values.micro_lane
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
function analyzeCleanFields(fields, family, expectedLane) {
|
|
562
|
+
const trimmed = fields.map((field) => field.trim());
|
|
563
|
+
const issues = [];
|
|
564
|
+
const laneField = family === "base_explorer" ? "lane" : "micro_lane";
|
|
565
|
+
if (trimmed.length !== CLEAN_FIELD_COUNT) {
|
|
566
|
+
issues.push({
|
|
567
|
+
field: "row",
|
|
568
|
+
message: `Expected exactly ${CLEAN_FIELD_COUNT - 1} CLEAN fields after [CLEAN], received ${Math.max(0, trimmed.length - 1)}`
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
if (trimmed[0] !== "[CLEAN]") {
|
|
572
|
+
issues.push({ field: "row", message: "CLEAN row must begin with [CLEAN]" });
|
|
573
|
+
}
|
|
574
|
+
const lane = trimmed[1] || null;
|
|
575
|
+
const coverageScope = trimmed[2] || null;
|
|
576
|
+
const evidence = trimmed[3] || null;
|
|
577
|
+
if (lane === null) {
|
|
578
|
+
issues.push({
|
|
579
|
+
field: laneField,
|
|
580
|
+
message: `Missing required field: ${laneField}`
|
|
581
|
+
});
|
|
582
|
+
} else if (expectedLane !== undefined && lane !== expectedLane) {
|
|
583
|
+
issues.push({
|
|
584
|
+
field: laneField,
|
|
585
|
+
message: `Expected ${laneField} ${candidateDiagnosticPreview(expectedLane)}, received ${candidateDiagnosticPreview(lane)}`
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
if (coverageScope === null) {
|
|
589
|
+
issues.push({
|
|
590
|
+
field: "coverage_scope",
|
|
591
|
+
message: "Missing required field: coverage_scope"
|
|
592
|
+
});
|
|
593
|
+
} else if (coverageScope.length < CLEAN_COVERAGE_SCOPE_MIN_CHARS) {
|
|
594
|
+
issues.push({
|
|
595
|
+
field: "coverage_scope",
|
|
596
|
+
message: `coverage_scope must contain at least ${CLEAN_COVERAGE_SCOPE_MIN_CHARS} characters`
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
if (evidence === null) {
|
|
600
|
+
issues.push({
|
|
601
|
+
field: "evidence",
|
|
602
|
+
message: "Missing required field: evidence"
|
|
603
|
+
});
|
|
604
|
+
} else if (evidence.length < CLEAN_EVIDENCE_MIN_CHARS) {
|
|
605
|
+
issues.push({
|
|
606
|
+
field: "evidence",
|
|
607
|
+
message: `evidence must contain at least ${CLEAN_EVIDENCE_MIN_CHARS} characters`
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
return {
|
|
611
|
+
valid: issues.length === 0,
|
|
612
|
+
issues,
|
|
613
|
+
lane,
|
|
614
|
+
coverageScope,
|
|
615
|
+
evidence
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// src/background/candidate-parser.ts
|
|
620
|
+
init_zod();
|
|
621
|
+
|
|
622
|
+
// src/background/candidate-sidecar-store.ts
|
|
623
|
+
init_zod();
|
|
624
|
+
var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
|
|
625
|
+
var SidecarEnvelopeSchema = exports_external.object({
|
|
626
|
+
record_type: exports_external.literal("invocation"),
|
|
627
|
+
source_output_ref: exports_external.string().min(1),
|
|
628
|
+
source_batch_id: exports_external.string().min(1),
|
|
629
|
+
source_lane_id: exports_external.string().min(1),
|
|
630
|
+
source_agent: exports_external.string().min(1),
|
|
631
|
+
source_digest: exports_external.string().regex(/^[a-f0-9]{64}$/),
|
|
632
|
+
row_format_version: exports_external.number().int().nonnegative(),
|
|
633
|
+
producer: exports_external.string().optional(),
|
|
634
|
+
produced_at: exports_external.string().min(1),
|
|
635
|
+
record_version: exports_external.object({
|
|
636
|
+
major: exports_external.number().int().nonnegative(),
|
|
637
|
+
minor: exports_external.number().int().nonnegative()
|
|
638
|
+
}),
|
|
639
|
+
sessionId: exports_external.string().min(1).optional(),
|
|
640
|
+
parentSessionId: exports_external.string().min(1).optional(),
|
|
641
|
+
format_families_detected: exports_external.array(exports_external.string()),
|
|
642
|
+
candidate_count: exports_external.number().int().nonnegative(),
|
|
643
|
+
parse_errors: exports_external.number().int().nonnegative(),
|
|
644
|
+
malformed_rows: exports_external.number().int().nonnegative(),
|
|
645
|
+
clean_attestation_count: exports_external.number().int().nonnegative().optional()
|
|
646
|
+
}).strict();
|
|
647
|
+
var SidecarCleanAttestationSchema = exports_external.discriminatedUnion("row_format_family", [
|
|
648
|
+
exports_external.object({
|
|
649
|
+
record_type: exports_external.literal("clean_attestation"),
|
|
650
|
+
row_format_family: exports_external.literal("base_explorer"),
|
|
651
|
+
row_format_version: exports_external.number().int().nonnegative(),
|
|
652
|
+
record_version: exports_external.object({
|
|
653
|
+
major: exports_external.number().int().nonnegative(),
|
|
654
|
+
minor: exports_external.number().int().nonnegative()
|
|
655
|
+
}),
|
|
656
|
+
source_output_ref: exports_external.string().min(1),
|
|
657
|
+
source_batch_id: exports_external.string().min(1),
|
|
658
|
+
source_lane_id: exports_external.string().min(1),
|
|
659
|
+
source_agent: exports_external.string().min(1),
|
|
660
|
+
source_digest: exports_external.string().regex(/^[a-f0-9]{64}$/),
|
|
661
|
+
extracted_from_partial_source: exports_external.literal(false),
|
|
662
|
+
sessionId: exports_external.string().min(1).optional(),
|
|
663
|
+
parentSessionId: exports_external.string().min(1).optional(),
|
|
664
|
+
producer: exports_external.string().optional(),
|
|
665
|
+
lane: exports_external.string().min(1),
|
|
666
|
+
coverage_scope: exports_external.string().min(CLEAN_COVERAGE_SCOPE_MIN_CHARS),
|
|
667
|
+
evidence: exports_external.string().min(CLEAN_EVIDENCE_MIN_CHARS)
|
|
668
|
+
}).strict(),
|
|
669
|
+
exports_external.object({
|
|
670
|
+
record_type: exports_external.literal("clean_attestation"),
|
|
671
|
+
row_format_family: exports_external.literal("micro_lane"),
|
|
672
|
+
row_format_version: exports_external.number().int().nonnegative(),
|
|
673
|
+
record_version: exports_external.object({
|
|
674
|
+
major: exports_external.number().int().nonnegative(),
|
|
675
|
+
minor: exports_external.number().int().nonnegative()
|
|
676
|
+
}),
|
|
677
|
+
source_output_ref: exports_external.string().min(1),
|
|
678
|
+
source_batch_id: exports_external.string().min(1),
|
|
679
|
+
source_lane_id: exports_external.string().min(1),
|
|
680
|
+
source_agent: exports_external.string().min(1),
|
|
681
|
+
source_digest: exports_external.string().regex(/^[a-f0-9]{64}$/),
|
|
682
|
+
extracted_from_partial_source: exports_external.literal(false),
|
|
683
|
+
sessionId: exports_external.string().min(1).optional(),
|
|
684
|
+
parentSessionId: exports_external.string().min(1).optional(),
|
|
685
|
+
producer: exports_external.string().optional(),
|
|
686
|
+
micro_lane: exports_external.string().min(1),
|
|
687
|
+
coverage_scope: exports_external.string().min(CLEAN_COVERAGE_SCOPE_MIN_CHARS),
|
|
688
|
+
evidence: exports_external.string().min(CLEAN_EVIDENCE_MIN_CHARS)
|
|
689
|
+
}).strict()
|
|
690
|
+
]);
|
|
691
|
+
var SidecarCandidateCommon = {
|
|
692
|
+
record_type: exports_external.literal("candidate"),
|
|
693
|
+
row_format_version: exports_external.number().int().nonnegative(),
|
|
694
|
+
record_version: exports_external.object({
|
|
695
|
+
major: exports_external.number().int().nonnegative(),
|
|
696
|
+
minor: exports_external.number().int().nonnegative()
|
|
697
|
+
}),
|
|
698
|
+
source_output_ref: exports_external.string().min(1),
|
|
699
|
+
source_batch_id: exports_external.string().min(1),
|
|
700
|
+
source_lane_id: exports_external.string().min(1),
|
|
701
|
+
source_agent: exports_external.string().min(1),
|
|
702
|
+
source_digest: exports_external.string().regex(/^[a-f0-9]{64}$/),
|
|
703
|
+
sessionId: exports_external.string().min(1).optional(),
|
|
704
|
+
parentSessionId: exports_external.string().min(1).optional(),
|
|
705
|
+
producer: exports_external.string().optional(),
|
|
706
|
+
extracted_from_partial_source: exports_external.boolean(),
|
|
707
|
+
candidate_id: exports_external.string().min(1),
|
|
708
|
+
severity: exports_external.enum(CANDIDATE_SEVERITIES),
|
|
709
|
+
category: exports_external.string().min(1),
|
|
710
|
+
file_line: exports_external.string().min(1),
|
|
711
|
+
claim: exports_external.string().min(1),
|
|
712
|
+
evidence_summary: exports_external.string().min(1),
|
|
713
|
+
confidence: exports_external.enum(CANDIDATE_CONFIDENCES)
|
|
714
|
+
};
|
|
715
|
+
var SidecarCandidateSchema = exports_external.discriminatedUnion("row_format_family", [
|
|
716
|
+
exports_external.object({
|
|
717
|
+
...SidecarCandidateCommon,
|
|
718
|
+
row_format_family: exports_external.literal("base_explorer"),
|
|
719
|
+
lane: exports_external.string().min(1),
|
|
720
|
+
micro_lane: exports_external.null(),
|
|
721
|
+
impact_context: exports_external.string().min(1),
|
|
722
|
+
invariant_violated: exports_external.null()
|
|
723
|
+
}).strict(),
|
|
724
|
+
exports_external.object({
|
|
725
|
+
...SidecarCandidateCommon,
|
|
726
|
+
row_format_family: exports_external.literal("micro_lane"),
|
|
727
|
+
lane: exports_external.null(),
|
|
728
|
+
micro_lane: exports_external.string().min(1),
|
|
729
|
+
impact_context: exports_external.null(),
|
|
730
|
+
invariant_violated: exports_external.string().min(1)
|
|
731
|
+
}).strict()
|
|
732
|
+
]);
|
|
733
|
+
|
|
734
|
+
// src/background/candidate-parser.ts
|
|
735
|
+
var ArtifactInputSchema = exports_external.object({
|
|
736
|
+
output_ref: exports_external.string().min(1, "output_ref must be a non-empty string"),
|
|
737
|
+
batchId: exports_external.string().min(1, "batchId must be a non-empty string"),
|
|
738
|
+
laneId: exports_external.string().min(1, "laneId must be a non-empty string"),
|
|
739
|
+
agent: exports_external.string().min(1, "agent must be a non-empty string"),
|
|
740
|
+
role: exports_external.string().min(1, "role must be a non-empty string"),
|
|
741
|
+
sessionId: exports_external.string().min(1).optional(),
|
|
742
|
+
parentSessionId: exports_external.string().min(1).optional(),
|
|
743
|
+
digest: exports_external.string().regex(/^[a-f0-9]{64}$/, "digest must be a SHA-256 hex string"),
|
|
744
|
+
text: exports_external.string(),
|
|
745
|
+
transcriptIncomplete: exports_external.boolean().optional(),
|
|
746
|
+
artifact_status: exports_external.enum(["ok", "ref-not-found", "artifact-corrupted"]),
|
|
747
|
+
source: exports_external.enum(["dispatch_lanes", "collect_lane_results"]),
|
|
748
|
+
produced_at: exports_external.string().min(1, "produced_at must be a non-empty string")
|
|
749
|
+
}).strict();
|
|
750
|
+
var ParseFlagsSchema = exports_external.object({
|
|
751
|
+
accept_partial: exports_external.boolean(),
|
|
752
|
+
accept_degraded: exports_external.boolean(),
|
|
753
|
+
degraded: exports_external.boolean(),
|
|
754
|
+
row_format_version: exports_external.number().int().nonnegative(),
|
|
755
|
+
producer: exports_external.string().optional(),
|
|
756
|
+
expected_family: exports_external.enum(["base_explorer", "micro_lane"]).optional(),
|
|
757
|
+
expected_lane: exports_external.string().trim().min(1).max(120).optional(),
|
|
758
|
+
expected_lanes: exports_external.array(exports_external.string().trim().min(1).max(120)).min(1).max(11).optional(),
|
|
759
|
+
expected_micro_lane: exports_external.string().trim().min(1).max(120).optional(),
|
|
760
|
+
expected_micro_lanes: exports_external.array(exports_external.string().trim().min(1).max(120)).min(1).max(11).optional()
|
|
761
|
+
}).strict().superRefine((value, context) => {
|
|
762
|
+
for (const issue of analyzeCandidateLaneContext(value)) {
|
|
763
|
+
context.addIssue({
|
|
764
|
+
code: exports_external.ZodIssueCode.custom,
|
|
765
|
+
path: [issue.field],
|
|
766
|
+
message: issue.message
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
var RECORD_VERSION = { major: 1, minor: 1 };
|
|
771
|
+
|
|
772
|
+
class UnknownFormatFamilyError extends Error {
|
|
773
|
+
constructor() {
|
|
774
|
+
super("Unknown format family: neither impact_context nor invariant_violated present");
|
|
775
|
+
this.name = "UnknownFormatFamilyError";
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
function detectRowFormatFamily(rowFields) {
|
|
779
|
+
const trimmed = rowFields.map((f) => f.trim());
|
|
780
|
+
const hasImpactContext = trimmed.length > 7 && trimmed[7] !== "";
|
|
781
|
+
const hasInvariantViolated = trimmed.length > 6 && trimmed[6] !== "";
|
|
782
|
+
if (hasImpactContext && !hasInvariantViolated)
|
|
783
|
+
return "base_explorer";
|
|
784
|
+
if (hasInvariantViolated && !hasImpactContext)
|
|
785
|
+
return "micro_lane";
|
|
786
|
+
if (hasImpactContext && hasInvariantViolated)
|
|
787
|
+
return "base_explorer";
|
|
788
|
+
throw new UnknownFormatFamilyError;
|
|
789
|
+
}
|
|
790
|
+
function mapFields(fields, family) {
|
|
791
|
+
const trimmed = fields.map((f) => f.trim());
|
|
792
|
+
const names = CANDIDATE_FIELDS[family];
|
|
793
|
+
const result = {};
|
|
794
|
+
for (let i = 0;i < names.length; i++) {
|
|
795
|
+
result[names[i]] = i < trimmed.length && trimmed[i] !== "" ? trimmed[i] : null;
|
|
796
|
+
}
|
|
797
|
+
if (family === "base_explorer") {
|
|
798
|
+
result.micro_lane = null;
|
|
799
|
+
result.invariant_violated = null;
|
|
800
|
+
} else {
|
|
801
|
+
result.lane = null;
|
|
802
|
+
result.impact_context = null;
|
|
803
|
+
}
|
|
804
|
+
return result;
|
|
805
|
+
}
|
|
806
|
+
function getRequiredFields(family) {
|
|
807
|
+
return CANDIDATE_FIELDS[family];
|
|
808
|
+
}
|
|
809
|
+
function assertNonNullCandidateId(value) {
|
|
810
|
+
if (value === null || value === "") {
|
|
811
|
+
throw new Error("Invariant violation: null/empty candidate_id reached candidate builder");
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
function buildInvocationEnvelope(input, flags, formatFamiliesDetected, candidateCount, parseErrors, malformedRows, cleanAttestationCount = 0) {
|
|
815
|
+
return {
|
|
816
|
+
record_type: "invocation",
|
|
817
|
+
source_output_ref: input.output_ref,
|
|
818
|
+
source_batch_id: input.batchId,
|
|
819
|
+
source_lane_id: input.laneId,
|
|
820
|
+
source_agent: input.agent,
|
|
821
|
+
source_digest: input.digest,
|
|
822
|
+
row_format_version: flags.row_format_version,
|
|
823
|
+
sessionId: input.sessionId,
|
|
824
|
+
parentSessionId: input.parentSessionId,
|
|
825
|
+
...flags.producer ? { producer: flags.producer } : {},
|
|
826
|
+
produced_at: input.produced_at,
|
|
827
|
+
record_version: RECORD_VERSION,
|
|
828
|
+
format_families_detected: formatFamiliesDetected,
|
|
829
|
+
candidate_count: candidateCount,
|
|
830
|
+
parse_errors: parseErrors,
|
|
831
|
+
malformed_rows: malformedRows,
|
|
832
|
+
clean_attestation_count: cleanAttestationCount
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
function buildEmptyDiagnostics(input, flags) {
|
|
836
|
+
return {
|
|
837
|
+
candidate_count: 0,
|
|
838
|
+
parse_errors: 0,
|
|
839
|
+
parse_error_details: [],
|
|
840
|
+
malformed_rows: 0,
|
|
841
|
+
duplicate_id_count: 0,
|
|
842
|
+
duplicate_id_warnings: [],
|
|
843
|
+
degraded_source_count: flags.degraded ? 1 : 0,
|
|
844
|
+
incomplete_source_count: input.transcriptIncomplete ? 1 : 0,
|
|
845
|
+
format_families_detected: [],
|
|
846
|
+
clean_attestation_count: 0
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
function refusalResult(errorCode, errorMessage, input, flags) {
|
|
850
|
+
const envelope = buildInvocationEnvelope(input, flags, [], 0, 0, 0);
|
|
851
|
+
return {
|
|
852
|
+
error: errorMessage,
|
|
853
|
+
error_code: errorCode,
|
|
854
|
+
candidates: [],
|
|
855
|
+
invocation_envelope: envelope,
|
|
856
|
+
diagnostics: buildEmptyDiagnostics(input, flags)
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
function detectFormatMismatchHint(text) {
|
|
860
|
+
if (text.trim() === "")
|
|
861
|
+
return;
|
|
862
|
+
const severityPattern = /\b(CRITICAL|HIGH|MEDIUM|LOW|INFO)\b/;
|
|
863
|
+
const fileLinePattern = /\b\S+\.[a-z]{1,4}:\d+\b/;
|
|
864
|
+
const hasSeverity = severityPattern.test(text);
|
|
865
|
+
const textForFileLine = text.replace(/\b\w+:\/\/\S*/g, "");
|
|
866
|
+
const hasFileLine = fileLinePattern.test(textForFileLine);
|
|
867
|
+
if (hasSeverity && hasFileLine) {
|
|
868
|
+
return "Lane output contains severity keywords and file:line references but no parseable [CANDIDATE] rows. The explorer may have emitted findings in prose format instead of pipe-delimited candidate rows.";
|
|
869
|
+
}
|
|
870
|
+
if (hasSeverity) {
|
|
871
|
+
return "Lane output contains severity keywords but no parseable [CANDIDATE] rows. The explorer may have emitted findings in an unstructured format.";
|
|
872
|
+
}
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
function emptyTextResult(input, flags) {
|
|
876
|
+
const envelope = buildInvocationEnvelope(input, flags, [], 0, 0, 0);
|
|
877
|
+
const diagnostics = buildEmptyDiagnostics(input, flags);
|
|
878
|
+
const hint = detectFormatMismatchHint(input.text);
|
|
879
|
+
if (hint) {
|
|
880
|
+
diagnostics.format_mismatch_hint = hint;
|
|
881
|
+
}
|
|
882
|
+
return {
|
|
883
|
+
candidates: [],
|
|
884
|
+
invocation_envelope: envelope,
|
|
885
|
+
diagnostics
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
function parseCandidates(input, flags) {
|
|
889
|
+
const inputParse = ArtifactInputSchema.safeParse(input);
|
|
890
|
+
if (!inputParse.success) {
|
|
891
|
+
throw new Error(`Invalid ArtifactInput: ${inputParse.error.issues.map((issue) => issue.message).join(", ")}`);
|
|
892
|
+
}
|
|
893
|
+
const flagsParse = ParseFlagsSchema.safeParse(flags);
|
|
894
|
+
if (!flagsParse.success) {
|
|
895
|
+
throw new Error(`Invalid ParseFlags: ${flagsParse.error.issues.map((issue) => issue.message).join(", ")}`);
|
|
896
|
+
}
|
|
897
|
+
const validatedInput = inputParse.data;
|
|
898
|
+
const validatedFlags = flagsParse.data;
|
|
899
|
+
if (validatedInput.artifact_status === "ref-not-found") {
|
|
900
|
+
return refusalResult("ref-not-found", "Artifact reference not found in store", validatedInput, validatedFlags);
|
|
901
|
+
}
|
|
902
|
+
if (validatedInput.artifact_status === "artifact-corrupted") {
|
|
903
|
+
return refusalResult("artifact-corrupted", "Artifact data is corrupted", validatedInput, validatedFlags);
|
|
904
|
+
}
|
|
905
|
+
if (validatedFlags.degraded && !validatedFlags.accept_degraded) {
|
|
906
|
+
return refusalResult("degraded-source-refused", "Degraded source refused by caller", validatedInput, validatedFlags);
|
|
907
|
+
}
|
|
908
|
+
if (validatedInput.transcriptIncomplete === true && !validatedFlags.accept_partial) {
|
|
909
|
+
return refusalResult("partial-source-refused", "Partial transcript refused by caller", validatedInput, validatedFlags);
|
|
910
|
+
}
|
|
911
|
+
if (validatedInput.text.trim() === "") {
|
|
912
|
+
return emptyTextResult(validatedInput, validatedFlags);
|
|
913
|
+
}
|
|
914
|
+
return parseText(validatedInput, validatedFlags);
|
|
915
|
+
}
|
|
916
|
+
function parseText(input, flags) {
|
|
917
|
+
const cleanedText = removeCandidateCodeFences(input.text);
|
|
918
|
+
const lines = cleanedText.split(`
|
|
919
|
+
`);
|
|
920
|
+
let headerIndex = -1;
|
|
921
|
+
let headerFields = [];
|
|
922
|
+
for (let i = 0;i < lines.length; i++) {
|
|
923
|
+
const trimmed = lines[i].trim();
|
|
924
|
+
if (trimmed === "")
|
|
925
|
+
continue;
|
|
926
|
+
const fields = splitPipeFields(trimmed);
|
|
927
|
+
if (fields.length >= 2 && fields.some((f) => f.trim() !== "")) {
|
|
928
|
+
headerIndex = i;
|
|
929
|
+
headerFields = fields.map((f) => f.trim());
|
|
930
|
+
break;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
if (headerIndex === -1) {
|
|
934
|
+
return emptyTextResult(input, flags);
|
|
935
|
+
}
|
|
936
|
+
const headerFamily = candidateHeaderFamily(headerFields) ?? undefined;
|
|
937
|
+
if (headerFields[0] === "[CANDIDATE]" && headerFamily === undefined) {
|
|
938
|
+
return refusalResult("invalid-candidate-header", "Candidate output must begin with one exact canonical base or micro [CANDIDATE] header", input, flags);
|
|
939
|
+
}
|
|
940
|
+
if (flags.expected_family !== undefined && headerFamily !== undefined && flags.expected_family !== headerFamily) {
|
|
941
|
+
return refusalResult("expected-family-mismatch", `Expected ${flags.expected_family} rows but header declares ${headerFamily}`, input, flags);
|
|
942
|
+
}
|
|
943
|
+
const parseErrorDetails = [];
|
|
944
|
+
const candidates = [];
|
|
945
|
+
const idCounts = new Map;
|
|
946
|
+
const idFirstRows = new Map;
|
|
947
|
+
const formatFamiliesDetected = new Set;
|
|
948
|
+
let malformedRows = 0;
|
|
949
|
+
let currentCandidate = null;
|
|
950
|
+
let pendingClean;
|
|
951
|
+
let cleanErrorCode;
|
|
952
|
+
let cleanErrorMessage;
|
|
953
|
+
for (let i = headerIndex + 1;i < lines.length; i++) {
|
|
954
|
+
const rawLine = lines[i];
|
|
955
|
+
const trimmed = rawLine.trim();
|
|
956
|
+
if (trimmed === "")
|
|
957
|
+
continue;
|
|
958
|
+
let fields = splitPipeFields(trimmed);
|
|
959
|
+
if (fields[0]?.trim() === "[CLEAN]") {
|
|
960
|
+
const cleanFamily = flags.expected_family ?? headerFamily;
|
|
961
|
+
const cleanFields = fields.map((field) => field.trim());
|
|
962
|
+
const cleanLane = cleanFamily === "micro_lane" ? flags.expected_micro_lane : flags.expected_lane;
|
|
963
|
+
const cleanLanes = cleanFamily === "micro_lane" ? flags.expected_micro_lanes ?? (flags.expected_micro_lane ? [flags.expected_micro_lane] : undefined) : flags.expected_lanes ?? (flags.expected_lane ? [flags.expected_lane] : undefined);
|
|
964
|
+
if (cleanFamily !== undefined && cleanFields.length === 4 && cleanLane !== undefined && cleanFields[1] !== cleanLane && (cleanLanes ?? [cleanLane]).includes(cleanFields[1])) {
|
|
965
|
+
continue;
|
|
966
|
+
}
|
|
967
|
+
const cleanAnalysis = cleanFamily ? analyzeCleanFields(cleanFields, cleanFamily, cleanLane) : undefined;
|
|
968
|
+
if (pendingClean !== undefined) {
|
|
969
|
+
cleanErrorCode = "invalid-clean-attestation";
|
|
970
|
+
cleanErrorMessage = "Only one CLEAN attestation is allowed per artifact";
|
|
971
|
+
} else if (!cleanFamily || !cleanAnalysis) {
|
|
972
|
+
cleanErrorCode = "invalid-clean-attestation";
|
|
973
|
+
cleanErrorMessage = "CLEAN attestation is valid only for base_explorer or micro_lane output";
|
|
974
|
+
} else if (!cleanAnalysis.valid) {
|
|
975
|
+
cleanErrorCode = cleanAnalysis.issues.some((issue) => ["lane", "micro_lane"].includes(issue.field)) ? cleanFamily === "base_explorer" ? "expected-lane-mismatch" : "expected-micro-lane-mismatch" : "invalid-clean-attestation";
|
|
976
|
+
cleanErrorMessage = cleanAnalysis.issues.map((issue) => `${issue.field}: ${issue.message}`).join("; ");
|
|
977
|
+
} else if (cleanFamily === "base_explorer") {
|
|
978
|
+
pendingClean = {
|
|
979
|
+
record_type: "clean_attestation",
|
|
980
|
+
row_format_family: "base_explorer",
|
|
981
|
+
row_format_version: flags.row_format_version,
|
|
982
|
+
record_version: RECORD_VERSION,
|
|
983
|
+
source_output_ref: input.output_ref,
|
|
984
|
+
source_batch_id: input.batchId,
|
|
985
|
+
source_lane_id: input.laneId,
|
|
986
|
+
source_agent: input.agent,
|
|
987
|
+
source_digest: input.digest,
|
|
988
|
+
extracted_from_partial_source: false,
|
|
989
|
+
sessionId: input.sessionId,
|
|
990
|
+
parentSessionId: input.parentSessionId,
|
|
991
|
+
producer: flags.producer,
|
|
992
|
+
lane: cleanAnalysis.lane,
|
|
993
|
+
coverage_scope: cleanAnalysis.coverageScope,
|
|
994
|
+
evidence: cleanAnalysis.evidence
|
|
995
|
+
};
|
|
996
|
+
} else {
|
|
997
|
+
pendingClean = {
|
|
998
|
+
record_type: "clean_attestation",
|
|
999
|
+
row_format_family: "micro_lane",
|
|
1000
|
+
row_format_version: flags.row_format_version,
|
|
1001
|
+
record_version: RECORD_VERSION,
|
|
1002
|
+
source_output_ref: input.output_ref,
|
|
1003
|
+
source_batch_id: input.batchId,
|
|
1004
|
+
source_lane_id: input.laneId,
|
|
1005
|
+
source_agent: input.agent,
|
|
1006
|
+
source_digest: input.digest,
|
|
1007
|
+
extracted_from_partial_source: false,
|
|
1008
|
+
sessionId: input.sessionId,
|
|
1009
|
+
parentSessionId: input.parentSessionId,
|
|
1010
|
+
producer: flags.producer,
|
|
1011
|
+
micro_lane: cleanAnalysis.lane,
|
|
1012
|
+
coverage_scope: cleanAnalysis.coverageScope,
|
|
1013
|
+
evidence: cleanAnalysis.evidence
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
if (cleanErrorCode) {
|
|
1017
|
+
parseErrorDetails.push({
|
|
1018
|
+
row_index: i,
|
|
1019
|
+
field: "clean_attestation",
|
|
1020
|
+
message: cleanErrorMessage ?? "Invalid CLEAN attestation"
|
|
1021
|
+
});
|
|
1022
|
+
malformedRows++;
|
|
1023
|
+
continue;
|
|
1024
|
+
}
|
|
1025
|
+
if (flags.degraded || input.transcriptIncomplete === true) {
|
|
1026
|
+
cleanErrorCode = "untrusted-clean-attestation";
|
|
1027
|
+
cleanErrorMessage = "CLEAN attestation cannot come from a degraded or partial artifact";
|
|
1028
|
+
parseErrorDetails.push({
|
|
1029
|
+
row_index: i,
|
|
1030
|
+
field: "clean_attestation",
|
|
1031
|
+
message: cleanErrorMessage
|
|
1032
|
+
});
|
|
1033
|
+
malformedRows++;
|
|
1034
|
+
continue;
|
|
1035
|
+
}
|
|
1036
|
+
continue;
|
|
1037
|
+
}
|
|
1038
|
+
const hadCandidateMarker = fields[0]?.trim() === "[CANDIDATE]";
|
|
1039
|
+
if (hadCandidateMarker) {
|
|
1040
|
+
fields = fields.slice(1);
|
|
1041
|
+
}
|
|
1042
|
+
if (fields.length < CANDIDATE_FIELD_COUNT) {
|
|
1043
|
+
if (isCandidateLookingShortRow(fields, flags, hadCandidateMarker)) {
|
|
1044
|
+
parseErrorDetails.push({
|
|
1045
|
+
row_index: i,
|
|
1046
|
+
field: "row",
|
|
1047
|
+
message: "Structurally short [CANDIDATE] row must be a full candidate or CLEAN attestation, not continuation text"
|
|
1048
|
+
});
|
|
1049
|
+
malformedRows++;
|
|
1050
|
+
continue;
|
|
1051
|
+
}
|
|
1052
|
+
if (currentCandidate) {
|
|
1053
|
+
const prev = currentCandidate.evidence_summary ?? "";
|
|
1054
|
+
currentCandidate.evidence_summary = `${prev}
|
|
1055
|
+
${trimmed}`;
|
|
1056
|
+
} else {
|
|
1057
|
+
malformedRows++;
|
|
1058
|
+
}
|
|
1059
|
+
continue;
|
|
1060
|
+
}
|
|
1061
|
+
const rowFields = fields.slice(0, CANDIDATE_FIELD_COUNT);
|
|
1062
|
+
let rowFamily;
|
|
1063
|
+
if (flags.expected_family ?? headerFamily) {
|
|
1064
|
+
rowFamily = flags.expected_family ?? headerFamily;
|
|
1065
|
+
} else {
|
|
1066
|
+
try {
|
|
1067
|
+
rowFamily = detectRowFormatFamily(rowFields);
|
|
1068
|
+
} catch (e) {
|
|
1069
|
+
if (e instanceof UnknownFormatFamilyError) {
|
|
1070
|
+
malformedRows++;
|
|
1071
|
+
continue;
|
|
1072
|
+
}
|
|
1073
|
+
throw e;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
const rowHasImpact = rowFields[7].trim() !== "";
|
|
1077
|
+
const rowHasInvariant = rowFields[6].trim() !== "";
|
|
1078
|
+
if (rowHasImpact && rowHasInvariant && rowFamily === "base_explorer" && flags.expected_family === undefined && headerFamily === undefined) {
|
|
1079
|
+
parseErrorDetails.push({
|
|
1080
|
+
row_index: i,
|
|
1081
|
+
field: "row",
|
|
1082
|
+
message: "Both format-family discriminators present; defaulting to base_explorer"
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
const mapped = mapFields(rowFields, rowFamily);
|
|
1086
|
+
if (rowFamily === "base_explorer" && flags.expected_lane !== undefined && mapped.lane !== flags.expected_lane) {
|
|
1087
|
+
if (mapped.lane !== null && (flags.expected_lanes ?? [flags.expected_lane]).includes(mapped.lane)) {
|
|
1088
|
+
continue;
|
|
1089
|
+
}
|
|
1090
|
+
parseErrorDetails.push({
|
|
1091
|
+
row_index: i,
|
|
1092
|
+
field: "lane",
|
|
1093
|
+
message: `Expected lane ${candidateDiagnosticPreview(flags.expected_lane)}, received ${candidateDiagnosticPreview(mapped.lane ?? "<missing>")}`
|
|
1094
|
+
});
|
|
1095
|
+
malformedRows++;
|
|
1096
|
+
continue;
|
|
1097
|
+
}
|
|
1098
|
+
if (rowFamily === "micro_lane" && flags.expected_micro_lane !== undefined && mapped.micro_lane !== flags.expected_micro_lane) {
|
|
1099
|
+
if (mapped.micro_lane !== null && (flags.expected_micro_lanes ?? [flags.expected_micro_lane]).includes(mapped.micro_lane)) {
|
|
1100
|
+
continue;
|
|
1101
|
+
}
|
|
1102
|
+
parseErrorDetails.push({
|
|
1103
|
+
row_index: i,
|
|
1104
|
+
field: "micro_lane",
|
|
1105
|
+
message: `Expected micro_lane ${candidateDiagnosticPreview(flags.expected_micro_lane)}, received ${candidateDiagnosticPreview(mapped.micro_lane ?? "<missing>")}`
|
|
1106
|
+
});
|
|
1107
|
+
malformedRows++;
|
|
1108
|
+
continue;
|
|
1109
|
+
}
|
|
1110
|
+
const semanticAnalysis = analyzeCandidateFields(fields, rowFamily);
|
|
1111
|
+
if (!semanticAnalysis.valid) {
|
|
1112
|
+
for (const issue of semanticAnalysis.issues) {
|
|
1113
|
+
parseErrorDetails.push({
|
|
1114
|
+
row_index: i,
|
|
1115
|
+
field: issue.field,
|
|
1116
|
+
message: issue.message
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
malformedRows++;
|
|
1120
|
+
continue;
|
|
1121
|
+
}
|
|
1122
|
+
const requiredFields = rowFamily === "base_explorer" ? getRequiredFields("base_explorer") : getRequiredFields("micro_lane");
|
|
1123
|
+
let rowMalformed = false;
|
|
1124
|
+
for (const field of requiredFields) {
|
|
1125
|
+
const value = mapped[field];
|
|
1126
|
+
if (field === "candidate_id") {
|
|
1127
|
+
if (value === null || value === "") {
|
|
1128
|
+
rowMalformed = true;
|
|
1129
|
+
break;
|
|
1130
|
+
}
|
|
1131
|
+
} else if (value === null) {
|
|
1132
|
+
parseErrorDetails.push({
|
|
1133
|
+
row_index: i,
|
|
1134
|
+
field,
|
|
1135
|
+
message: `Missing required field: ${field}`
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
if (rowMalformed) {
|
|
1140
|
+
malformedRows++;
|
|
1141
|
+
continue;
|
|
1142
|
+
}
|
|
1143
|
+
assertNonNullCandidateId(mapped.candidate_id);
|
|
1144
|
+
const candidateId = mapped.candidate_id;
|
|
1145
|
+
const candidate = {
|
|
1146
|
+
record_type: "candidate",
|
|
1147
|
+
row_format_family: rowFamily,
|
|
1148
|
+
row_format_version: flags.row_format_version,
|
|
1149
|
+
record_version: RECORD_VERSION,
|
|
1150
|
+
source_output_ref: input.output_ref,
|
|
1151
|
+
source_batch_id: input.batchId,
|
|
1152
|
+
source_lane_id: input.laneId,
|
|
1153
|
+
source_agent: input.agent,
|
|
1154
|
+
source_digest: input.digest,
|
|
1155
|
+
sessionId: input.sessionId,
|
|
1156
|
+
parentSessionId: input.parentSessionId,
|
|
1157
|
+
producer: flags.producer,
|
|
1158
|
+
extracted_from_partial_source: !!(input.transcriptIncomplete || flags.degraded),
|
|
1159
|
+
candidate_id: candidateId,
|
|
1160
|
+
lane: mapped.lane,
|
|
1161
|
+
micro_lane: mapped.micro_lane,
|
|
1162
|
+
severity: mapped.severity,
|
|
1163
|
+
category: mapped.category,
|
|
1164
|
+
file_line: mapped.file_line,
|
|
1165
|
+
claim: mapped.claim,
|
|
1166
|
+
evidence_summary: mapped.evidence_summary,
|
|
1167
|
+
impact_context: mapped.impact_context,
|
|
1168
|
+
invariant_violated: mapped.invariant_violated,
|
|
1169
|
+
confidence: mapped.confidence
|
|
1170
|
+
};
|
|
1171
|
+
const cid = candidate.candidate_id;
|
|
1172
|
+
idCounts.set(cid, (idCounts.get(cid) ?? 0) + 1);
|
|
1173
|
+
if (!idFirstRows.has(cid)) {
|
|
1174
|
+
idFirstRows.set(cid, i);
|
|
1175
|
+
}
|
|
1176
|
+
formatFamiliesDetected.add(rowFamily);
|
|
1177
|
+
candidates.push(candidate);
|
|
1178
|
+
currentCandidate = candidate;
|
|
1179
|
+
}
|
|
1180
|
+
const duplicateIdWarnings = [];
|
|
1181
|
+
let duplicateIdCount = 0;
|
|
1182
|
+
for (const [id, count] of idCounts) {
|
|
1183
|
+
if (count > 1) {
|
|
1184
|
+
duplicateIdCount++;
|
|
1185
|
+
duplicateIdWarnings.push({
|
|
1186
|
+
candidate_id: candidateDiagnosticPreview(id),
|
|
1187
|
+
occurrences: count
|
|
1188
|
+
});
|
|
1189
|
+
parseErrorDetails.push({
|
|
1190
|
+
row_index: idFirstRows.get(id) ?? -1,
|
|
1191
|
+
field: "candidate_id",
|
|
1192
|
+
message: `Duplicate candidate_id: "${candidateDiagnosticPreview(id)}" appears ${count} times`
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
if (pendingClean && candidates.length > 0) {
|
|
1197
|
+
cleanErrorCode = "conflicting-clean-attestation";
|
|
1198
|
+
cleanErrorMessage = "CLEAN attestation cannot appear with candidate rows";
|
|
1199
|
+
parseErrorDetails.push({
|
|
1200
|
+
row_index: headerIndex,
|
|
1201
|
+
field: "clean_attestation",
|
|
1202
|
+
message: cleanErrorMessage
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
if (pendingClean && !cleanErrorCode && (malformedRows > 0 || parseErrorDetails.length > 0)) {
|
|
1206
|
+
cleanErrorCode = "untrusted-clean-attestation";
|
|
1207
|
+
cleanErrorMessage = "CLEAN attestation requires zero malformed rows and zero parse errors";
|
|
1208
|
+
parseErrorDetails.push({
|
|
1209
|
+
row_index: headerIndex,
|
|
1210
|
+
field: "clean_attestation",
|
|
1211
|
+
message: cleanErrorMessage
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
const cleanAttestation = pendingClean && !cleanErrorCode && candidates.length === 0 ? pendingClean : undefined;
|
|
1215
|
+
if (cleanAttestation) {
|
|
1216
|
+
formatFamiliesDetected.add(cleanAttestation.row_format_family);
|
|
1217
|
+
}
|
|
1218
|
+
const acceptedCandidates = cleanErrorCode ? [] : candidates;
|
|
1219
|
+
const parseErrors = parseErrorDetails.length;
|
|
1220
|
+
const envelope = buildInvocationEnvelope(input, flags, Array.from(formatFamiliesDetected), acceptedCandidates.length, parseErrors, malformedRows, cleanAttestation ? 1 : 0);
|
|
1221
|
+
const diagnostics = {
|
|
1222
|
+
candidate_count: acceptedCandidates.length,
|
|
1223
|
+
parse_errors: parseErrors,
|
|
1224
|
+
parse_error_details: parseErrorDetails,
|
|
1225
|
+
malformed_rows: malformedRows,
|
|
1226
|
+
duplicate_id_count: duplicateIdCount,
|
|
1227
|
+
duplicate_id_warnings: duplicateIdWarnings,
|
|
1228
|
+
degraded_source_count: flags.degraded ? 1 : 0,
|
|
1229
|
+
incomplete_source_count: input.transcriptIncomplete ? 1 : 0,
|
|
1230
|
+
format_families_detected: Array.from(formatFamiliesDetected),
|
|
1231
|
+
clean_attestation_count: cleanAttestation ? 1 : 0
|
|
1232
|
+
};
|
|
1233
|
+
if (acceptedCandidates.length === 0 && !cleanAttestation) {
|
|
1234
|
+
const hint = detectFormatMismatchHint(input.text);
|
|
1235
|
+
if (hint) {
|
|
1236
|
+
diagnostics.format_mismatch_hint = hint;
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
return {
|
|
1240
|
+
...cleanErrorCode ? {
|
|
1241
|
+
error: cleanErrorMessage ?? "Invalid CLEAN attestation",
|
|
1242
|
+
error_code: cleanErrorCode
|
|
1243
|
+
} : {},
|
|
1244
|
+
candidates: acceptedCandidates,
|
|
1245
|
+
...cleanAttestation ? { clean_attestation: cleanAttestation } : {},
|
|
1246
|
+
invocation_envelope: envelope,
|
|
1247
|
+
diagnostics
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
|
|
341
1251
|
// src/background/lane-output-store.ts
|
|
342
1252
|
init_zod();
|
|
343
1253
|
import { createHash } from "crypto";
|
|
@@ -418,6 +1328,167 @@ function digestText(text) {
|
|
|
418
1328
|
return createHash("sha256").update(text).digest("hex");
|
|
419
1329
|
}
|
|
420
1330
|
|
|
1331
|
+
// src/background/pr-review-trigger-contract.ts
|
|
1332
|
+
init_zod();
|
|
1333
|
+
var PR_REVIEW_TRIGGER_DEFINITIONS = [
|
|
1334
|
+
{
|
|
1335
|
+
id: "auth-identity-secrets",
|
|
1336
|
+
scope: "universal",
|
|
1337
|
+
trigger_row: "authentication, authorization, identity, sessions, permissions, secrets, cryptography",
|
|
1338
|
+
micro_lane: "Identity and secret boundaries"
|
|
1339
|
+
},
|
|
1340
|
+
{
|
|
1341
|
+
id: "untrusted-input-boundaries",
|
|
1342
|
+
scope: "universal",
|
|
1343
|
+
trigger_row: "parsing, serialization, queries, templates/rendering, file or network input/output",
|
|
1344
|
+
micro_lane: "Untrusted input and sink analysis"
|
|
1345
|
+
},
|
|
1346
|
+
{
|
|
1347
|
+
id: "subprocess-platform",
|
|
1348
|
+
scope: "universal",
|
|
1349
|
+
trigger_row: "subprocesses, shell commands, filesystem operations, OS/runtime-specific code",
|
|
1350
|
+
micro_lane: "Subprocess and platform safety"
|
|
1351
|
+
},
|
|
1352
|
+
{
|
|
1353
|
+
id: "concurrency-state",
|
|
1354
|
+
scope: "universal",
|
|
1355
|
+
trigger_row: "queues, caches, retries, transactions, locks, state machines, async coordination",
|
|
1356
|
+
micro_lane: "Concurrency and state transitions"
|
|
1357
|
+
},
|
|
1358
|
+
{
|
|
1359
|
+
id: "dependencies-build-release",
|
|
1360
|
+
scope: "universal",
|
|
1361
|
+
trigger_row: "dependency manifests, lockfiles, installers, build scripts, CI, packaging, deployment",
|
|
1362
|
+
micro_lane: "Dependency and delivery integrity"
|
|
1363
|
+
},
|
|
1364
|
+
{
|
|
1365
|
+
id: "api-schema-migrations",
|
|
1366
|
+
scope: "universal",
|
|
1367
|
+
trigger_row: "public API, wire/schema/config/storage formats, migrations, feature flags",
|
|
1368
|
+
micro_lane: "Compatibility and migration safety"
|
|
1369
|
+
},
|
|
1370
|
+
{
|
|
1371
|
+
id: "test-infrastructure",
|
|
1372
|
+
scope: "universal",
|
|
1373
|
+
trigger_row: "tests, mocks, fixtures, harnesses, coverage, CI matrices",
|
|
1374
|
+
micro_lane: "Test validity and isolation"
|
|
1375
|
+
},
|
|
1376
|
+
{
|
|
1377
|
+
id: "ui-accessibility-i18n",
|
|
1378
|
+
scope: "universal",
|
|
1379
|
+
trigger_row: "user interfaces, interaction flows, rendering, accessibility, localization",
|
|
1380
|
+
micro_lane: "UI and human-interface quality"
|
|
1381
|
+
},
|
|
1382
|
+
{
|
|
1383
|
+
id: "privacy-observability",
|
|
1384
|
+
scope: "universal",
|
|
1385
|
+
trigger_row: "telemetry, logs, analytics, traces, retention, diagnostics",
|
|
1386
|
+
micro_lane: "Privacy and observability safety"
|
|
1387
|
+
},
|
|
1388
|
+
{
|
|
1389
|
+
id: "generated-provenance",
|
|
1390
|
+
scope: "universal",
|
|
1391
|
+
trigger_row: "generated, vendored, binary, model-produced, codegen or checked-in build artifacts",
|
|
1392
|
+
micro_lane: "Generated artifact provenance"
|
|
1393
|
+
},
|
|
1394
|
+
{
|
|
1395
|
+
id: "unclassified-risk",
|
|
1396
|
+
scope: "universal",
|
|
1397
|
+
trigger_row: "any changed artifact or behavior not confidently classified by the rows above",
|
|
1398
|
+
micro_lane: "Unclassified high-risk fallback"
|
|
1399
|
+
}
|
|
1400
|
+
];
|
|
1401
|
+
var PR_REVIEW_REQUIRED_TRIGGER_IDS = PR_REVIEW_TRIGGER_DEFINITIONS.map((definition) => definition.id);
|
|
1402
|
+
var TriggerIdSchema = exports_external.enum(PR_REVIEW_REQUIRED_TRIGGER_IDS, { error: "unknown PR_REVIEW trigger_id" });
|
|
1403
|
+
var TriggerEvidenceSchema = exports_external.string().trim().min(1).max(4000);
|
|
1404
|
+
var PrReviewInlineTriggerRowSchema = exports_external.discriminatedUnion("result", [
|
|
1405
|
+
exports_external.object({
|
|
1406
|
+
trigger_id: TriggerIdSchema,
|
|
1407
|
+
result: exports_external.literal("MATCHED"),
|
|
1408
|
+
evidence: TriggerEvidenceSchema
|
|
1409
|
+
}).strict(),
|
|
1410
|
+
exports_external.object({
|
|
1411
|
+
trigger_id: TriggerIdSchema,
|
|
1412
|
+
result: exports_external.literal("NOT_TRIGGERED"),
|
|
1413
|
+
evidence: TriggerEvidenceSchema
|
|
1414
|
+
}).strict()
|
|
1415
|
+
]);
|
|
1416
|
+
var PrReviewPersistedInputRowSchema = exports_external.discriminatedUnion("result", [
|
|
1417
|
+
exports_external.object({
|
|
1418
|
+
trigger_id: TriggerIdSchema,
|
|
1419
|
+
result: exports_external.literal("MATCHED"),
|
|
1420
|
+
evidence: TriggerEvidenceSchema,
|
|
1421
|
+
source_batch_id: exports_external.string().trim().min(1),
|
|
1422
|
+
source_lane_id: exports_external.string().trim().min(1)
|
|
1423
|
+
}).strict(),
|
|
1424
|
+
exports_external.object({
|
|
1425
|
+
trigger_id: TriggerIdSchema,
|
|
1426
|
+
result: exports_external.literal("NOT_TRIGGERED"),
|
|
1427
|
+
evidence: TriggerEvidenceSchema
|
|
1428
|
+
}).strict()
|
|
1429
|
+
]);
|
|
1430
|
+
var TriggerDefinitionFieldShape = {
|
|
1431
|
+
scope: exports_external.literal("universal"),
|
|
1432
|
+
trigger_row: exports_external.string().trim().min(1),
|
|
1433
|
+
micro_lane: exports_external.string().trim().min(1)
|
|
1434
|
+
};
|
|
1435
|
+
var V2MatchedRowSchema = exports_external.object({
|
|
1436
|
+
trigger_id: TriggerIdSchema,
|
|
1437
|
+
...TriggerDefinitionFieldShape,
|
|
1438
|
+
result: exports_external.literal("MATCHED"),
|
|
1439
|
+
evidence: TriggerEvidenceSchema,
|
|
1440
|
+
source_batch_id: exports_external.string().trim().min(1),
|
|
1441
|
+
source_lane_id: exports_external.string().trim().min(1)
|
|
1442
|
+
}).strict();
|
|
1443
|
+
var V2NotTriggeredRowSchema = exports_external.object({
|
|
1444
|
+
trigger_id: TriggerIdSchema,
|
|
1445
|
+
...TriggerDefinitionFieldShape,
|
|
1446
|
+
result: exports_external.literal("NOT_TRIGGERED"),
|
|
1447
|
+
evidence: TriggerEvidenceSchema
|
|
1448
|
+
}).strict();
|
|
1449
|
+
var V2RowSchema = exports_external.union([V2MatchedRowSchema, V2NotTriggeredRowSchema]);
|
|
1450
|
+
var ReceiptEnvelopeSchema = exports_external.object({
|
|
1451
|
+
run_id: exports_external.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/),
|
|
1452
|
+
pr_head_sha: exports_external.string().trim().regex(/^[0-9a-f]{6,64}$/i),
|
|
1453
|
+
base_ref: exports_external.string().trim().regex(/^(?!-)[A-Za-z0-9][A-Za-z0-9._/-]{0,255}$/),
|
|
1454
|
+
base_sha: exports_external.string().trim().regex(/^[0-9a-f]{6,64}$/i),
|
|
1455
|
+
evaluated_at: exports_external.string().datetime(),
|
|
1456
|
+
dispatched_micro_lane_count: exports_external.number().int().min(1)
|
|
1457
|
+
});
|
|
1458
|
+
var V2ReceiptSchema = ReceiptEnvelopeSchema.extend({
|
|
1459
|
+
schema_version: exports_external.literal(2),
|
|
1460
|
+
trigger_count: exports_external.number().int().min(0),
|
|
1461
|
+
matched_count: exports_external.number().int().min(0),
|
|
1462
|
+
not_triggered_count: exports_external.number().int().min(0),
|
|
1463
|
+
no_match_count: exports_external.number().int().min(0),
|
|
1464
|
+
rows: exports_external.array(V2RowSchema)
|
|
1465
|
+
}).strict();
|
|
1466
|
+
var LegacyRowFieldShape = {
|
|
1467
|
+
trigger_id: TriggerIdSchema,
|
|
1468
|
+
result: exports_external.literal("MATCHED"),
|
|
1469
|
+
evidence: TriggerEvidenceSchema,
|
|
1470
|
+
source_batch_id: exports_external.string().trim().min(1),
|
|
1471
|
+
source_lane_id: exports_external.string().trim().min(1)
|
|
1472
|
+
};
|
|
1473
|
+
var LegacyBasicRowSchema = exports_external.object(LegacyRowFieldShape).strict();
|
|
1474
|
+
var LegacyFullRowSchema = exports_external.object({
|
|
1475
|
+
trigger_id: TriggerIdSchema,
|
|
1476
|
+
...TriggerDefinitionFieldShape,
|
|
1477
|
+
result: exports_external.literal("MATCHED"),
|
|
1478
|
+
evidence: TriggerEvidenceSchema,
|
|
1479
|
+
source_batch_id: exports_external.string().trim().min(1),
|
|
1480
|
+
source_lane_id: exports_external.string().trim().min(1)
|
|
1481
|
+
}).strict();
|
|
1482
|
+
var LegacyRowSchema = exports_external.union([LegacyBasicRowSchema, LegacyFullRowSchema]);
|
|
1483
|
+
var V1ReceiptSchema = ReceiptEnvelopeSchema.extend({
|
|
1484
|
+
schema_version: exports_external.literal(1),
|
|
1485
|
+
trigger_count: exports_external.number().int().min(0),
|
|
1486
|
+
matched_count: exports_external.number().int().min(0),
|
|
1487
|
+
no_match_count: exports_external.number().int().min(0),
|
|
1488
|
+
rows: exports_external.array(LegacyRowSchema)
|
|
1489
|
+
}).strict();
|
|
1490
|
+
var UnversionedRowOnlyReceiptSchema = exports_external.object({ rows: exports_external.array(exports_external.unknown()) }).strict();
|
|
1491
|
+
|
|
421
1492
|
// src/hooks/pr-workflow-gate.ts
|
|
422
1493
|
init_constants();
|
|
423
1494
|
init_schema();
|
|
@@ -843,19 +1914,7 @@ var PR_REVIEW_BASE_DIMENSION_IDS = [
|
|
|
843
1914
|
"reliability-performance",
|
|
844
1915
|
"compatibility-delivery"
|
|
845
1916
|
];
|
|
846
|
-
var PR_REVIEW_REQUIRED_MICRO_LANE_IDS =
|
|
847
|
-
"auth-identity-secrets",
|
|
848
|
-
"untrusted-input-boundaries",
|
|
849
|
-
"subprocess-platform",
|
|
850
|
-
"concurrency-state",
|
|
851
|
-
"dependencies-build-release",
|
|
852
|
-
"api-schema-migrations",
|
|
853
|
-
"test-infrastructure",
|
|
854
|
-
"ui-accessibility-i18n",
|
|
855
|
-
"privacy-observability",
|
|
856
|
-
"generated-provenance",
|
|
857
|
-
"unclassified-risk"
|
|
858
|
-
];
|
|
1917
|
+
var PR_REVIEW_REQUIRED_MICRO_LANE_IDS = PR_REVIEW_REQUIRED_TRIGGER_IDS;
|
|
859
1918
|
var PR_REVIEW_BASE_LANE_FLOORS = {
|
|
860
1919
|
S: 1,
|
|
861
1920
|
M: 3,
|
|
@@ -874,9 +1933,16 @@ var RENAME_RETRY_DELAY_MS = 10;
|
|
|
874
1933
|
var STATE_MUTATION_LOCK_MAX_ATTEMPTS = 50;
|
|
875
1934
|
var STATE_MUTATION_LOCK_RETRY_DELAY_MS = 10;
|
|
876
1935
|
var STATE_MUTATION_LOCK_UNINITIALIZED_STALE_MS = 30000;
|
|
1936
|
+
var MAX_COMPLETED_CHECKOUT_LOCK_OWNERS = 64;
|
|
1937
|
+
var CHECKOUT_MUTATION_ACTION_TIMEOUT_MS = 5 * 60000;
|
|
1938
|
+
var MAX_CANDIDATE_ISSUES_PER_ARTIFACT = 8;
|
|
1939
|
+
var MAX_BASE_COVERAGE_DIAGNOSTICS = 8;
|
|
1940
|
+
var MAX_BASE_COVERAGE_DIAGNOSTIC_CHARS = 1000;
|
|
877
1941
|
var WORKFLOW_GATE_DIR = "pr-workflow-gates";
|
|
878
1942
|
var trackedStatesByProjectSession = new Map;
|
|
879
1943
|
var pendingStateMutationsByProjectSession = new Map;
|
|
1944
|
+
var pendingCheckoutMutationsByProject = new Map;
|
|
1945
|
+
var completedCheckoutLockOwners = new Map;
|
|
880
1946
|
var PrReviewBaseDimensionIdSchema = exports_external.enum([
|
|
881
1947
|
"intent-architecture",
|
|
882
1948
|
"correctness-state",
|
|
@@ -1027,6 +2093,7 @@ var PrWorkflowGateStateSchema = exports_external.object({
|
|
|
1027
2093
|
}).strict().optional(),
|
|
1028
2094
|
prReviewBaseDispatches: exports_external.array(PrReviewBaseDispatchRecordSchema).max(MAX_WORKFLOW_BATCHES).optional(),
|
|
1029
2095
|
prReviewBaseDispatch: PrReviewBaseDispatchRecordSchema.optional(),
|
|
2096
|
+
prReviewTriggerLedger: exports_external.array(PrReviewInlineTriggerRowSchema).length(PR_REVIEW_REQUIRED_TRIGGER_IDS.length).optional(),
|
|
1030
2097
|
prReviewTriggerEvalPath: exports_external.string().min(1).optional(),
|
|
1031
2098
|
prReviewValidationBatches: exports_external.array(PrReviewValidationBatchRecordSchema).max(MAX_WORKFLOW_BATCHES).optional(),
|
|
1032
2099
|
prReviewArtifactRunId: exports_external.string().min(1).optional(),
|
|
@@ -1088,7 +2155,7 @@ async function activatePrWorkflow(directory, sessionID, mode, options = {}) {
|
|
|
1088
2155
|
throw new Error(formatCheckoutRecoveryBlock(checkoutState));
|
|
1089
2156
|
}
|
|
1090
2157
|
}
|
|
1091
|
-
const initialHead = options.prHeadSha ? await assertCurrentCheckoutHead(directory, options.prHeadSha) : undefined;
|
|
2158
|
+
const initialHead = options.prHeadSha ? await assertCurrentCheckoutHead(directory, options.prHeadSha, mode) : undefined;
|
|
1092
2159
|
const timestamp = isoNow();
|
|
1093
2160
|
const feedbackTargetUrl = mode === "PR_FEEDBACK" && options.prUrl ? canonicalGitHubPrUrl(options.prUrl) ? options.prUrl : null : undefined;
|
|
1094
2161
|
if (feedbackTargetUrl === null) {
|
|
@@ -1175,35 +2242,46 @@ async function abortPrWorkflow(directory, sessionID, options = {}) {
|
|
|
1175
2242
|
};
|
|
1176
2243
|
}
|
|
1177
2244
|
async function bindPrWorkflowHead(directory, sessionID, prHeadSha) {
|
|
1178
|
-
const state = await requireAnyActiveState(directory, sessionID);
|
|
1179
2245
|
const normalizedHead = normalizePrHeadSha(prHeadSha);
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
2246
|
+
return withPrWorkflowCheckoutMutationLock(directory, async () => withSessionStateMutation(directory, normalizeSessionID(sessionID), async () => {
|
|
2247
|
+
const state = await readPrWorkflowGateStateFromDisk(directory, normalizeSessionID(sessionID));
|
|
2248
|
+
if (!state) {
|
|
2249
|
+
throw new Error(`BLOCKED: no active PR workflow gate for session "${normalizeSessionID(sessionID)}". ` + "The gate must be activated before the PR head can be bound.");
|
|
2250
|
+
}
|
|
2251
|
+
await assertCurrentCheckoutHead(directory, normalizedHead, state.mode);
|
|
2252
|
+
if (state.prHeadSha && state.prHeadSha !== normalizedHead) {
|
|
2253
|
+
throw new Error(`BLOCKED: active ${state.mode} workflow is bound to PR head "${state.prHeadSha}"; received "${normalizedHead}"`);
|
|
2254
|
+
}
|
|
2255
|
+
if (!state.prHeadSha) {
|
|
2256
|
+
await assertPrReviewCleanCheckout(directory, state.mode);
|
|
2257
|
+
}
|
|
2258
|
+
if (!state.prHeadSha && state.mode === "PR_FEEDBACK") {
|
|
2259
|
+
await assertPrFeedbackTrackingCheckout(directory, normalizedHead);
|
|
2260
|
+
}
|
|
2261
|
+
if (state.prHeadSha === normalizedHead) {
|
|
2262
|
+
if (state.mode === "PR_FEEDBACK") {
|
|
2263
|
+
await assertPrFeedbackTrackingCheckout(directory, normalizedHead);
|
|
2264
|
+
}
|
|
2265
|
+
return state;
|
|
2266
|
+
}
|
|
2267
|
+
const nextState = {
|
|
2268
|
+
...state,
|
|
2269
|
+
prHeadSha: normalizedHead,
|
|
2270
|
+
updatedAt: isoNow()
|
|
2271
|
+
};
|
|
2272
|
+
await _test_exports.beforePrFeedbackTrackingPersist?.();
|
|
2273
|
+
return writeStateWhileLocked(directory, nextState);
|
|
2274
|
+
}));
|
|
1198
2275
|
}
|
|
1199
|
-
async function assertCurrentCheckoutHead(directory, expectedHead) {
|
|
2276
|
+
async function assertCurrentCheckoutHead(directory, expectedHead, mode = "PR_REVIEW") {
|
|
1200
2277
|
const normalizedExpected = normalizePrHeadSha(expectedHead);
|
|
1201
2278
|
const currentHead = (await _test_exports.resolveCurrentGitHeadAsync(directory))?.trim();
|
|
1202
2279
|
if (!currentHead) {
|
|
1203
2280
|
throw new Error(`BLOCKED: cannot resolve the current Git HEAD in "${directory}" to verify against PR head "${normalizedExpected}". ` + `This means Git could not resolve HEAD (the working directory may not be a Git repository, ` + `HEAD may be unborn, the commit object may be missing in a shallow clone, the Git binary may not be on PATH, ` + `or the bounded Git invocation may have timed out). ` + `Verify with: git -C "${directory}" rev-parse --verify HEAD^{commit}`);
|
|
1204
2281
|
}
|
|
1205
2282
|
if (currentHead.toLowerCase() !== normalizedExpected.toLowerCase()) {
|
|
1206
|
-
|
|
2283
|
+
const remediation = mode === "PR_FEEDBACK" ? "Check out the intended local tracking branch at that exact head (for example with a safe standalone `gh pr checkout <number-or-url>` or `git switch -c <local> --track <remote>/<branch>`), then retry the bind. Do not create a new detached feedback checkout." : `Run these bare, standalone commands from that directory: if the commit is not present locally, \`git fetch origin <pr-head-ref>\`; then \`git switch --detach ${normalizedExpected}\`. Do not prefix the switch with \`git -C\`; the read-only shell classifier refuses \`git -C ... switch\`.`;
|
|
2284
|
+
throw new Error(`BLOCKED: current checkout HEAD "${currentHead}" does not match PR head "${normalizedExpected}" ` + `(working directory: "${directory}"). ${remediation}`);
|
|
1207
2285
|
}
|
|
1208
2286
|
return normalizedExpected;
|
|
1209
2287
|
}
|
|
@@ -1213,13 +2291,62 @@ async function assertPrReviewCleanCheckout(directory, mode = "PR_REVIEW") {
|
|
|
1213
2291
|
}
|
|
1214
2292
|
}
|
|
1215
2293
|
async function assertPrFeedbackTrackingCheckout(directory, prHeadSha) {
|
|
1216
|
-
const
|
|
1217
|
-
if (!
|
|
1218
|
-
throw new Error("BLOCKED: PR_FEEDBACK requires a
|
|
2294
|
+
const currentHead = (await _test_exports.resolveCurrentGitHeadAsync(directory))?.trim();
|
|
2295
|
+
if (!currentHead) {
|
|
2296
|
+
throw new Error("BLOCKED: PR_FEEDBACK requires a resolved exact Git HEAD before first head bind");
|
|
1219
2297
|
}
|
|
2298
|
+
if (currentHead.toLowerCase() !== prHeadSha.toLowerCase()) {
|
|
2299
|
+
throw new Error(`BLOCKED: PR_FEEDBACK exact head changed before attach; expected ${prHeadSha}, received ${currentHead}`);
|
|
2300
|
+
}
|
|
2301
|
+
const currentUpstream = await _test_exports.resolveCurrentUpstreamPushTargetAsync(directory);
|
|
1220
2302
|
const matchingRemoteRefs = await _test_exports.resolveRemoteRefsContainingHeadAsync(directory, prHeadSha);
|
|
1221
|
-
if (
|
|
1222
|
-
|
|
2303
|
+
if (currentUpstream) {
|
|
2304
|
+
if (matchingRemoteRefs === null) {
|
|
2305
|
+
throw new Error("BLOCKED: PR_FEEDBACK could not verify the current upstream against the exact intake PR head; the bounded Git query failed or timed out");
|
|
2306
|
+
}
|
|
2307
|
+
if (matchingRemoteRefs.includes(currentUpstream.remoteTrackingRef))
|
|
2308
|
+
return;
|
|
2309
|
+
throw new Error(`BLOCKED: PR_FEEDBACK current upstream "${currentUpstream.remoteTrackingRef}" must point to the exact intake PR head "${prHeadSha}"; refusing to replace an existing tracking relationship`);
|
|
2310
|
+
}
|
|
2311
|
+
const candidates = await resolvePrFeedbackTrackingCandidatesAsync(directory, prHeadSha);
|
|
2312
|
+
if (!candidates) {
|
|
2313
|
+
throw new Error(`BLOCKED: PR_FEEDBACK could not inspect tracked branches for exact PR head "${prHeadSha}"; the bounded Git query failed or timed out`);
|
|
2314
|
+
}
|
|
2315
|
+
const { local: localCandidates, remote: remoteCandidates } = candidates;
|
|
2316
|
+
if (localCandidates.length === 1) {
|
|
2317
|
+
await _test_exports.beforePrFeedbackTrackingSwitch?.();
|
|
2318
|
+
if (!await switchPrFeedbackTrackingCandidateAsync(directory, localCandidates[0])) {
|
|
2319
|
+
throw new Error(`BLOCKED: PR_FEEDBACK could not attach exact local tracked branch "${localCandidates[0].branchName}"; it may be checked out in another linked worktree or the bounded Git switch failed`);
|
|
2320
|
+
}
|
|
2321
|
+
} else if (localCandidates.length > 1) {
|
|
2322
|
+
throw new Error(`BLOCKED: PR_FEEDBACK exact head "${prHeadSha}" matches multiple local tracked branches: ${localCandidates.map((candidate) => candidate.branchName).join(", ")}`);
|
|
2323
|
+
} else {
|
|
2324
|
+
if (remoteCandidates.length === 1) {
|
|
2325
|
+
await _test_exports.beforePrFeedbackTrackingSwitch?.();
|
|
2326
|
+
if (!await switchPrFeedbackTrackingCandidateAsync(directory, remoteCandidates[0])) {
|
|
2327
|
+
throw new Error(`BLOCKED: PR_FEEDBACK could not attach exact remote-tracking branch "${remoteCandidates[0].remoteTrackingRef}"; the bounded Git switch failed or timed out`);
|
|
2328
|
+
}
|
|
2329
|
+
} else if (remoteCandidates.length > 1) {
|
|
2330
|
+
throw new Error(`BLOCKED: PR_FEEDBACK exact head "${prHeadSha}" matches multiple remote-tracking refs: ${remoteCandidates.map((candidate) => candidate.remoteTrackingRef).join(", ")}`);
|
|
2331
|
+
} else {
|
|
2332
|
+
throw new Error("BLOCKED: PR_FEEDBACK requires a current local branch or exact remote-tracking ref that points to the intake PR head before the first bind");
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
await _test_exports.afterPrFeedbackTrackingSwitch?.();
|
|
2336
|
+
const attachedHead = (await _test_exports.resolveCurrentGitHeadAsync(directory))?.trim();
|
|
2337
|
+
if (attachedHead?.toLowerCase() !== prHeadSha.toLowerCase()) {
|
|
2338
|
+
throw new Error(`BLOCKED: PR_FEEDBACK exact head changed after attach; expected ${prHeadSha}, received ${attachedHead ?? "(unresolved)"}`);
|
|
2339
|
+
}
|
|
2340
|
+
if (await _test_exports.resolveIsWorkingTreeCleanAsync(directory) !== true) {
|
|
2341
|
+
throw new Error("BLOCKED: PR_FEEDBACK requires a clean working tree after checkout attachment");
|
|
2342
|
+
}
|
|
2343
|
+
const attachedUpstream = await _test_exports.resolveCurrentUpstreamPushTargetAsync(directory);
|
|
2344
|
+
if (!attachedUpstream) {
|
|
2345
|
+
throw new Error("BLOCKED: PR_FEEDBACK could not resolve the attached branch upstream after checkout promotion");
|
|
2346
|
+
}
|
|
2347
|
+
const attachedRemoteRefs = await _test_exports.resolveRemoteRefsContainingHeadAsync(directory, prHeadSha);
|
|
2348
|
+
if (!attachedRemoteRefs?.includes(attachedUpstream.remoteTrackingRef)) {
|
|
2349
|
+
throw new Error(`BLOCKED: PR_FEEDBACK upstream "${attachedUpstream.remoteTrackingRef}" must point to the exact intake PR head "${prHeadSha}" before the first head bind`);
|
|
1223
2350
|
}
|
|
1224
2351
|
}
|
|
1225
2352
|
async function assertPrReviewBaseCoverageSettled(directory, sessionID) {
|
|
@@ -1229,15 +2356,16 @@ async function assertPrReviewBaseCoverageSettled(directory, sessionID) {
|
|
|
1229
2356
|
throw new Error("BLOCKED: PR_REVIEW requires at least one base batch");
|
|
1230
2357
|
}
|
|
1231
2358
|
const covered = new Set;
|
|
2359
|
+
const malformedDiagnostics = [];
|
|
1232
2360
|
for (const batch of batches) {
|
|
1233
|
-
const successful = successfulObligationsFromExactBatch(directory, state, batch.batchId, batch.lanes, "swarm-pr-review:base", batch.validatedAt);
|
|
2361
|
+
const successful = successfulObligationsFromExactBatch(directory, state, batch.batchId, batch.lanes, "swarm-pr-review:base", batch.validatedAt, true, new Set, undefined, malformedDiagnostics);
|
|
1234
2362
|
for (const obligation of successful) {
|
|
1235
2363
|
covered.add(obligation);
|
|
1236
2364
|
}
|
|
1237
2365
|
}
|
|
1238
2366
|
const missing = PR_REVIEW_BASE_DIMENSION_IDS.filter((dimension) => !covered.has(dimension));
|
|
1239
2367
|
if (missing.length > 0 || covered.size !== PR_REVIEW_BASE_DIMENSION_IDS.length) {
|
|
1240
|
-
throw new Error(`BLOCKED: PR_REVIEW base coverage is incomplete; missing dimensions: ${missing.join(", ") || "(none)"}`);
|
|
2368
|
+
throw new Error(`BLOCKED: PR_REVIEW base coverage is incomplete; missing dimensions: ${missing.join(", ") || "(none)"}${malformedDiagnostics.length > 0 ? `; malformed candidate diagnostics: ${malformedDiagnostics.join(" | ")}` : ""}`);
|
|
1241
2369
|
}
|
|
1242
2370
|
return state;
|
|
1243
2371
|
}
|
|
@@ -1607,24 +2735,42 @@ async function transitionPrReviewToFeedback(directory, sessionID, request) {
|
|
|
1607
2735
|
var _test_exports = {
|
|
1608
2736
|
workflowGateStateRelativePath,
|
|
1609
2737
|
workflowGateStateLockRelativePath,
|
|
2738
|
+
workflowCheckoutMutationLockRelativePath,
|
|
2739
|
+
withPrWorkflowCheckoutMutationLock,
|
|
1610
2740
|
resetTrackedStateCache: () => {
|
|
1611
2741
|
trackedStatesByProjectSession.clear();
|
|
1612
2742
|
pendingStateMutationsByProjectSession.clear();
|
|
2743
|
+
pendingCheckoutMutationsByProject.clear();
|
|
2744
|
+
completedCheckoutLockOwners.clear();
|
|
1613
2745
|
_test_exports.beforeTerminalClear = undefined;
|
|
1614
2746
|
_test_exports.beforePrFeedbackTransitionLock = undefined;
|
|
2747
|
+
_test_exports.beforePrFeedbackTrackingSwitch = undefined;
|
|
2748
|
+
_test_exports.afterPrFeedbackTrackingSwitch = undefined;
|
|
2749
|
+
_test_exports.beforePrFeedbackTrackingPersist = undefined;
|
|
1615
2750
|
_test_exports.beforeBoundedSwarmFileOpen = undefined;
|
|
1616
2751
|
_test_exports.beforeSessionStateLockWrite = undefined;
|
|
2752
|
+
_test_exports.beforeCheckoutLockWrite = undefined;
|
|
1617
2753
|
_test_exports.beforeSafeDirectoryCreate = undefined;
|
|
1618
2754
|
_test_exports.beforeAtomicTempWrite = undefined;
|
|
1619
2755
|
_test_exports.beforeAtomicRename = undefined;
|
|
2756
|
+
_test_exports.openCheckoutLock = openCheckoutLockFile;
|
|
2757
|
+
_test_exports.removeCheckoutLock = removeCheckoutLockFile;
|
|
2758
|
+
_test_exports.checkoutMutationActionTimeoutMs = CHECKOUT_MUTATION_ACTION_TIMEOUT_MS;
|
|
1620
2759
|
},
|
|
1621
2760
|
beforeTerminalClear: undefined,
|
|
1622
2761
|
beforePrFeedbackTransitionLock: undefined,
|
|
2762
|
+
beforePrFeedbackTrackingSwitch: undefined,
|
|
2763
|
+
afterPrFeedbackTrackingSwitch: undefined,
|
|
2764
|
+
beforePrFeedbackTrackingPersist: undefined,
|
|
1623
2765
|
beforeBoundedSwarmFileOpen: undefined,
|
|
1624
2766
|
beforeSessionStateLockWrite: undefined,
|
|
2767
|
+
beforeCheckoutLockWrite: undefined,
|
|
1625
2768
|
beforeSafeDirectoryCreate: undefined,
|
|
1626
2769
|
beforeAtomicTempWrite: undefined,
|
|
1627
2770
|
beforeAtomicRename: undefined,
|
|
2771
|
+
openCheckoutLock: openCheckoutLockFile,
|
|
2772
|
+
removeCheckoutLock: removeCheckoutLockFile,
|
|
2773
|
+
checkoutMutationActionTimeoutMs: CHECKOUT_MUTATION_ACTION_TIMEOUT_MS,
|
|
1628
2774
|
resolveCurrentGitHead,
|
|
1629
2775
|
resolveCurrentGitHeadAsync,
|
|
1630
2776
|
resolveCurrentUpstreamPushTarget,
|
|
@@ -1665,7 +2811,7 @@ async function requireBoundState(directory, sessionID, expectedMode) {
|
|
|
1665
2811
|
throw new Error(`BLOCKED: active ${expectedMode} workflow is not bound to a PR head`);
|
|
1666
2812
|
}
|
|
1667
2813
|
if (expectedMode === "PR_REVIEW") {
|
|
1668
|
-
await assertCurrentCheckoutHead(directory, state.prHeadSha);
|
|
2814
|
+
await assertCurrentCheckoutHead(directory, state.prHeadSha, "PR_REVIEW");
|
|
1669
2815
|
await assertPrReviewCleanCheckout(directory);
|
|
1670
2816
|
}
|
|
1671
2817
|
return state;
|
|
@@ -1727,6 +2873,15 @@ function normalizePrHeadSha(prHeadSha) {
|
|
|
1727
2873
|
function sameStringArray(left, right) {
|
|
1728
2874
|
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
1729
2875
|
}
|
|
2876
|
+
function appendBoundedCandidateIssue(issues, message) {
|
|
2877
|
+
if (issues.length < MAX_CANDIDATE_ISSUES_PER_ARTIFACT - 1) {
|
|
2878
|
+
issues.push(message.slice(0, MAX_BASE_COVERAGE_DIAGNOSTIC_CHARS));
|
|
2879
|
+
return;
|
|
2880
|
+
}
|
|
2881
|
+
if (issues.length === MAX_CANDIDATE_ISSUES_PER_ARTIFACT - 1) {
|
|
2882
|
+
issues.push("additional malformed candidate diagnostics omitted");
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
1730
2885
|
function derivePrReviewCriticInventory(directory, state) {
|
|
1731
2886
|
const verdicts = deriveLatestPrReviewReviewerVerdicts(directory, state);
|
|
1732
2887
|
return [...verdicts.entries()].filter(([, verdict]) => verdict.classification === "CONFIRMED" && ["CRITICAL", "HIGH", "MEDIUM"].includes(verdict.severity)).map(([itemId]) => itemId).sort();
|
|
@@ -1760,7 +2915,7 @@ function deriveLatestPrReviewReviewerVerdicts(directory, state) {
|
|
|
1760
2915
|
}
|
|
1761
2916
|
return new Map;
|
|
1762
2917
|
}
|
|
1763
|
-
function successfulObligationsFromExactBatch(directory, state, batchId, expectedLanes, expectedMode, validatedAt, checkWorkflowLane = true, forbiddenSubagentSessionIds = new Set, expectedRevisionDigest) {
|
|
2918
|
+
function successfulObligationsFromExactBatch(directory, state, batchId, expectedLanes, expectedMode, validatedAt, checkWorkflowLane = true, forbiddenSubagentSessionIds = new Set, expectedRevisionDigest, diagnostics) {
|
|
1764
2919
|
const records = findByBatchId(directory, batchId, {
|
|
1765
2920
|
parentSessionId: state.sessionID
|
|
1766
2921
|
});
|
|
@@ -1787,7 +2942,7 @@ function successfulObligationsFromExactBatch(directory, state, batchId, expected
|
|
|
1787
2942
|
const expectedWorkflowLane = expectedLane?.workflowLane;
|
|
1788
2943
|
const expectedOwnedLanes = expectedLane?.ownedWorkflowLanes?.length ? expectedLane.ownedWorkflowLanes : expectedWorkflowLane !== undefined ? [expectedWorkflowLane] : undefined;
|
|
1789
2944
|
const subagentSessionId = record.subagentSessionId?.trim();
|
|
1790
|
-
if (expectedWorkflowLane !== undefined && expectedOwnedLanes !== undefined && record.laneId !== undefined && recordCountByLaneId.get(record.laneId) === 1 && Boolean(subagentSessionId) && recordCountBySubagentSessionId.get(subagentSessionId) === 1 && !forbiddenSubagentSessionIds.has(subagentSessionId) && record.createdAt >= validatedAtMs && (!checkWorkflowLane || record.workflowLane === expectedWorkflowLane) && (!checkWorkflowLane || ownedLaneSetsEqual(record.ownedWorkflowLanes, expectedOwnedLanes)) && record.mode === expectedMode && record.workspace?.prHeadSha === state.prHeadSha && record.workspace?.gitHead === state.prHeadSha && record.status === "completed" && record.result?.outputDegraded !== true && record.result?.transcriptIncomplete !== true && record.result?.truncated !== true && (record.result?.chars ?? 0) > 0 && Boolean(record.result?.digest?.trim()) && Boolean(record.result?.outputRef?.trim()) && workflowArtifactHasContractMarker(directory, state, record, expectedMode, expectedWorkflowLane, expectedLane?.reviewItemIds, expectedRevisionDigest, expectedOwnedLanes)) {
|
|
2945
|
+
if (expectedWorkflowLane !== undefined && expectedOwnedLanes !== undefined && record.laneId !== undefined && recordCountByLaneId.get(record.laneId) === 1 && Boolean(subagentSessionId) && recordCountBySubagentSessionId.get(subagentSessionId) === 1 && !forbiddenSubagentSessionIds.has(subagentSessionId) && record.createdAt >= validatedAtMs && (!checkWorkflowLane || record.workflowLane === expectedWorkflowLane) && (!checkWorkflowLane || ownedLaneSetsEqual(record.ownedWorkflowLanes, expectedOwnedLanes)) && record.mode === expectedMode && record.workspace?.prHeadSha === state.prHeadSha && record.workspace?.gitHead === state.prHeadSha && record.status === "completed" && record.result?.outputDegraded !== true && record.result?.transcriptIncomplete !== true && record.result?.truncated !== true && (record.result?.chars ?? 0) > 0 && Boolean(record.result?.digest?.trim()) && Boolean(record.result?.outputRef?.trim()) && workflowArtifactHasContractMarker(directory, state, record, expectedMode, expectedWorkflowLane, expectedLane?.reviewItemIds, expectedRevisionDigest, expectedOwnedLanes, diagnostics)) {
|
|
1791
2946
|
for (const obligation of expectedOwnedLanes) {
|
|
1792
2947
|
successful.add(obligation);
|
|
1793
2948
|
}
|
|
@@ -1806,7 +2961,7 @@ function ownedLaneSetsEqual(recordOwned, expectedOwned) {
|
|
|
1806
2961
|
}
|
|
1807
2962
|
return recordOwned.every((owned) => expectedSet.has(owned));
|
|
1808
2963
|
}
|
|
1809
|
-
function workflowArtifactHasContractMarker(directory, state, record, expectedMode, expectedWorkflowLane, reviewItemIds, expectedRevisionDigest, ownedWorkflowLanes) {
|
|
2964
|
+
function workflowArtifactHasContractMarker(directory, state, record, expectedMode, expectedWorkflowLane, reviewItemIds, expectedRevisionDigest, ownedWorkflowLanes, diagnostics) {
|
|
1810
2965
|
const ref = record.result?.outputRef?.trim();
|
|
1811
2966
|
if (!ref)
|
|
1812
2967
|
return false;
|
|
@@ -1841,13 +2996,25 @@ function workflowArtifactHasContractMarker(directory, state, record, expectedMod
|
|
|
1841
2996
|
return Boolean(reviewItemIds?.length && reviewItemIds.every((itemId) => artifactHasExactPositiveVerdictRow(artifact.text, feedbackVerdict[0], itemId, feedbackVerdict[1])));
|
|
1842
2997
|
}
|
|
1843
2998
|
const coveredLanes = ownedWorkflowLanes?.length ? ownedWorkflowLanes : [expectedWorkflowLane];
|
|
1844
|
-
|
|
2999
|
+
const coverageAnalyses = coveredLanes.map((coveredLane) => ({
|
|
3000
|
+
coveredLane,
|
|
3001
|
+
analysis: analyzePrReviewDiscoveryArtifact(artifact.text, coveredLane, coveredLanes)
|
|
3002
|
+
}));
|
|
3003
|
+
if (coverageAnalyses.some(({ analysis }) => !analysis.covered)) {
|
|
3004
|
+
for (const { coveredLane, analysis } of coverageAnalyses) {
|
|
3005
|
+
if (analysis.covered || analysis.issues.length === 0)
|
|
3006
|
+
continue;
|
|
3007
|
+
if (diagnostics && diagnostics.length < MAX_BASE_COVERAGE_DIAGNOSTICS) {
|
|
3008
|
+
const diagnostic = `batch=${record.batchId ?? "(missing)"} lane=${record.laneId ?? "(missing)"} workflow_lane=${coveredLane} output_ref=${ref} digest=${record.result?.digest ?? "(missing)"}: ${analysis.issues.join("; ")}`;
|
|
3009
|
+
diagnostics.push(diagnostic.slice(0, MAX_BASE_COVERAGE_DIAGNOSTIC_CHARS));
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
1845
3012
|
return false;
|
|
1846
3013
|
}
|
|
1847
3014
|
if (coveredLanes.length > 1) {
|
|
1848
3015
|
const seenEvidence = new Map;
|
|
1849
3016
|
for (const coveredLane of coveredLanes) {
|
|
1850
|
-
const evidence2 = extractLaneCoverageEvidenceText(artifact.text, coveredLane);
|
|
3017
|
+
const evidence2 = extractLaneCoverageEvidenceText(artifact.text, coveredLane, coveredLanes);
|
|
1851
3018
|
if (evidence2 === null)
|
|
1852
3019
|
continue;
|
|
1853
3020
|
const priorLane = seenEvidence.get(evidence2);
|
|
@@ -1858,51 +3025,78 @@ function workflowArtifactHasContractMarker(directory, state, record, expectedMod
|
|
|
1858
3025
|
}
|
|
1859
3026
|
return true;
|
|
1860
3027
|
}
|
|
1861
|
-
function
|
|
1862
|
-
const
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
3028
|
+
function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkflowLanes = [expectedWorkflowLane]) {
|
|
3029
|
+
const fallbackFamily = PR_REVIEW_BASE_DIMENSION_IDS.includes(expectedWorkflowLane) ? "base_explorer" : "micro_lane";
|
|
3030
|
+
const issues = [];
|
|
3031
|
+
const canonicalText = removeCandidateCodeFences(text);
|
|
3032
|
+
const firstTabularFields = canonicalText.split(/\r?\n/).map((line) => splitPipeFields(line).map((field) => field.trim())).find((fields) => fields.length >= 2 && fields.some((field) => field.length > 0));
|
|
3033
|
+
const headerFamily = firstTabularFields ? candidateHeaderFamily(firstTabularFields) : null;
|
|
3034
|
+
if (headerFamily !== fallbackFamily) {
|
|
3035
|
+
appendBoundedCandidateIssue(issues, `field header: expected one exact canonical ${fallbackFamily} [CANDIDATE] header before discovery rows`);
|
|
3036
|
+
return { covered: false, evidence: null, issues };
|
|
3037
|
+
}
|
|
3038
|
+
const parsed = parseCandidates({
|
|
3039
|
+
output_ref: "pr-workflow:discovery-artifact",
|
|
3040
|
+
batchId: "pr-workflow",
|
|
3041
|
+
laneId: expectedWorkflowLane,
|
|
3042
|
+
agent: "explorer",
|
|
3043
|
+
role: "explorer",
|
|
3044
|
+
digest: "0".repeat(64),
|
|
3045
|
+
text: canonicalText,
|
|
3046
|
+
artifact_status: "ok",
|
|
3047
|
+
source: "collect_lane_results",
|
|
3048
|
+
produced_at: "1970-01-01T00:00:00.000Z"
|
|
3049
|
+
}, {
|
|
3050
|
+
accept_partial: false,
|
|
3051
|
+
accept_degraded: false,
|
|
3052
|
+
degraded: false,
|
|
3053
|
+
row_format_version: 1,
|
|
3054
|
+
producer: "swarm-pr-review",
|
|
3055
|
+
expected_family: fallbackFamily,
|
|
3056
|
+
...fallbackFamily === "base_explorer" ? {
|
|
3057
|
+
expected_lane: expectedWorkflowLane,
|
|
3058
|
+
expected_lanes: [...ownedWorkflowLanes]
|
|
3059
|
+
} : {
|
|
3060
|
+
expected_micro_lane: expectedWorkflowLane,
|
|
3061
|
+
expected_micro_lanes: [...ownedWorkflowLanes]
|
|
1878
3062
|
}
|
|
1879
|
-
|
|
1880
|
-
|
|
3063
|
+
});
|
|
3064
|
+
if (parsed.error)
|
|
3065
|
+
appendBoundedCandidateIssue(issues, parsed.error);
|
|
3066
|
+
for (const detail of parsed.diagnostics.parse_error_details) {
|
|
3067
|
+
appendBoundedCandidateIssue(issues, `row ${detail.row_index + 1} field ${detail.field}: ${detail.message}`);
|
|
1881
3068
|
}
|
|
1882
|
-
|
|
1883
|
-
}
|
|
1884
|
-
function extractLaneCoverageEvidenceText(text, expectedWorkflowLane) {
|
|
1885
|
-
const lines = text.split(/\r?\n/);
|
|
1886
|
-
let candidateHeaderSeen = false;
|
|
1887
|
-
for (const line of lines) {
|
|
1888
|
-
const fields = pipeFields(line);
|
|
1889
|
-
if (fields.length === 0)
|
|
1890
|
-
continue;
|
|
1891
|
-
if (fields[0] === "[CLEAN]" && fields.length >= 4 && fields[1] === expectedWorkflowLane && fields.slice(1, 4).every(Boolean) && fields[2].length >= 12 && fields[3].length >= 20)
|
|
1892
|
-
return `${fields[2]}\x00${fields[3]}`;
|
|
1893
|
-
if (fields[0] === "[CANDIDATE]") {
|
|
1894
|
-
if (fields[1]?.toLowerCase() === "candidate_id") {
|
|
1895
|
-
candidateHeaderSeen = true;
|
|
1896
|
-
continue;
|
|
1897
|
-
}
|
|
1898
|
-
if (fields.length >= 10 && fields[2] === expectedWorkflowLane && fields.slice(1, 10).every(Boolean))
|
|
1899
|
-
return fields.slice(3, 10).join("\x00");
|
|
1900
|
-
continue;
|
|
1901
|
-
}
|
|
1902
|
-
if (candidateHeaderSeen && fields.length >= 9 && fields[0]?.toLowerCase() !== "candidate_id" && fields[1] === expectedWorkflowLane && fields.slice(0, 9).every(Boolean))
|
|
1903
|
-
return fields.slice(2, 9).join("\x00");
|
|
3069
|
+
if (parsed.error || parsed.diagnostics.parse_error_details.length > 0) {
|
|
3070
|
+
return { covered: false, evidence: null, issues };
|
|
1904
3071
|
}
|
|
1905
|
-
|
|
3072
|
+
const matchingCandidate = parsed.candidates.find((candidate) => (candidate.lane ?? candidate.micro_lane) === expectedWorkflowLane);
|
|
3073
|
+
if (matchingCandidate) {
|
|
3074
|
+
return {
|
|
3075
|
+
covered: true,
|
|
3076
|
+
evidence: [
|
|
3077
|
+
matchingCandidate.severity,
|
|
3078
|
+
matchingCandidate.category,
|
|
3079
|
+
matchingCandidate.file_line,
|
|
3080
|
+
matchingCandidate.claim,
|
|
3081
|
+
matchingCandidate.evidence_summary,
|
|
3082
|
+
matchingCandidate.impact_context ?? matchingCandidate.invariant_violated,
|
|
3083
|
+
matchingCandidate.confidence
|
|
3084
|
+
].join("\x00"),
|
|
3085
|
+
issues
|
|
3086
|
+
};
|
|
3087
|
+
}
|
|
3088
|
+
const clean = parsed.clean_attestation;
|
|
3089
|
+
if (clean && (clean.row_format_family === "base_explorer" ? clean.lane : clean.micro_lane) === expectedWorkflowLane) {
|
|
3090
|
+
return {
|
|
3091
|
+
covered: true,
|
|
3092
|
+
evidence: `${clean.coverage_scope}\x00${clean.evidence}`,
|
|
3093
|
+
issues
|
|
3094
|
+
};
|
|
3095
|
+
}
|
|
3096
|
+
return { covered: false, evidence: null, issues };
|
|
3097
|
+
}
|
|
3098
|
+
function extractLaneCoverageEvidenceText(text, expectedWorkflowLane, ownedWorkflowLanes = [expectedWorkflowLane]) {
|
|
3099
|
+
return analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkflowLanes).evidence;
|
|
1906
3100
|
}
|
|
1907
3101
|
var REVIEWER_CLASSIFICATIONS = new Set([
|
|
1908
3102
|
"CONFIRMED",
|
|
@@ -2023,6 +3217,258 @@ async function writeStateWhileLocked(directory, state, options = {}) {
|
|
|
2023
3217
|
rememberState(directory, nextState);
|
|
2024
3218
|
return nextState;
|
|
2025
3219
|
}
|
|
3220
|
+
function workflowCheckoutMutationLockRelativePath() {
|
|
3221
|
+
return path4.join(WORKFLOW_GATE_DIR, "checkout.lock");
|
|
3222
|
+
}
|
|
3223
|
+
function workflowCheckoutMutationLockPath(directory) {
|
|
3224
|
+
return validateSwarmPath(directory, workflowCheckoutMutationLockRelativePath());
|
|
3225
|
+
}
|
|
3226
|
+
function openCheckoutLockFile(lockPath) {
|
|
3227
|
+
return fsp2.open(lockPath, "wx");
|
|
3228
|
+
}
|
|
3229
|
+
function removeCheckoutLockFile(lockPath) {
|
|
3230
|
+
return fsp2.rm(lockPath);
|
|
3231
|
+
}
|
|
3232
|
+
function checkoutMutationProjectKey(directory) {
|
|
3233
|
+
return normalizeComparableFsPath(directory);
|
|
3234
|
+
}
|
|
3235
|
+
|
|
3236
|
+
class PrWorkflowCheckoutMutationTimeoutError extends Error {
|
|
3237
|
+
phase;
|
|
3238
|
+
code = "PR_WORKFLOW_CHECKOUT_MUTATION_TIMEOUT";
|
|
3239
|
+
retryable = false;
|
|
3240
|
+
constructor(phase) {
|
|
3241
|
+
super(phase === "queue" ? "BLOCKED: timed out waiting for the active PR workflow checkout mutation; the existing owner still holds serialization and must settle before retrying" : "BLOCKED: PR workflow checkout mutation exceeded its execution deadline; serialization remains held until the in-flight action actually settles");
|
|
3242
|
+
this.phase = phase;
|
|
3243
|
+
this.name = "PrWorkflowCheckoutMutationTimeoutError";
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
async function withCheckoutMutationDeadline(promise, phase) {
|
|
3247
|
+
let timeout;
|
|
3248
|
+
try {
|
|
3249
|
+
return await Promise.race([
|
|
3250
|
+
promise,
|
|
3251
|
+
new Promise((resolve3) => {
|
|
3252
|
+
timeout = setTimeout(() => {
|
|
3253
|
+
resolve3({
|
|
3254
|
+
status: "timeout",
|
|
3255
|
+
error: new PrWorkflowCheckoutMutationTimeoutError(phase)
|
|
3256
|
+
});
|
|
3257
|
+
}, _test_exports.checkoutMutationActionTimeoutMs);
|
|
3258
|
+
})
|
|
3259
|
+
]);
|
|
3260
|
+
} finally {
|
|
3261
|
+
if (timeout)
|
|
3262
|
+
clearTimeout(timeout);
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3265
|
+
async function withPrWorkflowCheckoutMutationLock(directory, action) {
|
|
3266
|
+
const key = checkoutMutationProjectKey(directory);
|
|
3267
|
+
const previous = pendingCheckoutMutationsByProject.get(key) ?? Promise.resolve();
|
|
3268
|
+
let release;
|
|
3269
|
+
const current = new Promise((resolve3) => {
|
|
3270
|
+
release = resolve3;
|
|
3271
|
+
});
|
|
3272
|
+
const queued = previous.then(() => current);
|
|
3273
|
+
pendingCheckoutMutationsByProject.set(key, queued);
|
|
3274
|
+
const previousResult = await withCheckoutMutationDeadline(previous.then(() => ({ status: "ready" })), "queue");
|
|
3275
|
+
if (previousResult.status === "timeout") {
|
|
3276
|
+
release();
|
|
3277
|
+
queued.then(() => {
|
|
3278
|
+
if (pendingCheckoutMutationsByProject.get(key) === queued) {
|
|
3279
|
+
pendingCheckoutMutationsByProject.delete(key);
|
|
3280
|
+
}
|
|
3281
|
+
});
|
|
3282
|
+
throw previousResult.error;
|
|
3283
|
+
}
|
|
3284
|
+
let lock;
|
|
3285
|
+
try {
|
|
3286
|
+
lock = await acquireCheckoutMutationLock(directory);
|
|
3287
|
+
} catch (error2) {
|
|
3288
|
+
release();
|
|
3289
|
+
if (pendingCheckoutMutationsByProject.get(key) === queued) {
|
|
3290
|
+
pendingCheckoutMutationsByProject.delete(key);
|
|
3291
|
+
}
|
|
3292
|
+
throw error2;
|
|
3293
|
+
}
|
|
3294
|
+
const actionOutcome = Promise.resolve().then(action).then((value) => ({ status: "fulfilled", value }), (error2) => ({ status: "rejected", error: error2 }));
|
|
3295
|
+
const outcome = await withCheckoutMutationDeadline(actionOutcome, "action");
|
|
3296
|
+
if (outcome.status === "timeout") {
|
|
3297
|
+
actionOutcome.then(async () => {
|
|
3298
|
+
try {
|
|
3299
|
+
await releaseCheckoutMutationLock(lock);
|
|
3300
|
+
} catch {} finally {
|
|
3301
|
+
release();
|
|
3302
|
+
if (pendingCheckoutMutationsByProject.get(key) === queued) {
|
|
3303
|
+
pendingCheckoutMutationsByProject.delete(key);
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
});
|
|
3307
|
+
throw outcome.error;
|
|
3308
|
+
}
|
|
3309
|
+
try {
|
|
3310
|
+
if (outcome.status === "rejected")
|
|
3311
|
+
throw outcome.error;
|
|
3312
|
+
return outcome.value;
|
|
3313
|
+
} finally {
|
|
3314
|
+
try {
|
|
3315
|
+
await releaseCheckoutMutationLock(lock);
|
|
3316
|
+
} finally {
|
|
3317
|
+
release();
|
|
3318
|
+
if (pendingCheckoutMutationsByProject.get(key) === queued) {
|
|
3319
|
+
pendingCheckoutMutationsByProject.delete(key);
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
}
|
|
3324
|
+
async function acquireCheckoutMutationLock(directory) {
|
|
3325
|
+
const lockPath = workflowCheckoutMutationLockPath(directory);
|
|
3326
|
+
const verifiedParent = await ensurePrWorkflowSafeParentDirectory(directory, lockPath);
|
|
3327
|
+
for (let attempt = 0;attempt < STATE_MUTATION_LOCK_MAX_ATTEMPTS; attempt++) {
|
|
3328
|
+
try {
|
|
3329
|
+
const handle = await _test_exports.openCheckoutLock(lockPath);
|
|
3330
|
+
const lock = {
|
|
3331
|
+
ownerToken: randomUUID(),
|
|
3332
|
+
pid: process.pid,
|
|
3333
|
+
createdAtMs: _test_exports.nowMs()
|
|
3334
|
+
};
|
|
3335
|
+
let lockIdentity;
|
|
3336
|
+
let writeError;
|
|
3337
|
+
try {
|
|
3338
|
+
const openedStat = await handle.stat({ bigint: true });
|
|
3339
|
+
lockIdentity = { dev: openedStat.dev, ino: openedStat.ino };
|
|
3340
|
+
lockIdentity = await assertOpenedSwarmFileIdentity(directory, lockPath, handle, verifiedParent, "PR workflow checkout mutation lock");
|
|
3341
|
+
await _test_exports.beforeCheckoutLockWrite?.();
|
|
3342
|
+
await handle.writeFile(JSON.stringify(lock), "utf-8");
|
|
3343
|
+
} catch (error2) {
|
|
3344
|
+
writeError = error2;
|
|
3345
|
+
} finally {
|
|
3346
|
+
await handle.close().catch(() => {
|
|
3347
|
+
return;
|
|
3348
|
+
});
|
|
3349
|
+
}
|
|
3350
|
+
if (writeError) {
|
|
3351
|
+
if (lockIdentity) {
|
|
3352
|
+
await removeCheckoutMutationLockByIdentity(directory, lockPath, lockIdentity, verifiedParent);
|
|
3353
|
+
}
|
|
3354
|
+
throw writeError;
|
|
3355
|
+
}
|
|
3356
|
+
return { path: lockPath, ownerToken: lock.ownerToken };
|
|
3357
|
+
} catch (error2) {
|
|
3358
|
+
if (error2.code !== "EEXIST")
|
|
3359
|
+
throw error2;
|
|
3360
|
+
if (await reclaimAbandonedCheckoutMutationLock(lockPath))
|
|
3361
|
+
continue;
|
|
3362
|
+
if (attempt < STATE_MUTATION_LOCK_MAX_ATTEMPTS - 1) {
|
|
3363
|
+
await delay(STATE_MUTATION_LOCK_RETRY_DELAY_MS);
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
throw new Error("BLOCKED: PR workflow checkout mutation is being handled by another process; retry after that checkout settles");
|
|
3368
|
+
}
|
|
3369
|
+
async function releaseCheckoutMutationLock(lock) {
|
|
3370
|
+
let lastError;
|
|
3371
|
+
for (let attempt = 0;attempt < WINDOWS_RENAME_MAX_RETRIES; attempt++) {
|
|
3372
|
+
try {
|
|
3373
|
+
if (await removeCheckoutMutationLockIfOwned(lock.path, lock.ownerToken)) {
|
|
3374
|
+
completedCheckoutLockOwners.delete(lock.path);
|
|
3375
|
+
return;
|
|
3376
|
+
}
|
|
3377
|
+
lastError = new Error("PR workflow checkout mutation lock ownership changed before release");
|
|
3378
|
+
} catch (error2) {
|
|
3379
|
+
lastError = error2;
|
|
3380
|
+
}
|
|
3381
|
+
if (attempt < WINDOWS_RENAME_MAX_RETRIES - 1) {
|
|
3382
|
+
await delay(RENAME_RETRY_DELAY_MS);
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
rememberCompletedCheckoutLockOwner(lock.path, lock.ownerToken);
|
|
3386
|
+
throw lastError instanceof Error ? lastError : new Error("PR workflow checkout mutation lock release failed");
|
|
3387
|
+
}
|
|
3388
|
+
async function reclaimAbandonedCheckoutMutationLock(lockPath) {
|
|
3389
|
+
const lock = await readCheckoutMutationLock(lockPath);
|
|
3390
|
+
if (lock) {
|
|
3391
|
+
if (lock.pid === process.pid && completedCheckoutLockOwners.get(lockPath) === lock.ownerToken) {
|
|
3392
|
+
const removed = await removeCheckoutMutationLockIfOwned(lockPath, lock.ownerToken);
|
|
3393
|
+
if (removed)
|
|
3394
|
+
completedCheckoutLockOwners.delete(lockPath);
|
|
3395
|
+
return removed;
|
|
3396
|
+
}
|
|
3397
|
+
if (_test_exports.isProcessAlive(lock.pid))
|
|
3398
|
+
return false;
|
|
3399
|
+
return removeCheckoutMutationLockIfOwned(lockPath, lock.ownerToken);
|
|
3400
|
+
}
|
|
3401
|
+
try {
|
|
3402
|
+
const stat2 = await fsp2.stat(lockPath);
|
|
3403
|
+
if (_test_exports.nowMs() - stat2.mtimeMs < STATE_MUTATION_LOCK_UNINITIALIZED_STALE_MS) {
|
|
3404
|
+
return false;
|
|
3405
|
+
}
|
|
3406
|
+
await fsp2.rm(lockPath, { force: true });
|
|
3407
|
+
return true;
|
|
3408
|
+
} catch (error2) {
|
|
3409
|
+
if (error2.code === "ENOENT")
|
|
3410
|
+
return true;
|
|
3411
|
+
throw error2;
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3414
|
+
async function readCheckoutMutationLock(lockPath) {
|
|
3415
|
+
let raw;
|
|
3416
|
+
try {
|
|
3417
|
+
raw = await fsp2.readFile(lockPath, "utf-8");
|
|
3418
|
+
} catch (error2) {
|
|
3419
|
+
if (error2.code === "ENOENT")
|
|
3420
|
+
return null;
|
|
3421
|
+
throw error2;
|
|
3422
|
+
}
|
|
3423
|
+
try {
|
|
3424
|
+
const parsed = JSON.parse(raw);
|
|
3425
|
+
if (typeof parsed === "object" && parsed !== null && typeof parsed.ownerToken === "string" && parsed.ownerToken.length > 0 && typeof parsed.pid === "number" && Number.isInteger(parsed.pid) && parsed.pid > 0 && typeof parsed.createdAtMs === "number" && Number.isFinite(parsed.createdAtMs)) {
|
|
3426
|
+
return parsed;
|
|
3427
|
+
}
|
|
3428
|
+
} catch {}
|
|
3429
|
+
return null;
|
|
3430
|
+
}
|
|
3431
|
+
async function removeCheckoutMutationLockIfOwned(lockPath, ownerToken) {
|
|
3432
|
+
const lock = await readCheckoutMutationLock(lockPath);
|
|
3433
|
+
if (!lock || lock.ownerToken !== ownerToken)
|
|
3434
|
+
return false;
|
|
3435
|
+
try {
|
|
3436
|
+
await _test_exports.removeCheckoutLock(lockPath);
|
|
3437
|
+
return true;
|
|
3438
|
+
} catch (error2) {
|
|
3439
|
+
if (error2.code === "ENOENT")
|
|
3440
|
+
return false;
|
|
3441
|
+
throw error2;
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
async function removeCheckoutMutationLockByIdentity(directory, lockPath, identity, verifiedParent) {
|
|
3445
|
+
let lastError;
|
|
3446
|
+
for (let attempt = 0;attempt < WINDOWS_RENAME_MAX_RETRIES; attempt++) {
|
|
3447
|
+
try {
|
|
3448
|
+
await assertClosedSwarmFileIdentity(directory, lockPath, identity, verifiedParent, "PR workflow checkout mutation lock");
|
|
3449
|
+
await _test_exports.removeCheckoutLock(lockPath);
|
|
3450
|
+
return;
|
|
3451
|
+
} catch (error2) {
|
|
3452
|
+
if (error2.code === "ENOENT")
|
|
3453
|
+
return;
|
|
3454
|
+
lastError = error2;
|
|
3455
|
+
if (attempt < WINDOWS_RENAME_MAX_RETRIES - 1) {
|
|
3456
|
+
await delay(RENAME_RETRY_DELAY_MS);
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
throw lastError instanceof Error ? lastError : new Error("PR workflow checkout mutation lock cleanup failed");
|
|
3461
|
+
}
|
|
3462
|
+
function rememberCompletedCheckoutLockOwner(lockPath, ownerToken) {
|
|
3463
|
+
completedCheckoutLockOwners.delete(lockPath);
|
|
3464
|
+
completedCheckoutLockOwners.set(lockPath, ownerToken);
|
|
3465
|
+
while (completedCheckoutLockOwners.size > MAX_COMPLETED_CHECKOUT_LOCK_OWNERS) {
|
|
3466
|
+
const oldest = completedCheckoutLockOwners.keys().next().value;
|
|
3467
|
+
if (oldest === undefined)
|
|
3468
|
+
break;
|
|
3469
|
+
completedCheckoutLockOwners.delete(oldest);
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
2026
3472
|
async function withSessionStateMutation(directory, sessionID, action) {
|
|
2027
3473
|
const key = stateCacheKey(directory, sessionID);
|
|
2028
3474
|
const previous = pendingStateMutationsByProjectSession.get(key) ?? Promise.resolve();
|
|
@@ -12311,7 +13757,7 @@ function hasActiveEpicMode(sessionID) {
|
|
|
12311
13757
|
async function rehydratePrSubscriptions(sessionID, directory) {
|
|
12312
13758
|
const map = new Map;
|
|
12313
13759
|
try {
|
|
12314
|
-
const { listActive: listActive2 } = await import("./pr-subscriptions-
|
|
13760
|
+
const { listActive: listActive2 } = await import("./pr-subscriptions-v5xd3j6d.js");
|
|
12315
13761
|
const records = await listActive2(directory);
|
|
12316
13762
|
for (const record of records) {
|
|
12317
13763
|
if (record.sessionID !== sessionID)
|
|
@@ -15187,7 +16633,7 @@ init_logger();
|
|
|
15187
16633
|
import { readFile as readFile9, rename as rename4, writeFile as writeFile5 } from "fs/promises";
|
|
15188
16634
|
|
|
15189
16635
|
// src/services/skill-improver-quota.ts
|
|
15190
|
-
var
|
|
16636
|
+
var import_proper_lockfile2 = __toESM(require_proper_lockfile(), 1);
|
|
15191
16637
|
import { existsSync as existsSync12 } from "fs";
|
|
15192
16638
|
import { mkdir as mkdir7, readFile as readFile8, rename as rename3, writeFile as writeFile4 } from "fs/promises";
|
|
15193
16639
|
import * as path27 from "path";
|
|
@@ -15202,7 +16648,7 @@ var LOCK_RETRY_OPTS = {
|
|
|
15202
16648
|
stale: 5000
|
|
15203
16649
|
};
|
|
15204
16650
|
async function acquireLock(dir) {
|
|
15205
|
-
const acquire =
|
|
16651
|
+
const acquire = import_proper_lockfile2.default.lock(dir, LOCK_RETRY_OPTS);
|
|
15206
16652
|
let timer;
|
|
15207
16653
|
const timeout = new Promise((_, reject) => {
|
|
15208
16654
|
timer = setTimeout(() => {
|
|
@@ -15958,7 +17404,7 @@ function planMerges(active, threshold, maxComparisons, maxMerges) {
|
|
|
15958
17404
|
};
|
|
15959
17405
|
}
|
|
15960
17406
|
async function sweepActiveNearDuplicates(directory, options = {}) {
|
|
15961
|
-
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-
|
|
17407
|
+
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-g3meqrsg.js");
|
|
15962
17408
|
const { config } = loadPluginConfigWithMeta2(directory);
|
|
15963
17409
|
const sweepConfig = config.learning?.dedup_sweep;
|
|
15964
17410
|
if (sweepConfig?.enabled === false)
|
|
@@ -17551,7 +18997,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
|
|
|
17551
18997
|
}
|
|
17552
18998
|
if (config.skill_generation_enabled === true && skillCandidates.length > 0) {
|
|
17553
18999
|
try {
|
|
17554
|
-
const skillModule = await import("./skill-generator-
|
|
19000
|
+
const skillModule = await import("./skill-generator-54xbjgak.js");
|
|
17555
19001
|
for (const cand of skillCandidates) {
|
|
17556
19002
|
if (cand.confidence < (config.min_skill_confidence ?? DEFAULT_SKILL_MIN_CONFIDENCE)) {
|
|
17557
19003
|
continue;
|
|
@@ -17952,7 +19398,7 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
|
|
|
17952
19398
|
} catch {}
|
|
17953
19399
|
}
|
|
17954
19400
|
if (contradictedEntries.length > 0 && knowledgeConfig.contradiction_threshold_action === "quarantine") {
|
|
17955
|
-
const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-
|
|
19401
|
+
const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-w7by1m8m.js");
|
|
17956
19402
|
for (const { id } of contradictedEntries) {
|
|
17957
19403
|
try {
|
|
17958
19404
|
await maybeQuarantineOnContradiction(directory, id, knowledgeConfig.contradiction_quarantine_threshold, knowledgeConfig.contradiction_quarantine_window_days);
|
|
@@ -18760,7 +20206,7 @@ async function runCuratorPostMortem(directory, options = {}) {
|
|
|
18760
20206
|
warnings.push("Failed to collect knowledge summary.");
|
|
18761
20207
|
}
|
|
18762
20208
|
try {
|
|
18763
|
-
const { getScanStatus } = await import("./scan-cursor-
|
|
20209
|
+
const { getScanStatus } = await import("./scan-cursor-1q1zmt4b.js");
|
|
18764
20210
|
const scanStatus = await getScanStatus(directory);
|
|
18765
20211
|
if (scanStatus.remaining_estimate > 0) {
|
|
18766
20212
|
warnings.push(`Fair scan: reviewed ${knowledgeSummary.length} entries this sweep (generation ${scanStatus.generation}); ${scanStatus.remaining_estimate} more eligible entries will be visited in future curation sweeps.`);
|
|
@@ -18802,7 +20248,7 @@ async function runCuratorPostMortem(directory, options = {}) {
|
|
|
18802
20248
|
let actionResult;
|
|
18803
20249
|
if (options.llmDelegate) {
|
|
18804
20250
|
try {
|
|
18805
|
-
const { CURATOR_POSTMORTEM_PROMPT } = await import("./explorer-
|
|
20251
|
+
const { CURATOR_POSTMORTEM_PROMPT } = await import("./explorer-15gqzxh8.js");
|
|
18806
20252
|
const userInput = assembleLLMInput(effectivePlanId, scope, options.sessionID, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports);
|
|
18807
20253
|
const ac = new AbortController;
|
|
18808
20254
|
const timer = setTimeout(() => ac.abort(), options.llmTimeoutMs ?? 300000);
|
|
@@ -18932,9 +20378,9 @@ var _internals24 = {
|
|
|
18932
20378
|
verifyPostMortemKnowledgeActions,
|
|
18933
20379
|
repairPostMortemActions,
|
|
18934
20380
|
loadDefaultKnowledgeConfig: async (directory) => {
|
|
18935
|
-
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-
|
|
20381
|
+
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-8mtsfspm.js");
|
|
18936
20382
|
try {
|
|
18937
|
-
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-
|
|
20383
|
+
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-g3meqrsg.js");
|
|
18938
20384
|
const loaded = loadPluginConfigWithMeta2(directory);
|
|
18939
20385
|
return KnowledgeConfigSchema2.parse(loaded.config.knowledge ?? {});
|
|
18940
20386
|
} catch {
|
|
@@ -18942,11 +20388,11 @@ var _internals24 = {
|
|
|
18942
20388
|
}
|
|
18943
20389
|
},
|
|
18944
20390
|
applyCuratorKnowledgeUpdates: async (directory, recommendations, knowledgeConfig, generation) => {
|
|
18945
|
-
const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-
|
|
20391
|
+
const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-r1s2mwf2.js");
|
|
18946
20392
|
return applyCuratorKnowledgeUpdates2(directory, recommendations, knowledgeConfig, generation);
|
|
18947
20393
|
},
|
|
18948
20394
|
checkHivePromotions: async (entries, knowledgeConfig, directory) => {
|
|
18949
|
-
const { checkHivePromotions } = await import("./hive-promoter-
|
|
20395
|
+
const { checkHivePromotions } = await import("./hive-promoter-586dhndj.js");
|
|
18950
20396
|
return checkHivePromotions(entries, knowledgeConfig, directory);
|
|
18951
20397
|
},
|
|
18952
20398
|
applyProposalTriage: async (directory, triage) => {
|
|
@@ -18955,7 +20401,7 @@ var _internals24 = {
|
|
|
18955
20401
|
activateProposal,
|
|
18956
20402
|
listSkills: listSkills2,
|
|
18957
20403
|
sanitizeSlug
|
|
18958
|
-
} = await import("./skill-generator-
|
|
20404
|
+
} = await import("./skill-generator-54xbjgak.js");
|
|
18959
20405
|
const result = {
|
|
18960
20406
|
approved: [],
|
|
18961
20407
|
rejected: [],
|
|
@@ -18997,7 +20443,7 @@ var _internals24 = {
|
|
|
18997
20443
|
return result;
|
|
18998
20444
|
},
|
|
18999
20445
|
readPriorDriftReports: async (directory) => {
|
|
19000
|
-
const { readPriorDriftReports } = await import("./curator-drift-
|
|
20446
|
+
const { readPriorDriftReports } = await import("./curator-drift-2ta83hss.js");
|
|
19001
20447
|
return readPriorDriftReports(directory);
|
|
19002
20448
|
},
|
|
19003
20449
|
readSwarmKnowledge: (directory) => readKnowledge(resolveSwarmKnowledgePath(directory))
|
|
@@ -19094,7 +20540,7 @@ function failedGateNames(decision) {
|
|
|
19094
20540
|
}
|
|
19095
20541
|
|
|
19096
20542
|
// src/hooks/hive-transaction.ts
|
|
19097
|
-
var
|
|
20543
|
+
var import_proper_lockfile3 = __toESM(require_proper_lockfile(), 1);
|
|
19098
20544
|
import { appendFile as appendFile4, mkdir as mkdir8 } from "fs/promises";
|
|
19099
20545
|
import * as path32 from "path";
|
|
19100
20546
|
init_logger();
|
|
@@ -19194,7 +20640,7 @@ async function trimHiveEventsUnderLock(eventsPath) {
|
|
|
19194
20640
|
`);
|
|
19195
20641
|
}
|
|
19196
20642
|
var _internals25 = {
|
|
19197
|
-
lockfile:
|
|
20643
|
+
lockfile: import_proper_lockfile3.default,
|
|
19198
20644
|
readKnowledge,
|
|
19199
20645
|
atomicWriteFile,
|
|
19200
20646
|
selectKnowledgeCapSurvivors,
|
|
@@ -19951,7 +21397,7 @@ import {
|
|
|
19951
21397
|
import * as path38 from "path";
|
|
19952
21398
|
|
|
19953
21399
|
// src/services/synonym-map.ts
|
|
19954
|
-
var
|
|
21400
|
+
var import_proper_lockfile4 = __toESM(require_proper_lockfile(), 1);
|
|
19955
21401
|
import {
|
|
19956
21402
|
mkdir as mkdir10,
|
|
19957
21403
|
readFile as readFile11,
|
|
@@ -20132,7 +21578,7 @@ async function rebuildSynonymMap(directory, entries, maxPairs = DEFAULT_MAX_PAIR
|
|
|
20132
21578
|
await mkdir10(dir, { recursive: true });
|
|
20133
21579
|
let release = null;
|
|
20134
21580
|
try {
|
|
20135
|
-
release = await
|
|
21581
|
+
release = await import_proper_lockfile4.default.lock(dir, {
|
|
20136
21582
|
retries: { retries: 5, minTimeout: 100, maxTimeout: 500 },
|
|
20137
21583
|
stale: 5000
|
|
20138
21584
|
});
|
|
@@ -21582,9 +23028,9 @@ function hashContent(content) {
|
|
|
21582
23028
|
return createHash9("sha1").update(content).digest("hex");
|
|
21583
23029
|
}
|
|
21584
23030
|
async function loadConfigForPolicyCurator(directory) {
|
|
21585
|
-
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-
|
|
23031
|
+
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-8mtsfspm.js");
|
|
21586
23032
|
try {
|
|
21587
|
-
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-
|
|
23033
|
+
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-g3meqrsg.js");
|
|
21588
23034
|
const { config: loadedConfig } = loadPluginConfigWithMeta2(directory);
|
|
21589
23035
|
return KnowledgeConfigSchema2.parse(loadedConfig.knowledge ?? {});
|
|
21590
23036
|
} catch {
|
|
@@ -24929,7 +26375,7 @@ async function runFinalizeStage(ctx) {
|
|
|
24929
26375
|
}
|
|
24930
26376
|
}
|
|
24931
26377
|
try {
|
|
24932
|
-
const { CuratorConfigSchema: CCS } = await import("./schema-
|
|
26378
|
+
const { CuratorConfigSchema: CCS } = await import("./schema-8mtsfspm.js");
|
|
24933
26379
|
const { config: pmLoadedConfig } = _internals34.loadPluginConfigWithMeta(ctx.directory);
|
|
24934
26380
|
const curatorCfg = CCS.parse(pmLoadedConfig.curator ?? {});
|
|
24935
26381
|
if (curatorCfg.enabled && curatorCfg.postmortem_enabled) {
|
|
@@ -27159,9 +28605,9 @@ var _internals39 = {
|
|
|
27159
28605
|
loadPluginConfigWithMeta,
|
|
27160
28606
|
loadCuratorDeps: async () => {
|
|
27161
28607
|
const [{ CuratorConfigSchema }, curator, { createCuratorLLMDelegate: createCuratorLLMDelegate2 }] = await Promise.all([
|
|
27162
|
-
import("./schema-
|
|
27163
|
-
import("./curator-
|
|
27164
|
-
import("./curator-llm-factory-
|
|
28608
|
+
import("./schema-8mtsfspm.js"),
|
|
28609
|
+
import("./curator-r1s2mwf2.js"),
|
|
28610
|
+
import("./curator-llm-factory-9983528d.js")
|
|
27165
28611
|
]);
|
|
27166
28612
|
return { CuratorConfigSchema, curator, createCuratorLLMDelegate: createCuratorLLMDelegate2 };
|
|
27167
28613
|
}
|
|
@@ -28062,7 +29508,7 @@ async function computeCurationDiagnostics(directory, entries) {
|
|
|
28062
29508
|
let scanCompleted = false;
|
|
28063
29509
|
let scanRemaining = 0;
|
|
28064
29510
|
try {
|
|
28065
|
-
const { getScanStatus } = await import("./scan-cursor-
|
|
29511
|
+
const { getScanStatus } = await import("./scan-cursor-1q1zmt4b.js");
|
|
28066
29512
|
const status = await getScanStatus(directory);
|
|
28067
29513
|
scanGeneration = status.generation;
|
|
28068
29514
|
scanCompleted = status.completed;
|
|
@@ -28070,19 +29516,19 @@ async function computeCurationDiagnostics(directory, entries) {
|
|
|
28070
29516
|
} catch {}
|
|
28071
29517
|
let configMatch = null;
|
|
28072
29518
|
try {
|
|
28073
|
-
const { isLinked: isLinked2 } = await import("./knowledge-link-
|
|
29519
|
+
const { isLinked: isLinked2 } = await import("./knowledge-link-mm1w967j.js");
|
|
28074
29520
|
if (isLinked2(directory)) {
|
|
28075
|
-
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-
|
|
29521
|
+
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-8mtsfspm.js");
|
|
28076
29522
|
let config;
|
|
28077
29523
|
try {
|
|
28078
|
-
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-
|
|
29524
|
+
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-g3meqrsg.js");
|
|
28079
29525
|
const loaded = loadPluginConfigWithMeta2(directory);
|
|
28080
29526
|
config = KnowledgeConfigSchema2.parse(loaded.config.knowledge ?? {});
|
|
28081
29527
|
} catch {
|
|
28082
29528
|
config = KnowledgeConfigSchema2.parse({});
|
|
28083
29529
|
}
|
|
28084
29530
|
const { cohortConfigFingerprint: cohortConfigFingerprint2 } = await import("./config-fingerprint-v22xh5a2.js");
|
|
28085
|
-
const { buildConfigFingerprintInput: buildConfigFingerprintInput2, readCohortConfigFingerprint } = await import("./curation-policy-
|
|
29531
|
+
const { buildConfigFingerprintInput: buildConfigFingerprintInput2, readCohortConfigFingerprint } = await import("./curation-policy-b2caf3v3.js");
|
|
28086
29532
|
const currentFp = cohortConfigFingerprint2(buildConfigFingerprintInput2(config));
|
|
28087
29533
|
const cohortFp = await readCohortConfigFingerprint(directory);
|
|
28088
29534
|
configMatch = cohortFp === null ? null : currentFp === cohortFp;
|
|
@@ -29293,7 +30739,7 @@ async function handleDoctorCommand(directory, args) {
|
|
|
29293
30739
|
const result = runConfigDoctor(config, directory);
|
|
29294
30740
|
let output;
|
|
29295
30741
|
if (enableAutoFix && result.hasAutoFixableIssues) {
|
|
29296
|
-
const { runConfigDoctorWithFixes } = await import("./config-doctor-
|
|
30742
|
+
const { runConfigDoctorWithFixes } = await import("./config-doctor-pj3yvxdb.js");
|
|
29297
30743
|
const fixResult = await runConfigDoctorWithFixes(directory, config, true, {
|
|
29298
30744
|
applyLossy: true
|
|
29299
30745
|
});
|
|
@@ -30130,7 +31576,7 @@ async function handleEvidenceCommand(directory, args) {
|
|
|
30130
31576
|
return formatTaskEvidenceMarkdown(evidenceData);
|
|
30131
31577
|
}
|
|
30132
31578
|
async function handleEvidenceSummaryCommand(directory) {
|
|
30133
|
-
const { buildEvidenceSummary } = await import("./evidence-summary-service-
|
|
31579
|
+
const { buildEvidenceSummary } = await import("./evidence-summary-service-feby8ny0.js");
|
|
30134
31580
|
const artifact = await buildEvidenceSummary(directory);
|
|
30135
31581
|
if (!artifact) {
|
|
30136
31582
|
return "No plan found. Run `/swarm plan` to check plan status.";
|
|
@@ -30193,11 +31639,11 @@ async function handleExportCommand(directory, _args) {
|
|
|
30193
31639
|
return formatExportMarkdown(exportData);
|
|
30194
31640
|
}
|
|
30195
31641
|
// src/full-auto/state.ts
|
|
30196
|
-
var
|
|
31642
|
+
var import_proper_lockfile5 = __toESM(require_proper_lockfile(), 1);
|
|
30197
31643
|
import * as fs21 from "fs";
|
|
30198
31644
|
import * as path55 from "path";
|
|
30199
31645
|
init_logger();
|
|
30200
|
-
var
|
|
31646
|
+
var lockfile5 = import_proper_lockfile5.default;
|
|
30201
31647
|
var STATE_FILE2 = "full-auto-state.json";
|
|
30202
31648
|
function nowISO2() {
|
|
30203
31649
|
return new Date().toISOString();
|
|
@@ -30275,7 +31721,7 @@ function withStateLock(directory, fn) {
|
|
|
30275
31721
|
fs21.writeFileSync(lockTarget, `${JSON.stringify(seed, null, 2)}
|
|
30276
31722
|
`, "utf-8");
|
|
30277
31723
|
}
|
|
30278
|
-
release =
|
|
31724
|
+
release = lockfile5.lockSync(lockTarget, {
|
|
30279
31725
|
retries: { retries: 5, minTimeout: 5, maxTimeout: 50 },
|
|
30280
31726
|
stale: 5000
|
|
30281
31727
|
});
|
|
@@ -37007,7 +38453,7 @@ import * as path69 from "path";
|
|
|
37007
38453
|
init_schema();
|
|
37008
38454
|
|
|
37009
38455
|
// src/knowledge/family-migration.ts
|
|
37010
|
-
var
|
|
38456
|
+
var import_proper_lockfile6 = __toESM(require_proper_lockfile(), 1);
|
|
37011
38457
|
import { existsSync as existsSync38, readFileSync as readFileSync29 } from "fs";
|
|
37012
38458
|
import { mkdir as mkdir16 } from "fs/promises";
|
|
37013
38459
|
import * as path68 from "path";
|
|
@@ -37295,7 +38741,7 @@ async function snapshotSourceFamily(storeDir) {
|
|
|
37295
38741
|
await mkdir16(storeDir, { recursive: true });
|
|
37296
38742
|
} catch {}
|
|
37297
38743
|
try {
|
|
37298
|
-
release = await
|
|
38744
|
+
release = await import_proper_lockfile6.default.lock(storeDir, {
|
|
37299
38745
|
...MIGRATION_LOCK_RETRIES,
|
|
37300
38746
|
stale: MIGRATION_LOCK_STALE_MS
|
|
37301
38747
|
});
|
|
@@ -37314,7 +38760,7 @@ async function migrateKnowledgeFamily(destinationDir, sourceDir) {
|
|
|
37314
38760
|
await mkdir16(destinationDir, { recursive: true });
|
|
37315
38761
|
let destRelease = null;
|
|
37316
38762
|
try {
|
|
37317
|
-
destRelease = await
|
|
38763
|
+
destRelease = await import_proper_lockfile6.default.lock(destinationDir, {
|
|
37318
38764
|
...MIGRATION_LOCK_RETRIES,
|
|
37319
38765
|
stale: MIGRATION_LOCK_STALE_MS
|
|
37320
38766
|
});
|
|
@@ -38703,7 +40149,7 @@ import * as path74 from "path";
|
|
|
38703
40149
|
init_schema();
|
|
38704
40150
|
|
|
38705
40151
|
// src/memory/memory-family-migration.ts
|
|
38706
|
-
var
|
|
40152
|
+
var import_proper_lockfile7 = __toESM(require_proper_lockfile(), 1);
|
|
38707
40153
|
import { copyFileSync as copyFileSync4, existsSync as existsSync40, readFileSync as readFileSync30, unlinkSync as unlinkSync12 } from "fs";
|
|
38708
40154
|
import { mkdir as mkdir19, rename as rename9 } from "fs/promises";
|
|
38709
40155
|
import * as path73 from "path";
|
|
@@ -38928,7 +40374,7 @@ async function migrateMemoryFamily(destRoot, sourceRoot) {
|
|
|
38928
40374
|
await mkdir19(destStoragePath, { recursive: true });
|
|
38929
40375
|
let destRelease = null;
|
|
38930
40376
|
try {
|
|
38931
|
-
destRelease = await
|
|
40377
|
+
destRelease = await import_proper_lockfile7.default.lock(destStoragePath, {
|
|
38932
40378
|
...MIGRATION_LOCK_RETRIES,
|
|
38933
40379
|
stale: MIGRATION_LOCK_STALE_MS
|
|
38934
40380
|
});
|
|
@@ -42148,7 +43594,7 @@ var DISPATCH_FRAMEWORK_MAP = {
|
|
|
42148
43594
|
};
|
|
42149
43595
|
async function detectTestFrameworkViaDispatch(cwd) {
|
|
42150
43596
|
try {
|
|
42151
|
-
const { pickBackend } = await import("./dispatch-
|
|
43597
|
+
const { pickBackend } = await import("./dispatch-wtk6dxky.js");
|
|
42152
43598
|
const backend = await pickBackend(cwd);
|
|
42153
43599
|
if (!backend?.selectTestFramework)
|
|
42154
43600
|
return "none";
|
|
@@ -42164,7 +43610,7 @@ async function buildTestCommandViaDispatch(framework, scope, files, coverage, ba
|
|
|
42164
43610
|
if (framework === "none")
|
|
42165
43611
|
return null;
|
|
42166
43612
|
try {
|
|
42167
|
-
const { pickBackend } = await import("./dispatch-
|
|
43613
|
+
const { pickBackend } = await import("./dispatch-wtk6dxky.js");
|
|
42168
43614
|
const backend = await pickBackend(baseDir);
|
|
42169
43615
|
if (backend?.buildTestCommand) {
|
|
42170
43616
|
const cmd = backend.buildTestCommand(framework, files, baseDir, {
|
|
@@ -42184,7 +43630,7 @@ async function parseTestOutputViaDispatch(framework, output, baseDir) {
|
|
|
42184
43630
|
if (framework === "none")
|
|
42185
43631
|
return null;
|
|
42186
43632
|
try {
|
|
42187
|
-
const { pickBackend } = await import("./dispatch-
|
|
43633
|
+
const { pickBackend } = await import("./dispatch-wtk6dxky.js");
|
|
42188
43634
|
const backend = await pickBackend(baseDir);
|
|
42189
43635
|
if (!backend?.parseTestOutput)
|
|
42190
43636
|
return null;
|
|
@@ -50713,7 +52159,7 @@ function buildDetailedHelp(commandName, entry) {
|
|
|
50713
52159
|
async function handleHelpCommand(ctx) {
|
|
50714
52160
|
const targetCommand = ctx.args.join(" ");
|
|
50715
52161
|
if (!targetCommand) {
|
|
50716
|
-
const { buildHelpText } = await import("./index-
|
|
52162
|
+
const { buildHelpText } = await import("./index-cwdy5vw9.js");
|
|
50717
52163
|
return buildHelpText();
|
|
50718
52164
|
}
|
|
50719
52165
|
const tokens = targetCommand.split(/\s+/);
|
|
@@ -50722,7 +52168,7 @@ async function handleHelpCommand(ctx) {
|
|
|
50722
52168
|
return _internals12.buildDetailedHelp(resolved.key, resolved.entry);
|
|
50723
52169
|
}
|
|
50724
52170
|
const similar = _internals12.findSimilarCommands(targetCommand);
|
|
50725
|
-
const { buildHelpText: fullHelp } = await import("./index-
|
|
52171
|
+
const { buildHelpText: fullHelp } = await import("./index-cwdy5vw9.js");
|
|
50726
52172
|
if (similar.length > 0) {
|
|
50727
52173
|
return `Command '/swarm ${targetCommand}' not found.
|
|
50728
52174
|
|
|
@@ -50899,7 +52345,7 @@ var COMMAND_REGISTRY = {
|
|
|
50899
52345
|
},
|
|
50900
52346
|
"guardrail explain": {
|
|
50901
52347
|
handler: async (ctx) => {
|
|
50902
|
-
const { handleGuardrailExplain } = await import("./guardrail-explain-
|
|
52348
|
+
const { handleGuardrailExplain } = await import("./guardrail-explain-pwxvg5n0.js");
|
|
50903
52349
|
return handleGuardrailExplain(ctx.directory, ctx.args);
|
|
50904
52350
|
},
|
|
50905
52351
|
description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
|
|
@@ -50909,7 +52355,7 @@ var COMMAND_REGISTRY = {
|
|
|
50909
52355
|
},
|
|
50910
52356
|
"guardrail-explain": {
|
|
50911
52357
|
handler: async (ctx) => {
|
|
50912
|
-
const { handleGuardrailExplain } = await import("./guardrail-explain-
|
|
52358
|
+
const { handleGuardrailExplain } = await import("./guardrail-explain-pwxvg5n0.js");
|
|
50913
52359
|
return handleGuardrailExplain(ctx.directory, ctx.args);
|
|
50914
52360
|
},
|
|
50915
52361
|
description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
|
|
@@ -50919,7 +52365,7 @@ var COMMAND_REGISTRY = {
|
|
|
50919
52365
|
},
|
|
50920
52366
|
"guardrail-log": {
|
|
50921
52367
|
handler: async (ctx) => {
|
|
50922
|
-
const { handleGuardrailLog } = await import("./guardrail-log-
|
|
52368
|
+
const { handleGuardrailLog } = await import("./guardrail-log-yzpz8b7t.js");
|
|
50923
52369
|
return handleGuardrailLog(ctx.directory, ctx.args);
|
|
50924
52370
|
},
|
|
50925
52371
|
description: "Read the guardrail decision log (use --blocks-only for blocks)",
|
|
@@ -51292,7 +52738,7 @@ Subcommands:
|
|
|
51292
52738
|
handler: (ctx) => handleModeCommandWithBundledSkills(ctx, handlePrReviewCommand, "PR_REVIEW"),
|
|
51293
52739
|
description: "Launch deep PR review with multi-lane analysis [url] [--council]",
|
|
51294
52740
|
args: "<pr-url|owner/repo#N|N> [--council]",
|
|
51295
|
-
details: "Launches a structured PR review: reconstructs PR intent via obligation extraction cascade, computes a depth tier (S/M/L) from the bound merge-base diff, launches the base explorer wave through dispatch_lanes_async (all 6 review dimensions covered on every PR \u2014 six singleton lanes at tier L, consolidated owned_workflow_lanes partitions at tiers S/M) while the architect keeps doing non-dependent work, polls collect_lane_results incrementally
|
|
52741
|
+
details: "Launches a structured PR review: preserves dirty state, verifies and binds an exact detached PR head, reconstructs PR intent via obligation extraction cascade, computes a depth tier (S/M/L) from the bound merge-base diff, launches the base explorer wave through dispatch_lanes_async (all 6 review dimensions covered on every PR \u2014 six singleton lanes at tier L, consolidated owned_workflow_lanes partitions at tiers S/M) while the architect keeps doing non-dependent work, and polls collect_lane_results incrementally. It evaluates an exact 11-row repository-agnostic risk-family ledger, records applicable rows as MATCHED and concretely inapplicable rows as provenance-free NOT_TRIGGERED, always keeps unclassified-risk MATCHED, and dispatches micro work only for MATCHED families (dedicated lanes at tier L, consolidated sweeps at S/M). It then validates findings through independent reviewer confirmation, applies critic challenge to HIGH/CRITICAL findings, and synthesizes only after matched coverage is closed. Failed obligations retry through the same structured async mode and exact PR head; blocking or direct-Task dispatch is not provenance-equivalent, so unclosed matched coverage leaves the review BLOCKED rather than degraded. --council variant fires adversarial multi-model review. Supports full GitHub URL, owner/repo#N shorthand, or bare PR number (resolves against origin remote).",
|
|
51296
52742
|
category: "agent",
|
|
51297
52743
|
toolPolicy: "none"
|
|
51298
52744
|
},
|
|
@@ -52717,7 +54163,7 @@ HARD CONSTRAINTS (apply regardless of skill load success):
|
|
|
52717
54163
|
- RUN ALL BASE LANES: the default PR_REVIEW path always launches exactly six repository-agnostic base check-type lanes from the skill. Use \`mode: "swarm-pr-review:base"\` and the exact six \`workflow_lane\` identifiers. The runtime rejects partial, duplicate, or mislabelled waves. Do not collapse, omit, or scale down the base lanes for a small, docs-only, or CI-only PR.
|
|
52718
54164
|
- RETRY STRUCTURALLY: retry only failed base obligations in later \`swarm-pr-review:base\` async batches with the same exact \`pr_head_sha\`. Blocking \`dispatch_lanes\` and direct Task explorer/reviewer/critic dispatch are not provenance-equivalent and are rejected.
|
|
52719
54165
|
- USE ASYNC DISPATCH WITHOUT IDLING: launch the base lanes with one \`dispatch_lanes_async\` call when available, record the \`batch_id\`, then keep doing non-dependent architect work while they run. Poll with \`collect_lane_results\` without \`wait\` (or \`wait: false\`) to process settled lanes and continue independent work between polls; use \`wait: true\` only as the final join when no independent work remains.
|
|
52720
|
-
-
|
|
54166
|
+
- EVALUATE ALL RISK FAMILIES: after the base explorer lanes settle, evaluate every repository-agnostic trigger row against the exact diff/context pack. Record applicable families as \`MATCHED\` and inapplicable families as provenance-free \`NOT_TRIGGERED\` with concrete absence evidence; \`unclassified-risk\` always remains \`MATCHED\`. Launch \`swarm-pr-review:micro\` lanes only for the \`MATCHED\` IDs, pass the complete exact eleven-row \`trigger_evaluation\` to every batch, and use each dispatched trigger ID as \`workflow_lane\`. Missing an evaluation row or a matched-family attestation is BLOCKED; a \`NOT_TRIGGERED\` family must not create a micro artifact.
|
|
52721
54167
|
- RUN HEAD-BOUND VALIDATION: dispatch independent reviewers with \`mode: "swarm-pr-review:reviewer"\` and critics with \`mode: "swarm-pr-review:critic"\`, unique \`workflow_lane\` obligations, role-correct agents, and the same exact \`pr_head_sha\`. Critic dispatch is blocked until all declared reviewer obligations have successful artifacts.
|
|
52722
54168
|
- COMPLETE EXPLICITLY: after final artifacts and synthesis, call \`complete_pr_workflow\` with \`mode: "PR_REVIEW"\` and the bound \`pr_head_sha\`; never leave the session trapped in a stale workflow gate.
|
|
52723
54169
|
- ABORT IF UNRECOVERABLE: if the PR head cannot be fetched/checked out (the working tree stays on the wrong branch), a compound \`git fetch && git checkout\` keeps being rejected as read-only shell syntax (run them as TWO separate standalone commands first), or the merge-base bind is unreachable for any reason, call \`abort_pr_workflow\` with \`mode: "PR_REVIEW"\` and a one-line \`reason\` instead of looping. The tool refuses while PR workflow lanes are in flight (collect their results first). Aborting clears the durable gate and stops the auto-resume loop; the user can also run \`/swarm abort-pr-workflow\`. Do NOT abort as a shortcut to avoid coverage obligations \u2014 only when the bind/checkout path is genuinely unreachable.
|
|
@@ -52739,7 +54185,7 @@ ACTION: Load skill ${bundledProjectSkillFileReference("swarm-pr-feedback")} imme
|
|
|
52739
54185
|
|
|
52740
54186
|
HARD CONSTRAINTS (apply regardless of skill load success):
|
|
52741
54187
|
- FOLLOW THE SKILL EXACTLY: build the complete feedback ledger from all available sources before editing, and execute every phase in order with no shortcuts.
|
|
52742
|
-
- CHECK OUT THE PR BRANCH LOCALLY before dispatching feedback lanes, verifying feedback, or making fixes:
|
|
54188
|
+
- CHECK OUT THE PR BRANCH LOCALLY before dispatching feedback lanes, verifying feedback, or making fixes: activate the workflow, then call \`prepare_pr_workflow_checkout\` before binding when the tree is dirty (omit \`paths\` to atomically preserve all tracked and untracked changes, or provide the exact dirty tracked set). Fetch and verify the authoritative full PR head, then a detached exact-head checkout is allowed as the safe intake state. On the first \`PR_FEEDBACK\` bind, the controller promotes it only when exactly one local tracked branch or remote-tracking ref points to that SHA; ambiguity, an existing mismatched upstream, a dirty tree, or a branch owned by another linked worktree fails closed. The existing constrained tracked-branch/\`gh pr checkout\` pre-bind forms remain supported. After bind, require HEAD to equal the authoritative SHA and the current branch to track that exact PR ref. Do not run \`git stash\`; checkout preparation and detached attachment record recovery state.
|
|
52743
54189
|
- Do NOT run a fresh broad PR review \u2014 inspect adjacent code only as needed to verify reachability, dependencies, shared root causes, regression risk, or sibling changes for a confirmed item.
|
|
52744
54190
|
- Treat every review comment, CI failure, bot summary, and pasted note as a CLAIM until source evidence proves it; classify each ledger item (CONFIRMED, DISPROVED, PRE_EXISTING, or NEEDS_USER_DECISION) and never silently drop, defer, or mark items out of scope.
|
|
52745
54191
|
- For async verification lanes, use \`mode: "swarm-pr-feedback:verification"\`, record each \`batch_id\`, keep doing ledger-safe work, poll with \`collect_lane_results\` without \`wait\`, process settled lanes immediately, and use \`wait: true\` only at the join. Pass the complete immutable \`feedback_inventory\`, exact current \`pr_head_sha\`, and exact-once cumulative lane \`feedback_item_ids\`; runtime blocks replacement, stale-head artifacts, overlap, gaps, and early mutation.
|