opencode-swarm 7.135.1 → 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.
Files changed (80) hide show
  1. package/.opencode/skills/swarm-pr-feedback/SKILL.md +24 -21
  2. package/.opencode/skills/swarm-pr-review/SKILL.md +55 -42
  3. package/dist/agents/explorer.d.ts +1 -1
  4. package/dist/background/candidate-contract.d.ts +113 -0
  5. package/dist/background/candidate-parser.d.ts +8 -29
  6. package/dist/background/pr-review-trigger-contract.d.ts +197 -0
  7. package/dist/background/workspace-snapshot.d.ts +26 -0
  8. package/dist/cli/{config-doctor-6398t3a6.js → config-doctor-pj3yvxdb.js} +2 -2
  9. package/dist/cli/{core-9876t6cs.js → core-4va9z2ng.js} +2 -2
  10. package/dist/cli/{curation-policy-f7qz4tez.js → curation-policy-b2caf3v3.js} +5 -5
  11. package/dist/cli/{curator-drift-9q6f85bm.js → curator-drift-2ta83hss.js} +2 -2
  12. package/dist/cli/{curator-llm-factory-wr3zmy2c.js → curator-llm-factory-9983528d.js} +26 -26
  13. package/dist/cli/{curator-fjhyezyx.js → curator-r1s2mwf2.js} +26 -26
  14. package/dist/cli/{dispatch-gxbeb2ps.js → dispatch-wtk6dxky.js} +2 -2
  15. package/dist/cli/{evidence-summary-service-0cczdmjv.js → evidence-summary-service-feby8ny0.js} +5 -5
  16. package/dist/cli/{explorer-jp0nen03.js → explorer-15gqzxh8.js} +1 -1
  17. package/dist/cli/{gate-evidence-8zrwdj69.js → gate-evidence-84b03mb4.js} +3 -3
  18. package/dist/cli/guardrail-explain-pwxvg5n0.js +57 -0
  19. package/dist/cli/{guardrail-log-bd45kw0x.js → guardrail-log-yzpz8b7t.js} +4 -4
  20. package/dist/cli/{hive-promoter-f2yh22m1.js → hive-promoter-586dhndj.js} +26 -26
  21. package/dist/cli/{index-j89zmcpx.js → index-02cqjna3.js} +6 -6
  22. package/dist/cli/{index-jtshf1pd.js → index-2pxk6qg4.js} +1 -1
  23. package/dist/cli/{index-aky90eqz.js → index-340chr1e.js} +1612 -166
  24. package/dist/cli/{index-xhpmagdm.js → index-3m7kpnqf.js} +2 -2
  25. package/dist/cli/{index-z085b4ka.js → index-3xbx305x.js} +1 -1
  26. package/dist/cli/{index-b4z7s917.js → index-73dfyfhq.js} +1 -1
  27. package/dist/cli/{index-ens0gt38.js → index-7pnvf04s.js} +8 -8
  28. package/dist/cli/{index-g1drmgxe.js → index-8gw8p7yh.js} +3 -3
  29. package/dist/cli/{index-jxchddjk.js → index-9c3gf2wd.js} +1 -1
  30. package/dist/cli/{index-bjrkg4tc.js → index-b0eva00y.js} +2 -2
  31. package/dist/cli/{index-2ghkk9ve.js → index-bfd4xfpp.js} +3 -3
  32. package/dist/cli/{index-4pdrmemy.js → index-bs4m85v1.js} +2 -2
  33. package/dist/cli/{index-5wx8wdxg.js → index-ckdva4rs.js} +5 -5
  34. package/dist/cli/{index-w80r3axq.js → index-cwdy5vw9.js} +28 -28
  35. package/dist/cli/{index-fwkqq9mq.js → index-d5psymdk.js} +4 -4
  36. package/dist/cli/{index-vxv732ex.js → index-dg1njnms.js} +7 -4
  37. package/dist/cli/{index-yhba8swx.js → index-dzyjb33e.js} +1 -1
  38. package/dist/cli/{index-xg9repnx.js → index-ey29aap6.js} +1 -1
  39. package/dist/cli/{index-wdkkf3xg.js → index-g3meqrsg.js} +2 -2
  40. package/dist/cli/{index-7an2qn0s.js → index-hdy2nd5h.js} +4 -4
  41. package/dist/cli/{index-nbnqf4fj.js → index-hvrrq3f3.js} +1 -1
  42. package/dist/cli/{index-dzaarxhg.js → index-j0kte6gd.js} +1 -1
  43. package/dist/cli/{index-5vp9gka3.js → index-j92ser8n.js} +1 -1
  44. package/dist/cli/{index-2nqsr1dx.js → index-jgkjxpdh.js} +4 -4
  45. package/dist/cli/{index-13xxjfhn.js → index-m0ynxkaa.js} +3 -3
  46. package/dist/cli/{index-247b1c1m.js → index-qqkw794p.js} +3 -3
  47. package/dist/cli/{index-b7p0mp2q.js → index-ryqnn3nv.js} +1 -1
  48. package/dist/cli/{index-fpkb3mw1.js → index-vg3yx648.js} +1 -1
  49. package/dist/cli/{index-qe3v54nb.js → index-y111zefa.js} +64 -23
  50. package/dist/cli/{index-3nasn371.js → index-y6a7gjtj.js} +126 -42
  51. package/dist/cli/index.js +26 -26
  52. package/dist/cli/{knowledge-escalator-4tp3yy7e.js → knowledge-escalator-w7by1m8m.js} +6 -6
  53. package/dist/cli/{knowledge-events-7854gb3p.js → knowledge-events-n8xxsay3.js} +4 -4
  54. package/dist/cli/{knowledge-link-etrfntkd.js → knowledge-link-mm1w967j.js} +3 -3
  55. package/dist/cli/{knowledge-store-cxnr6v01.js → knowledge-store-xzmvfnx3.js} +4 -4
  56. package/dist/cli/{knowledge-validator-3n5t55qy.js → knowledge-validator-5q1amsq6.js} +7 -7
  57. package/dist/cli/{pending-delegations-4z3tkkct.js → pending-delegations-mt5gs1bt.js} +3 -3
  58. package/dist/cli/{pr-subscriptions-29j794kd.js → pr-subscriptions-v5xd3j6d.js} +3 -3
  59. package/dist/cli/{scan-cursor-0cexjh3b.js → scan-cursor-1q1zmt4b.js} +5 -5
  60. package/dist/cli/{schema-rs5a7ywx.js → schema-8mtsfspm.js} +1 -1
  61. package/dist/cli/{scope-persistence-1vsnkmm4.js → scope-persistence-h2fpgxww.js} +2 -2
  62. package/dist/cli/{skill-generator-n0h9n0zd.js → skill-generator-54xbjgak.js} +8 -8
  63. package/dist/cli/{workspace-snapshot-9h5za0s6.js → workspace-snapshot-jmyamqnv.js} +6 -1
  64. package/dist/cli/{worktree-collision-ownership-dtwr0xj4.js → worktree-collision-ownership-15hymf1y.js} +5 -5
  65. package/dist/commands/registry.d.ts +1 -1
  66. package/dist/hooks/pr-workflow-gate.d.ts +30 -4
  67. package/dist/hooks/repo-graph-builder.d.ts +18 -0
  68. package/dist/index.js +394 -390
  69. package/dist/tools/convene-council.d.ts +3 -3
  70. package/dist/tools/dispatch-lanes.d.ts +65 -7
  71. package/dist/tools/repo-graph/builder.d.ts +20 -2
  72. package/dist/tools/repo-graph/incremental.d.ts +18 -1
  73. package/dist/tools/repo-graph/ontology.d.ts +8 -0
  74. package/dist/tools/repo-graph/types.d.ts +62 -1
  75. package/dist/tools/repo-graph.d.ts +1 -1
  76. package/dist/tools/submit-phase-council-verdicts.d.ts +3 -3
  77. package/dist/tools/write-final-council-evidence.d.ts +3 -3
  78. package/dist/tools/write-pr-review-trigger-eval.d.ts +34 -63
  79. package/package.json +1 -1
  80. package/dist/cli/guardrail-explain-g1s89b3e.js +0 -57
@@ -5,13 +5,13 @@ import {
5
5
  getProfileForFile,
6
6
  isCommandAvailable,
7
7
  tool
8
- } from "./index-13xxjfhn.js";
8
+ } from "./index-m0ynxkaa.js";
9
9
  import {
10
10
  detectStraySwarmDirs,
11
11
  readDoctorArtifact,
12
12
  removeStraySwarmDir,
13
13
  runConfigDoctor
14
- } from "./index-5vp9gka3.js";
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-ens0gt38.js";
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-247b1c1m.js";
95
+ } from "./index-qqkw794p.js";
96
96
  import {
97
97
  readRecentEscalations
98
- } from "./index-5wx8wdxg.js";
98
+ } from "./index-ckdva4rs.js";
99
99
  import {
100
100
  alreadyCuratedThisGeneration,
101
101
  claimNextScanBatch,
102
102
  resolveCohortId
103
- } from "./index-bjrkg4tc.js";
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-vxv732ex.js";
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-z085b4ka.js";
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-2nqsr1dx.js";
136
+ } from "./index-jgkjxpdh.js";
137
137
  import {
138
138
  authorizeCuration,
139
139
  buildConfigFingerprintInput
140
- } from "./index-xhpmagdm.js";
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-j89zmcpx.js";
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-g1drmgxe.js";
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-yhba8swx.js";
199
+ } from "./index-dzyjb33e.js";
200
200
  import {
201
201
  atomicWriteFile
202
- } from "./index-xg9repnx.js";
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-jxchddjk.js";
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-dzaarxhg.js";
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-fpkb3mw1.js";
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-7an2qn0s.js";
269
+ } from "./index-hdy2nd5h.js";
270
270
  import {
271
271
  readSwarmFileAsync,
272
272
  safeHook,
273
273
  validateSwarmPath
274
- } from "./index-2ghkk9ve.js";
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
- } from "./index-3nasn371.js";
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
- await assertCurrentCheckoutHead(directory, normalizedHead);
1181
- if (!state.prHeadSha)
1182
- await assertPrReviewCleanCheckout(directory, state.mode);
1183
- if (!state.prHeadSha && state.mode === "PR_FEEDBACK") {
1184
- await assertPrFeedbackTrackingCheckout(directory, normalizedHead);
1185
- }
1186
- if (state.prHeadSha && state.prHeadSha !== normalizedHead) {
1187
- throw new Error(`BLOCKED: active ${state.mode} workflow is bound to PR head "${state.prHeadSha}"; received "${normalizedHead}"`);
1188
- }
1189
- if (state.prHeadSha === normalizedHead)
1190
- return state;
1191
- const nextState = {
1192
- ...state,
1193
- prHeadSha: normalizedHead,
1194
- updatedAt: isoNow()
1195
- };
1196
- await persistState(directory, nextState);
1197
- return nextState;
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
- throw new Error(`BLOCKED: current checkout HEAD "${currentHead}" does not match PR head "${normalizedExpected}" ` + `(working directory: "${directory}"). ` + `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\`.`);
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 upstream = await _test_exports.resolveCurrentUpstreamPushTargetAsync(directory);
1217
- if (!upstream) {
1218
- throw new Error("BLOCKED: PR_FEEDBACK requires a current local branch bound to an exact remote name, remote branch ref, and remote-tracking ref before the first head bind; detached or non-tracking checkouts are not allowed");
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 (!matchingRemoteRefs?.includes(upstream.remoteTrackingRef)) {
1222
- throw new Error(`BLOCKED: PR_FEEDBACK upstream "${upstream.remoteTrackingRef}" must point to the exact intake PR head "${prHeadSha}" before the first head bind`);
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
- if (!coveredLanes.every((coveredLane) => prReviewDiscoveryArtifactCoversLane(artifact.text, coveredLane))) {
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 prReviewDiscoveryArtifactCoversLane(text, expectedWorkflowLane) {
1862
- const lines = text.split(/\r?\n/);
1863
- let candidateHeaderSeen = false;
1864
- for (const line of lines) {
1865
- const fields = pipeFields(line);
1866
- if (fields.length === 0)
1867
- continue;
1868
- if (fields[0] === "[CLEAN]" && fields.length >= 4 && fields[1] === expectedWorkflowLane && fields.slice(1, 4).every(Boolean) && fields[2].length >= 12 && fields[3].length >= 20)
1869
- return true;
1870
- if (fields[0] === "[CANDIDATE]") {
1871
- if (fields[1]?.toLowerCase() === "candidate_id") {
1872
- candidateHeaderSeen = true;
1873
- continue;
1874
- }
1875
- if (fields.length >= 10 && fields[2] === expectedWorkflowLane && fields.slice(1, 10).every(Boolean))
1876
- return true;
1877
- continue;
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
- if (candidateHeaderSeen && fields.length >= 9 && fields[0]?.toLowerCase() !== "candidate_id" && fields[1] === expectedWorkflowLane && fields.slice(0, 9).every(Boolean))
1880
- return true;
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
- return false;
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
- return null;
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-29j794kd.js");
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 import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
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 = import_proper_lockfile.default.lock(dir, LOCK_RETRY_OPTS);
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-wdkkf3xg.js");
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-n0h9n0zd.js");
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-4tp3yy7e.js");
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-0cexjh3b.js");
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-jp0nen03.js");
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-rs5a7ywx.js");
20381
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-8mtsfspm.js");
18936
20382
  try {
18937
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-wdkkf3xg.js");
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-fjhyezyx.js");
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-f2yh22m1.js");
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-n0h9n0zd.js");
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-9q6f85bm.js");
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 import_proper_lockfile2 = __toESM(require_proper_lockfile(), 1);
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: import_proper_lockfile2.default,
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 import_proper_lockfile3 = __toESM(require_proper_lockfile(), 1);
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 import_proper_lockfile3.default.lock(dir, {
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-rs5a7ywx.js");
23031
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-8mtsfspm.js");
21586
23032
  try {
21587
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-wdkkf3xg.js");
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-rs5a7ywx.js");
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-rs5a7ywx.js"),
27163
- import("./curator-fjhyezyx.js"),
27164
- import("./curator-llm-factory-wr3zmy2c.js")
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-0cexjh3b.js");
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-etrfntkd.js");
29519
+ const { isLinked: isLinked2 } = await import("./knowledge-link-mm1w967j.js");
28074
29520
  if (isLinked2(directory)) {
28075
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-rs5a7ywx.js");
29521
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-8mtsfspm.js");
28076
29522
  let config;
28077
29523
  try {
28078
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-wdkkf3xg.js");
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-f7qz4tez.js");
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-6398t3a6.js");
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-0cczdmjv.js");
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 import_proper_lockfile4 = __toESM(require_proper_lockfile(), 1);
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 lockfile4 = import_proper_lockfile4.default;
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 = lockfile4.lockSync(lockTarget, {
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 import_proper_lockfile5 = __toESM(require_proper_lockfile(), 1);
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 import_proper_lockfile5.default.lock(storeDir, {
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 import_proper_lockfile5.default.lock(destinationDir, {
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 import_proper_lockfile6 = __toESM(require_proper_lockfile(), 1);
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 import_proper_lockfile6.default.lock(destStoragePath, {
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-gxbeb2ps.js");
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-gxbeb2ps.js");
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-gxbeb2ps.js");
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-w80r3axq.js");
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-w80r3axq.js");
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-g1s89b3e.js");
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-g1s89b3e.js");
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-bd45kw0x.js");
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, evaluates all 11 mandatory repository-agnostic risk families without applicability waivers (dedicated micro-lanes at tier L, consolidated sweeps at S/M), validates findings through independent reviewer confirmation, applies critic challenge to HIGH/CRITICAL findings, then synthesizes only after 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 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).",
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
- - RUN ALL MICRO-LANES: after the base explorer lanes settle, focus every repository-agnostic row from the exact diff/context pack and launch all 11 micro-lanes with \`mode: "swarm-pr-review:micro"\`, the complete all-\`MATCHED\` \`trigger_evaluation\`, and each trigger ID as \`workflow_lane\`. Keyword or path heuristics cannot waive a row. Missing any micro-lane is a BLOCKED coverage gap, not a speed optimization; split the lanes across bounded \`dispatch_lanes_async\` batches and use the same non-idling incremental collection pattern.
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: fetch the PR head ref if absent, verify the working tree is clean (git status --porcelain), and if tracked changes exist call \`prepare_pr_workflow_checkout\` with every explicit dirty tracked path before checkout. It returns an auditable stash OID and recovery command; it never stashes untracked files, which must be moved or removed manually. Do NOT run \`git stash\` through shell. Then check out the head branch. For a detached/fresh remote head, establish tracking before head binding with only \`git switch -c <local> --track <remote>/<branch>\` (or the constrained set-upstream form documented by the skill). If using \`gh pr checkout\`, never use \`--force\`, \`--recurse-submodules\`, or detached checkout. Before first bind, prove HEAD equals the authoritative full PR head SHA, the tree is clean, and the current branch tracks the intended PR remote/branch. Feedback verification and fix validation require the PR branch in the working tree.
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.