opencode-swarm 7.140.1 → 7.140.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.opencode/skills/swarm-pr-feedback/SKILL.md +19 -3
- package/.opencode/skills/swarm-pr-review/SKILL.md +61 -14
- package/dist/background/candidate-contract.d.ts +3 -0
- package/dist/cli/{config-doctor-0b5z13vq.js → config-doctor-shc0n4q1.js} +3 -3
- package/dist/cli/{core-m5y25n0j.js → core-p5cyej3r.js} +2 -2
- package/dist/cli/{curation-policy-bn7g8n40.js → curation-policy-7n6t8he4.js} +2 -2
- package/dist/cli/{curator-llm-factory-0mkbw2hg.js → curator-llm-factory-pr4bdfqw.js} +14 -14
- package/dist/cli/{curator-b7f6nt8h.js → curator-zp5qq24c.js} +14 -14
- package/dist/cli/{guardrail-explain-p2htdqkz.js → guardrail-explain-thddj6jv.js} +15 -15
- package/dist/cli/{guardrail-log-rqt9wrzw.js → guardrail-log-w8fmxe1x.js} +4 -4
- package/dist/cli/{hive-promoter-61sjdrgt.js → hive-promoter-0yg3gxg6.js} +14 -14
- package/dist/cli/{index-0an7an2s.js → index-0tevsq1a.js} +2 -2
- package/dist/cli/{index-hnprx6qv.js → index-1j5my2qy.js} +2 -2
- package/dist/cli/{index-dbg3agxz.js → index-325zmfyj.js} +4 -4
- package/dist/cli/{index-ff7b0cr5.js → index-4rje8dc0.js} +16 -16
- package/dist/cli/{index-dn3nkwbc.js → index-7ax0bkqh.js} +1 -1
- package/dist/cli/{index-vw75bhf3.js → index-898f16j9.js} +1 -1
- package/dist/cli/{index-yf6p4wc5.js → index-9txca231.js} +3 -3
- package/dist/cli/{index-jq7n7w9y.js → index-bmmzfrc7.js} +2 -2
- package/dist/cli/{index-acc18zrj.js → index-chx92bes.js} +2 -2
- package/dist/cli/{index-wysn7pcz.js → index-jfp1bgt1.js} +1 -1
- package/dist/cli/{index-v1d4qz8y.js → index-n408rw7n.js} +1 -1
- package/dist/cli/{index-7f72x6nv.js → index-n831qq2v.js} +435 -172
- package/dist/cli/{index-a98msw3z.js → index-t59cdg48.js} +1 -1
- package/dist/cli/{index-hgrzjhsf.js → index-vjqe1z2w.js} +3 -3
- package/dist/cli/{index-m9csth1h.js → index-x6mtyj6q.js} +2 -2
- package/dist/cli/{index-6fwr2w8v.js → index-y98ngs4g.js} +2 -2
- package/dist/cli/{index-7nhrn9sy.js → index-yt3vz0e1.js} +1 -1
- package/dist/cli/{index-r7tg8kjh.js → index-z6md3t8m.js} +5 -5
- package/dist/cli/index.js +14 -14
- package/dist/cli/{knowledge-escalator-twwz5a0d.js → knowledge-escalator-58tw74js.js} +3 -3
- package/dist/cli/{knowledge-events-hgaz06m8.js → knowledge-events-knr0pfq4.js} +1 -1
- package/dist/cli/{knowledge-store-46jrzgvm.js → knowledge-store-e2srcd89.js} +1 -1
- package/dist/cli/{knowledge-validator-7vnqwjtv.js → knowledge-validator-1dtj02q8.js} +4 -4
- package/dist/cli/{scan-cursor-acsjpv2n.js → scan-cursor-7tqsvbp8.js} +2 -2
- package/dist/cli/{schema-4dy7emnw.js → schema-b046zss2.js} +2 -2
- package/dist/cli/{skill-generator-a48t83hf.js → skill-generator-bvph9d98.js} +5 -5
- package/dist/commands/registry.d.ts +1 -1
- package/dist/git/pr-workflow-state.d.ts +3 -1
- package/dist/hooks/pr-workflow-gate.d.ts +19 -13
- package/dist/index.js +104 -99
- package/dist/tools/complete-pr-workflow.d.ts +6 -0
- package/dist/tools/gh-evidence.d.ts +6 -0
- package/dist/tools/prepare-pr-workflow-checkout.d.ts +11 -0
- package/package.json +1 -1
|
@@ -16,13 +16,13 @@ import {
|
|
|
16
16
|
} from "./index-1m1ksyqt.js";
|
|
17
17
|
import {
|
|
18
18
|
stableCanonicalStringify
|
|
19
|
-
} from "./index-
|
|
19
|
+
} from "./index-x6mtyj6q.js";
|
|
20
20
|
import {
|
|
21
21
|
detectStraySwarmDirs,
|
|
22
22
|
readDoctorArtifact,
|
|
23
23
|
removeStraySwarmDir,
|
|
24
24
|
runConfigDoctor
|
|
25
|
-
} from "./index-
|
|
25
|
+
} from "./index-chx92bes.js";
|
|
26
26
|
import {
|
|
27
27
|
MAX_TRANSIENT_RETRIES,
|
|
28
28
|
archiveEvidence,
|
|
@@ -122,15 +122,15 @@ import {
|
|
|
122
122
|
retireSkill,
|
|
123
123
|
selectCandidateEntries,
|
|
124
124
|
syncBundledProjectSkillsIfMissingAsync
|
|
125
|
-
} from "./index-
|
|
125
|
+
} from "./index-9txca231.js";
|
|
126
126
|
import {
|
|
127
127
|
readRecentEscalations
|
|
128
|
-
} from "./index-
|
|
128
|
+
} from "./index-z6md3t8m.js";
|
|
129
129
|
import {
|
|
130
130
|
alreadyCuratedThisGeneration,
|
|
131
131
|
claimNextScanBatch,
|
|
132
132
|
resolveCohortId
|
|
133
|
-
} from "./index-
|
|
133
|
+
} from "./index-yt3vz0e1.js";
|
|
134
134
|
import {
|
|
135
135
|
CURATOR_INIT_PROMPT,
|
|
136
136
|
CURATOR_PHASE_PROMPT,
|
|
@@ -222,11 +222,11 @@ import {
|
|
|
222
222
|
validateActionableFields,
|
|
223
223
|
validateLesson,
|
|
224
224
|
validateSkillPath
|
|
225
|
-
} from "./index-
|
|
225
|
+
} from "./index-1j5my2qy.js";
|
|
226
226
|
import {
|
|
227
227
|
authorizeCuration,
|
|
228
228
|
buildConfigFingerprintInput
|
|
229
|
-
} from "./index-
|
|
229
|
+
} from "./index-t59cdg48.js";
|
|
230
230
|
import {
|
|
231
231
|
cohortConfigFingerprint
|
|
232
232
|
} from "./index-rtry5xyf.js";
|
|
@@ -259,7 +259,7 @@ import {
|
|
|
259
259
|
transactFile,
|
|
260
260
|
transactKnowledge,
|
|
261
261
|
wordBigrams
|
|
262
|
-
} from "./index-
|
|
262
|
+
} from "./index-325zmfyj.js";
|
|
263
263
|
import {
|
|
264
264
|
MAX_EVENT_LOG_ENTRIES,
|
|
265
265
|
_internals,
|
|
@@ -269,7 +269,7 @@ import {
|
|
|
269
269
|
recordHiveKnowledgeEvent,
|
|
270
270
|
recordKnowledgeEvent,
|
|
271
271
|
resolveKnowledgeEventsPath
|
|
272
|
-
} from "./index-
|
|
272
|
+
} from "./index-jfp1bgt1.js";
|
|
273
273
|
import {
|
|
274
274
|
resolveHiveDataDir,
|
|
275
275
|
resolveHiveEventsPath,
|
|
@@ -313,7 +313,7 @@ import {
|
|
|
313
313
|
init_loader,
|
|
314
314
|
loadPluginConfig,
|
|
315
315
|
loadPluginConfigWithMeta
|
|
316
|
-
} from "./index-
|
|
316
|
+
} from "./index-0tevsq1a.js";
|
|
317
317
|
import {
|
|
318
318
|
RetrospectiveEvidenceSchema
|
|
319
319
|
} from "./index-gprxjmm7.js";
|
|
@@ -345,7 +345,7 @@ import {
|
|
|
345
345
|
package_default,
|
|
346
346
|
resolveAutoReviewConfig,
|
|
347
347
|
stripKnownSwarmPrefix
|
|
348
|
-
} from "./index-
|
|
348
|
+
} from "./index-bmmzfrc7.js";
|
|
349
349
|
import {
|
|
350
350
|
AGENT_TOOL_MAP,
|
|
351
351
|
ALL_AGENT_NAMES,
|
|
@@ -357,7 +357,7 @@ import {
|
|
|
357
357
|
init_agent_names,
|
|
358
358
|
init_constants,
|
|
359
359
|
init_tool_metadata
|
|
360
|
-
} from "./index-
|
|
360
|
+
} from "./index-n408rw7n.js";
|
|
361
361
|
import {
|
|
362
362
|
KNOWLEDGE_SCHEMA_VERSION,
|
|
363
363
|
OUTCOME_BLOCK_THRESHOLD,
|
|
@@ -509,6 +509,122 @@ function removeCandidateCodeFences(text) {
|
|
|
509
509
|
return lines.join(`
|
|
510
510
|
`);
|
|
511
511
|
}
|
|
512
|
+
function parseClosedMarkdownFences(text) {
|
|
513
|
+
const lines = text.split(/\r?\n/);
|
|
514
|
+
const blocks = [];
|
|
515
|
+
const fencedLineIndexes = new Set;
|
|
516
|
+
let openingLine = -1;
|
|
517
|
+
let openingWidth = 0;
|
|
518
|
+
for (let index = 0;index < lines.length; index += 1) {
|
|
519
|
+
const line = lines[index];
|
|
520
|
+
if (openingLine < 0) {
|
|
521
|
+
const opening = line.match(/^\s{0,3}(`{3,})([^`]*)$/);
|
|
522
|
+
if (!opening)
|
|
523
|
+
continue;
|
|
524
|
+
openingLine = index;
|
|
525
|
+
openingWidth = opening[1].length;
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
const closing = line.match(/^\s{0,3}(`{3,})\s*$/);
|
|
529
|
+
if (!closing || closing[1].length < openingWidth)
|
|
530
|
+
continue;
|
|
531
|
+
const content = lines.slice(openingLine + 1, index);
|
|
532
|
+
blocks.push({ openingLine, closingLine: index, content });
|
|
533
|
+
for (let fenced = openingLine;fenced <= index; fenced += 1) {
|
|
534
|
+
fencedLineIndexes.add(fenced);
|
|
535
|
+
}
|
|
536
|
+
openingLine = -1;
|
|
537
|
+
openingWidth = 0;
|
|
538
|
+
}
|
|
539
|
+
if (openingLine >= 0)
|
|
540
|
+
return null;
|
|
541
|
+
return {
|
|
542
|
+
lines,
|
|
543
|
+
blocks,
|
|
544
|
+
unfencedLines: lines.filter((_, index) => !fencedLineIndexes.has(index))
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
function lineCarriesCandidateProtocol(line) {
|
|
548
|
+
const fields = splitPipeFields(line.trim()).map((field) => field.trim());
|
|
549
|
+
return fields[0] === CANDIDATE_MARKER || fields[0] === CLEAN_MARKER || candidateHeaderFamily(fields) !== null;
|
|
550
|
+
}
|
|
551
|
+
function lastUnescapedPipeIndex(line) {
|
|
552
|
+
let last = -1;
|
|
553
|
+
for (let index = 0;index < line.length; index += 1) {
|
|
554
|
+
if (line[index] === "\\" && line[index + 1] === "|") {
|
|
555
|
+
index += 1;
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
if (line[index] === "|")
|
|
559
|
+
last = index;
|
|
560
|
+
}
|
|
561
|
+
return last;
|
|
562
|
+
}
|
|
563
|
+
function repairRedundantCleanConfidenceLine(line, family) {
|
|
564
|
+
const fields = splitPipeFields(line).map((field) => field.trim());
|
|
565
|
+
if (fields.length !== CLEAN_FIELD_COUNT + 1 || fields[0] !== CLEAN_MARKER || !isCandidateConfidence(fields[CLEAN_FIELD_COUNT]) || !analyzeCleanFields(fields.slice(0, CLEAN_FIELD_COUNT), family).valid) {
|
|
566
|
+
return { line, repaired: false };
|
|
567
|
+
}
|
|
568
|
+
const delimiter = lastUnescapedPipeIndex(line);
|
|
569
|
+
if (delimiter < 0)
|
|
570
|
+
return { line, repaired: false };
|
|
571
|
+
return { line: line.slice(0, delimiter).trimEnd(), repaired: true };
|
|
572
|
+
}
|
|
573
|
+
function repairRedundantCleanConfidence(text, family) {
|
|
574
|
+
let repaired = false;
|
|
575
|
+
const lines = text.split(/\r?\n/).map((line) => {
|
|
576
|
+
const result = repairRedundantCleanConfidenceLine(line, family);
|
|
577
|
+
repaired ||= result.repaired;
|
|
578
|
+
return result.line;
|
|
579
|
+
});
|
|
580
|
+
return { text: lines.join(`
|
|
581
|
+
`), repaired };
|
|
582
|
+
}
|
|
583
|
+
function isStrictProtocolDataLine(line, family) {
|
|
584
|
+
const clean = repairRedundantCleanConfidenceLine(line, family).line;
|
|
585
|
+
const fields = splitPipeFields(clean).map((field) => field.trim());
|
|
586
|
+
if (fields[0] === CLEAN_MARKER) {
|
|
587
|
+
return analyzeCleanFields(fields, family).valid;
|
|
588
|
+
}
|
|
589
|
+
if (fields[0] === CANDIDATE_MARKER) {
|
|
590
|
+
return analyzeCandidateFields(fields.slice(1), family).valid;
|
|
591
|
+
}
|
|
592
|
+
return analyzeCandidateFields(fields, family).valid;
|
|
593
|
+
}
|
|
594
|
+
function recoverTerminalProtocolFence(rawText, family) {
|
|
595
|
+
const parsed = parseClosedMarkdownFences(rawText);
|
|
596
|
+
if (!parsed || parsed.blocks.length === 0)
|
|
597
|
+
return null;
|
|
598
|
+
if (parsed.unfencedLines.some(lineCarriesCandidateProtocol))
|
|
599
|
+
return null;
|
|
600
|
+
let lastNonblankLine = parsed.lines.length - 1;
|
|
601
|
+
while (lastNonblankLine >= 0 && parsed.lines[lastNonblankLine].trim().length === 0) {
|
|
602
|
+
lastNonblankLine -= 1;
|
|
603
|
+
}
|
|
604
|
+
const terminal = parsed.blocks.at(-1);
|
|
605
|
+
if (terminal.closingLine !== lastNonblankLine)
|
|
606
|
+
return null;
|
|
607
|
+
if (parsed.blocks.slice(0, -1).some((block) => block.content.some(lineCarriesCandidateProtocol))) {
|
|
608
|
+
return null;
|
|
609
|
+
}
|
|
610
|
+
const content = terminal.content.slice().map((line) => line.trimEnd());
|
|
611
|
+
while (content[0]?.trim() === "")
|
|
612
|
+
content.shift();
|
|
613
|
+
while (content.at(-1)?.trim() === "")
|
|
614
|
+
content.pop();
|
|
615
|
+
if (content[0]?.trim() !== CANDIDATE_HEADERS[family])
|
|
616
|
+
return null;
|
|
617
|
+
const rows = content.slice(1).filter((line) => line.trim() !== "");
|
|
618
|
+
if (rows.length === 0 || !rows.every((line) => isStrictProtocolDataLine(line, family))) {
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
const outside = parsed.unfencedLines.join(`
|
|
622
|
+
`).trimEnd();
|
|
623
|
+
const protocol = content.join(`
|
|
624
|
+
`);
|
|
625
|
+
return outside ? `${outside}
|
|
626
|
+
${protocol}` : protocol;
|
|
627
|
+
}
|
|
512
628
|
function declaredCanonicalFamilies(source) {
|
|
513
629
|
const families = [];
|
|
514
630
|
for (const line of source.split(/\r?\n/)) {
|
|
@@ -519,16 +635,23 @@ function declaredCanonicalFamilies(source) {
|
|
|
519
635
|
return families;
|
|
520
636
|
}
|
|
521
637
|
function normalizeCandidateArtifact(rawText, fallbackFamily) {
|
|
522
|
-
const
|
|
638
|
+
const repairKinds = [];
|
|
639
|
+
const recoveredFence = recoverTerminalProtocolFence(rawText, fallbackFamily);
|
|
640
|
+
if (recoveredFence !== null)
|
|
641
|
+
repairKinds.push("terminal-protocol-fence");
|
|
642
|
+
const cleanRepair = repairRedundantCleanConfidence(recoveredFence ?? removeCandidateCodeFences(rawText), fallbackFamily);
|
|
643
|
+
if (cleanRepair.repaired)
|
|
644
|
+
repairKinds.push("redundant-clean-confidence");
|
|
645
|
+
const text = cleanRepair.text;
|
|
523
646
|
const header = selectCandidateHeader(text.split(/\r?\n/));
|
|
524
647
|
if (header?.markerBearing && header.family !== null) {
|
|
525
|
-
return { text, synthesizedHeader: false };
|
|
648
|
+
return { text, synthesizedHeader: false, repairKinds };
|
|
526
649
|
}
|
|
527
650
|
if (declaredCanonicalFamilies(text).length > 0) {
|
|
528
|
-
return { text, synthesizedHeader: false };
|
|
651
|
+
return { text, synthesizedHeader: false, repairKinds };
|
|
529
652
|
}
|
|
530
653
|
if (declaredCanonicalFamilies(rawText).some((family) => family !== fallbackFamily)) {
|
|
531
|
-
return { text, synthesizedHeader: false };
|
|
654
|
+
return { text, synthesizedHeader: false, repairKinds };
|
|
532
655
|
}
|
|
533
656
|
const lines = text.split(/\r?\n/);
|
|
534
657
|
const hasSalvageableRow = lines.some((line) => {
|
|
@@ -540,11 +663,12 @@ function normalizeCandidateArtifact(rawText, fallbackFamily) {
|
|
|
540
663
|
return analyzeCleanFields(fields, fallbackFamily).valid;
|
|
541
664
|
});
|
|
542
665
|
if (!hasSalvageableRow)
|
|
543
|
-
return { text, synthesizedHeader: false };
|
|
666
|
+
return { text, synthesizedHeader: false, repairKinds };
|
|
544
667
|
const firstSalvageableIndex = lines.findIndex((line) => {
|
|
545
668
|
const marker = splitPipeFields(line)[0]?.trim();
|
|
546
669
|
return marker === CANDIDATE_MARKER || marker === CLEAN_MARKER;
|
|
547
670
|
});
|
|
671
|
+
repairKinds.push("synthesized-header");
|
|
548
672
|
return {
|
|
549
673
|
text: [
|
|
550
674
|
...lines.slice(0, firstSalvageableIndex),
|
|
@@ -552,7 +676,8 @@ function normalizeCandidateArtifact(rawText, fallbackFamily) {
|
|
|
552
676
|
...lines.slice(firstSalvageableIndex)
|
|
553
677
|
].join(`
|
|
554
678
|
`),
|
|
555
|
-
synthesizedHeader: true
|
|
679
|
+
synthesizedHeader: true,
|
|
680
|
+
repairKinds
|
|
556
681
|
};
|
|
557
682
|
}
|
|
558
683
|
function analyzeCandidateLaneContext(context) {
|
|
@@ -1914,7 +2039,7 @@ async function activeOperations(gitDir) {
|
|
|
1914
2039
|
}));
|
|
1915
2040
|
return checks.filter((value) => value !== null);
|
|
1916
2041
|
}
|
|
1917
|
-
async function classifyPrWorkflowGitState(directory) {
|
|
2042
|
+
async function classifyPrWorkflowGitState(directory, options = {}) {
|
|
1918
2043
|
const [statusResult, rootResult, gitDirResult] = await Promise.all([
|
|
1919
2044
|
_internals4.runGitCapture(directory, [
|
|
1920
2045
|
"status",
|
|
@@ -1937,16 +2062,25 @@ async function classifyPrWorkflowGitState(directory) {
|
|
|
1937
2062
|
}
|
|
1938
2063
|
const worktreeRoot = rootResult.stdout.trim();
|
|
1939
2064
|
const gitDir = gitDirResult.stdout.trim();
|
|
1940
|
-
const
|
|
1941
|
-
if (!worktreeRoot || !gitDir || !
|
|
2065
|
+
const parsedRaw = parsePorcelainV2Snapshot(statusResult.stdout);
|
|
2066
|
+
if (!worktreeRoot || !gitDir || !parsedRaw) {
|
|
1942
2067
|
return {
|
|
1943
2068
|
kind: "indeterminate",
|
|
1944
2069
|
code: "GIT_STATE_INDETERMINATE",
|
|
1945
2070
|
retryable: false,
|
|
1946
2071
|
requiredAction: "Resolve the malformed or incomplete Git status before starting the PR workflow.",
|
|
1947
|
-
evidence: evidence(
|
|
2072
|
+
evidence: evidence(parsedRaw, worktreeRoot || null, gitDir || null, [], "Git returned an incomplete status snapshot")
|
|
1948
2073
|
};
|
|
1949
2074
|
}
|
|
2075
|
+
const ignoredPaths = new Set((options.ignoredPaths ?? []).map((value) => value.replace(/\\/g, "/")));
|
|
2076
|
+
const keepPath = (value) => !ignoredPaths.has(value.replace(/\\/g, "/"));
|
|
2077
|
+
const parsed = {
|
|
2078
|
+
...parsedRaw,
|
|
2079
|
+
dirtyTrackedPaths: parsedRaw.dirtyTrackedPaths.filter(keepPath),
|
|
2080
|
+
untrackedPaths: parsedRaw.untrackedPaths.filter(keepPath),
|
|
2081
|
+
unmergedPaths: parsedRaw.unmergedPaths.filter(keepPath),
|
|
2082
|
+
dirtySubmodulePaths: parsedRaw.dirtySubmodulePaths.filter(keepPath)
|
|
2083
|
+
};
|
|
1950
2084
|
let operations;
|
|
1951
2085
|
try {
|
|
1952
2086
|
operations = await _internals4.activeOperations(path2.resolve(gitDir));
|
|
@@ -2059,6 +2193,7 @@ var STATE_MUTATION_LOCK_RETRY_DELAY_MS = 10;
|
|
|
2059
2193
|
var STATE_MUTATION_LOCK_UNINITIALIZED_STALE_MS = 30000;
|
|
2060
2194
|
var MAX_COMPLETED_CHECKOUT_LOCK_OWNERS = 64;
|
|
2061
2195
|
var CHECKOUT_MUTATION_ACTION_TIMEOUT_MS = 5 * 60000;
|
|
2196
|
+
var MAX_PR_WORKFLOW_GATE_DIRECTORY_ENTRIES = MAX_TRACKED_SESSIONS * 2 + 1;
|
|
2062
2197
|
var MAX_CANDIDATE_ISSUES_PER_ARTIFACT = 8;
|
|
2063
2198
|
var MAX_BASE_COVERAGE_DIAGNOSTICS = 8;
|
|
2064
2199
|
var MAX_BASE_COVERAGE_DIAGNOSTIC_CHARS = 1000;
|
|
@@ -2257,57 +2392,66 @@ function formatCheckoutRecoveryBlock(state) {
|
|
|
2257
2392
|
}
|
|
2258
2393
|
async function activatePrWorkflow(directory, sessionID, mode, options = {}) {
|
|
2259
2394
|
const normalizedSessionID = normalizeSessionID(sessionID);
|
|
2260
|
-
const
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
if (
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2395
|
+
const initiallyActive = await readPrWorkflowGateState(directory, normalizedSessionID);
|
|
2396
|
+
const checkoutPreflight = !initiallyActive && options.requireCheckoutPreflight ? await _test_exports.classifyPrWorkflowGitStateAsync(directory) : null;
|
|
2397
|
+
return withPrWorkflowCheckoutMutationLock(directory, async () => withSessionStateMutation(directory, normalizedSessionID, async () => {
|
|
2398
|
+
let existing = await readPrWorkflowGateStateFromDisk(directory, normalizedSessionID);
|
|
2399
|
+
if (existing?.mode === mode) {
|
|
2400
|
+
if (mode === "PR_FEEDBACK" && options.prUrl) {
|
|
2401
|
+
const requestedTarget = canonicalGitHubPrUrl(options.prUrl);
|
|
2402
|
+
if (!requestedTarget) {
|
|
2403
|
+
throw new Error("BLOCKED: PR_FEEDBACK target must be a canonical GitHub PR URL");
|
|
2404
|
+
}
|
|
2405
|
+
const existingTarget = existing.prFeedbackTargetUrl ? canonicalGitHubPrUrl(existing.prFeedbackTargetUrl) : null;
|
|
2406
|
+
if (existingTarget && requestedTarget !== existingTarget) {
|
|
2407
|
+
throw new Error(`BLOCKED: active PR_FEEDBACK targets a different GitHub pull request`);
|
|
2408
|
+
}
|
|
2409
|
+
if (!existingTarget) {
|
|
2410
|
+
existing = await writeStateWhileLocked(directory, {
|
|
2411
|
+
...existing,
|
|
2412
|
+
prFeedbackTargetUrl: options.prUrl,
|
|
2413
|
+
updatedAt: isoNow()
|
|
2414
|
+
});
|
|
2415
|
+
}
|
|
2279
2416
|
}
|
|
2417
|
+
return options.prHeadSha ? bindPrWorkflowHeadWhileLocked(directory, normalizedSessionID, options.prHeadSha) : existing;
|
|
2280
2418
|
}
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
if (existing) {
|
|
2284
|
-
throw new Error(`BLOCKED: session "${normalizedSessionID}" already has an active ${existing.mode} workflow; complete it before starting ${mode}`);
|
|
2285
|
-
}
|
|
2286
|
-
if (options.requireCheckoutPreflight) {
|
|
2287
|
-
const checkoutState = await classifyPrWorkflowGitState(directory);
|
|
2288
|
-
if (checkoutState.kind === "recovery-required" || checkoutState.kind === "indeterminate") {
|
|
2289
|
-
throw new Error(formatCheckoutRecoveryBlock(checkoutState));
|
|
2419
|
+
if (existing) {
|
|
2420
|
+
throw new Error(`BLOCKED: session "${normalizedSessionID}" already has an active ${existing.mode} workflow; complete it before starting ${mode}`);
|
|
2290
2421
|
}
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2422
|
+
if (options.requireCheckoutPreflight) {
|
|
2423
|
+
if (!checkoutPreflight) {
|
|
2424
|
+
throw new Error("BLOCKED: PR workflow gate state changed during checkout preflight; retry activation against the current project state");
|
|
2425
|
+
}
|
|
2426
|
+
const lockedCheckoutPreflight = await _test_exports.classifyPrWorkflowGitStateAsync(directory, {
|
|
2427
|
+
ignoredPaths: [
|
|
2428
|
+
path3.join(".swarm", workflowCheckoutMutationLockRelativePath()),
|
|
2429
|
+
path3.join(".swarm", workflowGateStateLockRelativePath(normalizedSessionID))
|
|
2430
|
+
]
|
|
2431
|
+
});
|
|
2432
|
+
if (lockedCheckoutPreflight.kind === "recovery-required" || lockedCheckoutPreflight.kind === "indeterminate") {
|
|
2433
|
+
throw new Error(formatCheckoutRecoveryBlock(lockedCheckoutPreflight));
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
const initialHead = options.prHeadSha ? await assertCurrentCheckoutHead(directory, options.prHeadSha, mode) : undefined;
|
|
2437
|
+
const timestamp = isoNow();
|
|
2438
|
+
const feedbackTargetUrl = mode === "PR_FEEDBACK" && options.prUrl ? canonicalGitHubPrUrl(options.prUrl) ? options.prUrl : null : undefined;
|
|
2439
|
+
if (feedbackTargetUrl === null) {
|
|
2440
|
+
throw new Error("BLOCKED: PR_FEEDBACK target must be a canonical GitHub PR URL");
|
|
2441
|
+
}
|
|
2442
|
+
const nextState = {
|
|
2443
|
+
schemaVersion: GATE_SCHEMA_VERSION,
|
|
2444
|
+
revision: 0,
|
|
2445
|
+
workflowInstanceId: randomUUID(),
|
|
2446
|
+
sessionID: normalizedSessionID,
|
|
2447
|
+
mode,
|
|
2448
|
+
activatedAt: timestamp,
|
|
2449
|
+
updatedAt: timestamp,
|
|
2450
|
+
...initialHead ? { prHeadSha: initialHead } : {},
|
|
2451
|
+
...feedbackTargetUrl ? { prFeedbackTargetUrl: feedbackTargetUrl } : {}
|
|
2452
|
+
};
|
|
2453
|
+
return writeStateWhileLocked(directory, nextState);
|
|
2454
|
+
}));
|
|
2311
2455
|
}
|
|
2312
2456
|
async function readPrWorkflowGateState(directory, sessionID) {
|
|
2313
2457
|
const normalizedSessionID = normalizeSessionID(sessionID);
|
|
@@ -2353,9 +2497,6 @@ async function abortPrWorkflow(directory, sessionID, options = { kind: "recovery
|
|
|
2353
2497
|
const laneIds = openLanes.map((record) => record.laneId ?? record.subagentSessionId ?? "unknown").filter(Boolean).slice(0, 10).join(", ");
|
|
2354
2498
|
throw new Error(`BLOCKED: ${state.mode} abort refused while ${openLanes.length} PR workflow lane(s) are still in flight (lane ids: ${laneIds}). Collect their results or let them settle before aborting.`);
|
|
2355
2499
|
}
|
|
2356
|
-
if (options.kind === "recovery" && state.prHeadSha && !state.checkoutRecovery) {
|
|
2357
|
-
throw new Error(`BLOCKED: recovery abort of a bound ${state.mode} (pr_head_sha ${state.prHeadSha}) requires a controller-recorded terminal recovery condition (checkoutRecovery). Binding succeeded, so "checkout unreachable" no longer justifies a recovery abort \u2014 complete the workflow, resolve the Git recovery state, or use the user-authorized force abort (/swarm abort-pr-workflow) to override.`);
|
|
2358
|
-
}
|
|
2359
2500
|
const sanitizedReason = typeof options.reason === "string" && options.reason.trim().length > 0 ? options.reason.trim().slice(0, 500) : undefined;
|
|
2360
2501
|
if (sanitizedReason === undefined) {
|
|
2361
2502
|
throw new Error(`BLOCKED: ${state.mode} abort requires a non-empty reason (recorded to the audit trail).`);
|
|
@@ -2383,47 +2524,45 @@ async function abortPrWorkflow(directory, sessionID, options = { kind: "recovery
|
|
|
2383
2524
|
openLanes: openLanes.length
|
|
2384
2525
|
};
|
|
2385
2526
|
}
|
|
2386
|
-
async function
|
|
2527
|
+
async function bindPrWorkflowHeadWhileLocked(directory, normalizedSessionID, prHeadSha) {
|
|
2387
2528
|
const normalizedHead = normalizePrHeadSha(prHeadSha);
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2529
|
+
const state = await readPrWorkflowGateStateFromDisk(directory, normalizedSessionID);
|
|
2530
|
+
if (!state) {
|
|
2531
|
+
throw new Error(`BLOCKED: no active PR workflow gate for session "${normalizedSessionID}". ` + "The gate must be activated before the PR head can be bound.");
|
|
2532
|
+
}
|
|
2533
|
+
await assertCurrentCheckoutHead(directory, normalizedHead, state.mode);
|
|
2534
|
+
if (state.prHeadSha && state.prHeadSha !== normalizedHead) {
|
|
2535
|
+
throw new Error(`BLOCKED: active ${state.mode} workflow is bound to PR head "${state.prHeadSha}"; received "${normalizedHead}"`);
|
|
2536
|
+
}
|
|
2537
|
+
if (!state.prHeadSha) {
|
|
2538
|
+
await assertPrReviewCleanCheckout(directory, state.mode);
|
|
2539
|
+
}
|
|
2540
|
+
if (!state.prHeadSha && state.mode === "PR_FEEDBACK") {
|
|
2541
|
+
await assertPrFeedbackTrackingCheckout(directory, normalizedHead);
|
|
2542
|
+
}
|
|
2543
|
+
if (state.prHeadSha === normalizedHead) {
|
|
2544
|
+
if (state.mode === "PR_FEEDBACK") {
|
|
2401
2545
|
await assertPrFeedbackTrackingCheckout(directory, normalizedHead);
|
|
2402
2546
|
}
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
updatedAt: isoNow()
|
|
2414
|
-
};
|
|
2415
|
-
await _test_exports.beforePrFeedbackTrackingPersist?.();
|
|
2416
|
-
return writeStateWhileLocked(directory, nextState);
|
|
2417
|
-
}));
|
|
2547
|
+
return state;
|
|
2548
|
+
}
|
|
2549
|
+
const nextState = {
|
|
2550
|
+
...state,
|
|
2551
|
+
prHeadSha: normalizedHead,
|
|
2552
|
+
checkoutRecovery: undefined,
|
|
2553
|
+
updatedAt: isoNow()
|
|
2554
|
+
};
|
|
2555
|
+
await _test_exports.beforePrFeedbackTrackingPersist?.();
|
|
2556
|
+
return writeStateWhileLocked(directory, nextState);
|
|
2418
2557
|
}
|
|
2419
2558
|
async function assertCurrentCheckoutHead(directory, expectedHead, mode = "PR_REVIEW") {
|
|
2420
2559
|
const normalizedExpected = normalizePrHeadSha(expectedHead);
|
|
2421
2560
|
const currentHead = (await _test_exports.resolveCurrentGitHeadAsync(directory))?.trim();
|
|
2422
2561
|
if (!currentHead) {
|
|
2423
|
-
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
|
|
2562
|
+
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 two separate standalone commands. First run: git -C "${directory}" rev-parse --verify HEAD^0. Then run: git -C "${directory}" cat-file -t HEAD (which must print commit).`);
|
|
2424
2563
|
}
|
|
2425
2564
|
if (currentHead.toLowerCase() !== normalizedExpected.toLowerCase()) {
|
|
2426
|
-
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
|
|
2565
|
+
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 bare, standalone commands from that directory. If the commit is not present locally, first run \`git fetch origin <pr-head-ref>\`. Then run \`git switch --detach ${normalizedExpected}\`. Do not prefix the switch with \`git -C\`; the read-only shell classifier refuses \`git -C ... switch\`.`;
|
|
2427
2566
|
throw new Error(`BLOCKED: current checkout HEAD "${currentHead}" does not match PR head "${normalizedExpected}" ` + `(working directory: "${directory}"). ${remediation}`);
|
|
2428
2567
|
}
|
|
2429
2568
|
return normalizedExpected;
|
|
@@ -2945,6 +3084,7 @@ var _test_exports = {
|
|
|
2945
3084
|
_test_exports.openCheckoutLock = openCheckoutLockFile;
|
|
2946
3085
|
_test_exports.removeCheckoutLock = removeCheckoutLockFile;
|
|
2947
3086
|
_test_exports.checkoutMutationActionTimeoutMs = CHECKOUT_MUTATION_ACTION_TIMEOUT_MS;
|
|
3087
|
+
_test_exports.classifyPrWorkflowGitStateAsync = classifyPrWorkflowGitState;
|
|
2948
3088
|
},
|
|
2949
3089
|
beforeTerminalClear: undefined,
|
|
2950
3090
|
beforePrFeedbackTransitionLock: undefined,
|
|
@@ -2960,6 +3100,7 @@ var _test_exports = {
|
|
|
2960
3100
|
openCheckoutLock: openCheckoutLockFile,
|
|
2961
3101
|
removeCheckoutLock: removeCheckoutLockFile,
|
|
2962
3102
|
checkoutMutationActionTimeoutMs: CHECKOUT_MUTATION_ACTION_TIMEOUT_MS,
|
|
3103
|
+
classifyPrWorkflowGitStateAsync: classifyPrWorkflowGitState,
|
|
2963
3104
|
resolveCurrentGitHead,
|
|
2964
3105
|
resolveCurrentGitHeadAsync,
|
|
2965
3106
|
resolveCurrentUpstreamPushTarget,
|
|
@@ -3072,8 +3213,128 @@ var PR_REVIEW_READ_ONLY_TOOL_NAMES = new Set([
|
|
|
3072
3213
|
"webfetch",
|
|
3073
3214
|
"websearch"
|
|
3074
3215
|
]);
|
|
3216
|
+
var PR_WORKFLOW_CD_PREFIX_PATTERN = /^cd\s+(?:\/d\s+)?(?:"[A-Za-z0-9 _.\\/:~+-]+"|'[A-Za-z0-9 _.\\/:~+-]+'|[A-Za-z0-9_.\\/:~+-]+)\s*&&\s*/i;
|
|
3217
|
+
var PR_WORKFLOW_TRAILING_STDERR_MERGE_PATTERN = /\s+2>&1\s*$/;
|
|
3218
|
+
var PR_WORKFLOW_MAX_CD_PREFIX_STRIPS = 3;
|
|
3219
|
+
function normalizePrWorkflowShellCommand(command) {
|
|
3220
|
+
let normalized = command.trim();
|
|
3221
|
+
const withoutTrailingMerge = normalized.replace(PR_WORKFLOW_TRAILING_STDERR_MERGE_PATTERN, "");
|
|
3222
|
+
if (withoutTrailingMerge !== normalized)
|
|
3223
|
+
normalized = withoutTrailingMerge.trim();
|
|
3224
|
+
let strippedCdPrefix = false;
|
|
3225
|
+
for (let index = 0;index < PR_WORKFLOW_MAX_CD_PREFIX_STRIPS; index += 1) {
|
|
3226
|
+
const next = normalized.replace(PR_WORKFLOW_CD_PREFIX_PATTERN, "");
|
|
3227
|
+
if (next === normalized)
|
|
3228
|
+
break;
|
|
3229
|
+
normalized = next.trim();
|
|
3230
|
+
strippedCdPrefix = true;
|
|
3231
|
+
}
|
|
3232
|
+
return { normalized, strippedCdPrefix };
|
|
3233
|
+
}
|
|
3234
|
+
function classifyPrWorkflowShellSyntax(command) {
|
|
3235
|
+
const { normalized: inner } = normalizePrWorkflowShellCommand(command);
|
|
3236
|
+
const compact = inner.replace(/\s+/g, " ").trim();
|
|
3237
|
+
if (!compact)
|
|
3238
|
+
return { unsafe: true, reason: "compound-syntax" };
|
|
3239
|
+
if (/\$\(|@\(/.test(inner)) {
|
|
3240
|
+
return { unsafe: true, reason: "command-substitution" };
|
|
3241
|
+
}
|
|
3242
|
+
if (/\\"|\^"/.test(inner)) {
|
|
3243
|
+
return { unsafe: true, reason: "ambiguous-escaped-quote" };
|
|
3244
|
+
}
|
|
3245
|
+
if (/[\r\n;&<>`]/.test(inner)) {
|
|
3246
|
+
return { unsafe: true, reason: "compound-syntax" };
|
|
3247
|
+
}
|
|
3248
|
+
const tokens = [];
|
|
3249
|
+
let current = "";
|
|
3250
|
+
let quoted = false;
|
|
3251
|
+
let quote = null;
|
|
3252
|
+
let pipeIsDoubleQuoted = true;
|
|
3253
|
+
const flush = () => {
|
|
3254
|
+
if (current.length > 0 || quoted) {
|
|
3255
|
+
tokens.push({ value: current, quoted, pipeIsDoubleQuoted });
|
|
3256
|
+
}
|
|
3257
|
+
current = "";
|
|
3258
|
+
quoted = false;
|
|
3259
|
+
quote = null;
|
|
3260
|
+
pipeIsDoubleQuoted = true;
|
|
3261
|
+
};
|
|
3262
|
+
for (let index = 0;index < inner.length; index += 1) {
|
|
3263
|
+
const ch = inner[index];
|
|
3264
|
+
if (quote === null) {
|
|
3265
|
+
if (/\s/.test(ch)) {
|
|
3266
|
+
flush();
|
|
3267
|
+
continue;
|
|
3268
|
+
}
|
|
3269
|
+
if (ch === "'" || ch === '"') {
|
|
3270
|
+
quoted = true;
|
|
3271
|
+
quote = ch;
|
|
3272
|
+
continue;
|
|
3273
|
+
}
|
|
3274
|
+
if (ch === ";" || ch === "<" || ch === ">" || ch === "`" || ch === "|") {
|
|
3275
|
+
return { unsafe: true, reason: "compound-syntax" };
|
|
3276
|
+
}
|
|
3277
|
+
if (ch === "$" && inner[index + 1] === "(") {
|
|
3278
|
+
return { unsafe: true, reason: "command-substitution" };
|
|
3279
|
+
}
|
|
3280
|
+
if (ch === "@" && inner[index + 1] === "(") {
|
|
3281
|
+
return { unsafe: true, reason: "command-substitution" };
|
|
3282
|
+
}
|
|
3283
|
+
current += ch;
|
|
3284
|
+
continue;
|
|
3285
|
+
}
|
|
3286
|
+
if (quote === "'") {
|
|
3287
|
+
if (ch === "'") {
|
|
3288
|
+
quote = null;
|
|
3289
|
+
continue;
|
|
3290
|
+
}
|
|
3291
|
+
if (ch === "|")
|
|
3292
|
+
pipeIsDoubleQuoted = false;
|
|
3293
|
+
current += ch;
|
|
3294
|
+
continue;
|
|
3295
|
+
}
|
|
3296
|
+
if (ch === '"') {
|
|
3297
|
+
quote = null;
|
|
3298
|
+
continue;
|
|
3299
|
+
}
|
|
3300
|
+
if (ch === "\\" && inner[index + 1] === '"') {
|
|
3301
|
+
return { unsafe: true, reason: "ambiguous-escaped-quote" };
|
|
3302
|
+
}
|
|
3303
|
+
if (ch === "`") {
|
|
3304
|
+
return { unsafe: true, reason: "command-substitution" };
|
|
3305
|
+
}
|
|
3306
|
+
if (ch === "$" && inner[index + 1] === "(") {
|
|
3307
|
+
return { unsafe: true, reason: "command-substitution" };
|
|
3308
|
+
}
|
|
3309
|
+
if (ch === "@" && inner[index + 1] === "(") {
|
|
3310
|
+
return { unsafe: true, reason: "command-substitution" };
|
|
3311
|
+
}
|
|
3312
|
+
current += ch;
|
|
3313
|
+
}
|
|
3314
|
+
if (quote !== null) {
|
|
3315
|
+
return { unsafe: true, reason: "unmatched-quote" };
|
|
3316
|
+
}
|
|
3317
|
+
flush();
|
|
3318
|
+
const pipeTokens = tokens.filter((token) => token.value.includes("|"));
|
|
3319
|
+
if (pipeTokens.length === 0)
|
|
3320
|
+
return { unsafe: false, reason: null };
|
|
3321
|
+
if (pipeTokens.length !== 1 || !pipeTokens[0].quoted || !pipeTokens[0].pipeIsDoubleQuoted) {
|
|
3322
|
+
return { unsafe: true, reason: "gh-api-jq-pipe" };
|
|
3323
|
+
}
|
|
3324
|
+
const quotedPipeToken = pipeTokens[0];
|
|
3325
|
+
if (!/^gh\s+api(?:\s|$)/i.test(compact)) {
|
|
3326
|
+
return { unsafe: true, reason: "gh-api-jq-pipe" };
|
|
3327
|
+
}
|
|
3328
|
+
for (let index = 0;index < tokens.length; index += 1) {
|
|
3329
|
+
const token = tokens[index];
|
|
3330
|
+
if (token.value === "--jq" && tokens[index + 1] === quotedPipeToken) {
|
|
3331
|
+
return { unsafe: false, reason: null };
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3334
|
+
return { unsafe: true, reason: "gh-api-jq-pipe" };
|
|
3335
|
+
}
|
|
3075
3336
|
function hasUnsafeShellControlSyntax(command) {
|
|
3076
|
-
return
|
|
3337
|
+
return classifyPrWorkflowShellSyntax(command).unsafe;
|
|
3077
3338
|
}
|
|
3078
3339
|
function isSafeStandaloneGitCommit(command) {
|
|
3079
3340
|
if (hasUnsafeShellControlSyntax(command))
|
|
@@ -3850,7 +4111,8 @@ function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkf
|
|
|
3850
4111
|
const issues = [];
|
|
3851
4112
|
const normalized = normalizeCandidateArtifact(text, fallbackFamily);
|
|
3852
4113
|
const canonicalText = normalized.text;
|
|
3853
|
-
const
|
|
4114
|
+
const repairKinds = normalized.repairKinds;
|
|
4115
|
+
const salvaged = repairKinds.length > 0;
|
|
3854
4116
|
const header = selectCandidateHeader(canonicalText.split(/\r?\n/));
|
|
3855
4117
|
if (header === null || !header.markerBearing || header.family !== fallbackFamily) {
|
|
3856
4118
|
appendBoundedCandidateIssue(issues, `field header: expected one exact canonical ${fallbackFamily} [CANDIDATE] header before discovery rows`);
|
|
@@ -3859,6 +4121,7 @@ function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkf
|
|
|
3859
4121
|
evidence: null,
|
|
3860
4122
|
issues,
|
|
3861
4123
|
salvaged,
|
|
4124
|
+
repairKinds,
|
|
3862
4125
|
failurePredicate: "discovery.header"
|
|
3863
4126
|
};
|
|
3864
4127
|
}
|
|
@@ -3902,6 +4165,7 @@ function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkf
|
|
|
3902
4165
|
evidence: null,
|
|
3903
4166
|
issues,
|
|
3904
4167
|
salvaged,
|
|
4168
|
+
repairKinds,
|
|
3905
4169
|
failurePredicate: "discovery.row"
|
|
3906
4170
|
};
|
|
3907
4171
|
}
|
|
@@ -3919,7 +4183,8 @@ function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkf
|
|
|
3919
4183
|
matchingCandidate.confidence
|
|
3920
4184
|
].join("\x00"),
|
|
3921
4185
|
issues,
|
|
3922
|
-
salvaged
|
|
4186
|
+
salvaged,
|
|
4187
|
+
repairKinds
|
|
3923
4188
|
};
|
|
3924
4189
|
}
|
|
3925
4190
|
const clean = parsed.clean_attestation;
|
|
@@ -3928,7 +4193,8 @@ function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkf
|
|
|
3928
4193
|
covered: true,
|
|
3929
4194
|
evidence: `${clean.coverage_scope}\x00${clean.evidence}`,
|
|
3930
4195
|
issues,
|
|
3931
|
-
salvaged
|
|
4196
|
+
salvaged,
|
|
4197
|
+
repairKinds
|
|
3932
4198
|
};
|
|
3933
4199
|
}
|
|
3934
4200
|
return {
|
|
@@ -3936,6 +4202,7 @@ function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkf
|
|
|
3936
4202
|
evidence: null,
|
|
3937
4203
|
issues,
|
|
3938
4204
|
salvaged,
|
|
4205
|
+
repairKinds,
|
|
3939
4206
|
failurePredicate: hasParseFailure ? "discovery.row" : "discovery.coverage"
|
|
3940
4207
|
};
|
|
3941
4208
|
}
|
|
@@ -3983,7 +4250,7 @@ function validatePrReviewDiscoveryLaneCompletion(input) {
|
|
|
3983
4250
|
if (!analysis.salvaged && analysis.issues.length === 0)
|
|
3984
4251
|
continue;
|
|
3985
4252
|
salvagedLanes.push(workflowLane);
|
|
3986
|
-
const reason = analysis.
|
|
4253
|
+
const reason = analysis.repairKinds.length > 0 ? `structural repairs applied: ${analysis.repairKinds.join(", ")}` : "one or more rows were dropped as malformed or out-of-ownership";
|
|
3987
4254
|
warn(`[pr-workflow-gate] discovery artifact salvaged: batch=${input.record.batchId ?? "(missing)"} lane=${input.record.laneId ?? "(missing)"} workflow_lane=${workflowLane} \u2014 ${reason}; retained coverage from the valid rows. Dropped-row diagnostics: ${analysis.issues.join("; ") || "(none)"}`);
|
|
3988
4255
|
}
|
|
3989
4256
|
const failedCoverage = analyses.find(({ analysis }) => !analysis.covered);
|
|
@@ -4131,13 +4398,6 @@ function pipeFields(line) {
|
|
|
4131
4398
|
return [];
|
|
4132
4399
|
return line.split("|").map((field) => field.trim());
|
|
4133
4400
|
}
|
|
4134
|
-
async function persistState(directory, state) {
|
|
4135
|
-
const validated = PrWorkflowGateStateSchema.parse(state);
|
|
4136
|
-
await withSessionStateMutation(directory, validated.sessionID, async () => {
|
|
4137
|
-
const nextState = await writeStateWhileLocked(directory, validated);
|
|
4138
|
-
Object.assign(state, nextState);
|
|
4139
|
-
});
|
|
4140
|
-
}
|
|
4141
4401
|
async function writeStateWhileLocked(directory, state, options = {}) {
|
|
4142
4402
|
const validated = PrWorkflowGateStateSchema.parse(state);
|
|
4143
4403
|
const current = await readPrWorkflowGateStateFromDisk(directory, validated.sessionID);
|
|
@@ -4537,6 +4797,9 @@ function isProcessAlive(pid) {
|
|
|
4537
4797
|
}
|
|
4538
4798
|
async function readPrWorkflowGateStateFromDisk(directory, sessionID) {
|
|
4539
4799
|
const filePath = workflowGateStatePath(directory, sessionID);
|
|
4800
|
+
return readPrWorkflowGateStateFileFromDisk(filePath, sessionID);
|
|
4801
|
+
}
|
|
4802
|
+
async function readPrWorkflowGateStateFileFromDisk(filePath, stateLabel) {
|
|
4540
4803
|
let raw;
|
|
4541
4804
|
try {
|
|
4542
4805
|
raw = await fsp2.readFile(filePath, "utf-8");
|
|
@@ -4549,11 +4812,11 @@ async function readPrWorkflowGateStateFromDisk(directory, sessionID) {
|
|
|
4549
4812
|
try {
|
|
4550
4813
|
parsedJson = JSON.parse(raw);
|
|
4551
4814
|
} catch {
|
|
4552
|
-
throw new Error(`BLOCKED: PR workflow gate state for session "${
|
|
4815
|
+
throw new Error(`BLOCKED: PR workflow gate state for session "${stateLabel}" is not valid JSON`);
|
|
4553
4816
|
}
|
|
4554
4817
|
const parsed = PrWorkflowGateStateSchema.safeParse(parsedJson);
|
|
4555
4818
|
if (!parsed.success) {
|
|
4556
|
-
throw new Error(`BLOCKED: PR workflow gate state for session "${
|
|
4819
|
+
throw new Error(`BLOCKED: PR workflow gate state for session "${stateLabel}" is invalid`);
|
|
4557
4820
|
}
|
|
4558
4821
|
return parsed.data;
|
|
4559
4822
|
}
|
|
@@ -4724,9 +4987,9 @@ var USAGE = [
|
|
|
4724
4987
|
"Usage: /swarm abort-pr-workflow [PR_REVIEW|PR_FEEDBACK] <reason...>",
|
|
4725
4988
|
"",
|
|
4726
4989
|
"Clear an active PR_REVIEW or PR_FEEDBACK mechanical gate for the current session",
|
|
4727
|
-
"and stop the auto-resume loop. This is the human-only FORCE escape hatch
|
|
4728
|
-
"
|
|
4729
|
-
"
|
|
4990
|
+
"and stop the auto-resume loop. This is the human-only FORCE escape hatch, usable",
|
|
4991
|
+
"even when the architect cannot call its audited recovery abort. Both paths may clear",
|
|
4992
|
+
"a BOUND gate after lanes settle; the force path does not require agent cooperation. Use when",
|
|
4730
4993
|
"a PR review or feedback workflow is unrecoverably stuck (e.g. the working tree",
|
|
4731
4994
|
"cannot reach the PR head, a compound shell command was rejected, or the wake",
|
|
4732
4995
|
"budget is suspended).",
|
|
@@ -4766,7 +5029,7 @@ ${USAGE}`;
|
|
|
4766
5029
|
...knownMode ? { expectedMode: knownMode } : {}
|
|
4767
5030
|
});
|
|
4768
5031
|
const headLine = summary.prHeadSha ? ` (was bound to PR head ${summary.prHeadSha})` : " (was not bound to a PR head)";
|
|
4769
|
-
return `Aborted active ${summary.mode} mechanical gate for session ${sessionID}${headLine} (force). The durable gate state has been cleared and the auto-resume loop will stop. An audit event was appended to .swarm/events.jsonl.`;
|
|
5032
|
+
return `Aborted active ${summary.mode} mechanical gate for session ${sessionID}${headLine} (force). The durable gate state has been cleared and the auto-resume loop will stop. An audit event was appended to .swarm/events.jsonl. If checkout preparation preserved changes, continue with prepare_pr_workflow_checkout operation=restore (or follow the preserved receipt manually).`;
|
|
4770
5033
|
} catch (error2) {
|
|
4771
5034
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
4772
5035
|
return `Error: ${message}
|
|
@@ -14569,8 +14832,8 @@ function dcValidateTargets(targets, cwd) {
|
|
|
14569
14832
|
const lstatBlock = dcLstatAncestorWalk(filesystemTarget, cwd);
|
|
14570
14833
|
if (lstatBlock)
|
|
14571
14834
|
return lstatBlock;
|
|
14572
|
-
const
|
|
14573
|
-
if (dcPathComponents(t).length === 1 && DC_SAFE_TARGETS.has(
|
|
14835
|
+
const basename6 = path23.basename(filesystemTarget);
|
|
14836
|
+
if (dcPathComponents(t).length === 1 && DC_SAFE_TARGETS.has(basename6)) {
|
|
14574
14837
|
continue;
|
|
14575
14838
|
}
|
|
14576
14839
|
if (DC_FS_ROOTS.has(t) || DC_FS_ROOTS.has(t.replace(/\//g, "\\"))) {
|
|
@@ -14656,8 +14919,8 @@ function dcEvaluateRecursiveDeleteTargets(input) {
|
|
|
14656
14919
|
reason: `BLOCKED: Recursive delete target "${safeTarget}" is not strictly contained by the workspace.`
|
|
14657
14920
|
};
|
|
14658
14921
|
}
|
|
14659
|
-
const
|
|
14660
|
-
if (!DC_SAFE_TARGETS.has(
|
|
14922
|
+
const basename6 = path23.basename(resolved);
|
|
14923
|
+
if (!DC_SAFE_TARGETS.has(basename6)) {
|
|
14661
14924
|
return {
|
|
14662
14925
|
allowed: false,
|
|
14663
14926
|
code: "DESTRUCTIVE_TARGET_NOT_SAFE_ARTIFACT",
|
|
@@ -22007,7 +22270,7 @@ function planMerges(active, threshold, maxComparisons, maxMerges) {
|
|
|
22007
22270
|
};
|
|
22008
22271
|
}
|
|
22009
22272
|
async function sweepActiveNearDuplicates(directory, options = {}) {
|
|
22010
|
-
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-
|
|
22273
|
+
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-vjqe1z2w.js");
|
|
22011
22274
|
const { config } = loadPluginConfigWithMeta2(directory);
|
|
22012
22275
|
const sweepConfig = config.learning?.dedup_sweep;
|
|
22013
22276
|
if (sweepConfig?.enabled === false)
|
|
@@ -23091,7 +23354,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
|
|
|
23091
23354
|
}
|
|
23092
23355
|
if (config.skill_generation_enabled === true && skillCandidates.length > 0) {
|
|
23093
23356
|
try {
|
|
23094
|
-
const skillModule = await import("./skill-generator-
|
|
23357
|
+
const skillModule = await import("./skill-generator-bvph9d98.js");
|
|
23095
23358
|
for (const cand of skillCandidates) {
|
|
23096
23359
|
if (cand.confidence < (config.min_skill_confidence ?? DEFAULT_SKILL_MIN_CONFIDENCE)) {
|
|
23097
23360
|
continue;
|
|
@@ -23551,7 +23814,7 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
|
|
|
23551
23814
|
} catch {}
|
|
23552
23815
|
}
|
|
23553
23816
|
if (contradictedEntries.length > 0 && knowledgeConfig.contradiction_threshold_action === "quarantine") {
|
|
23554
|
-
const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-
|
|
23817
|
+
const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-58tw74js.js");
|
|
23555
23818
|
for (const { id } of contradictedEntries) {
|
|
23556
23819
|
try {
|
|
23557
23820
|
await maybeQuarantineOnContradiction(directory, id, knowledgeConfig.contradiction_quarantine_threshold, knowledgeConfig.contradiction_quarantine_window_days);
|
|
@@ -24370,7 +24633,7 @@ async function runCuratorPostMortem(directory, options = {}) {
|
|
|
24370
24633
|
warnings.push("Failed to collect knowledge summary.");
|
|
24371
24634
|
}
|
|
24372
24635
|
try {
|
|
24373
|
-
const { getScanStatus } = await import("./scan-cursor-
|
|
24636
|
+
const { getScanStatus } = await import("./scan-cursor-7tqsvbp8.js");
|
|
24374
24637
|
const scanStatus = await getScanStatus(directory);
|
|
24375
24638
|
if (scanStatus.remaining_estimate > 0) {
|
|
24376
24639
|
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.`);
|
|
@@ -24542,9 +24805,9 @@ var _internals28 = {
|
|
|
24542
24805
|
verifyPostMortemKnowledgeActions,
|
|
24543
24806
|
repairPostMortemActions,
|
|
24544
24807
|
loadDefaultKnowledgeConfig: async (directory) => {
|
|
24545
|
-
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-
|
|
24808
|
+
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-b046zss2.js");
|
|
24546
24809
|
try {
|
|
24547
|
-
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-
|
|
24810
|
+
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-vjqe1z2w.js");
|
|
24548
24811
|
const loaded = loadPluginConfigWithMeta2(directory);
|
|
24549
24812
|
return KnowledgeConfigSchema2.parse(loaded.config.knowledge ?? {});
|
|
24550
24813
|
} catch {
|
|
@@ -24552,11 +24815,11 @@ var _internals28 = {
|
|
|
24552
24815
|
}
|
|
24553
24816
|
},
|
|
24554
24817
|
applyCuratorKnowledgeUpdates: async (directory, recommendations, knowledgeConfig, generation) => {
|
|
24555
|
-
const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-
|
|
24818
|
+
const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-zp5qq24c.js");
|
|
24556
24819
|
return applyCuratorKnowledgeUpdates2(directory, recommendations, knowledgeConfig, generation);
|
|
24557
24820
|
},
|
|
24558
24821
|
checkHivePromotions: async (entries, knowledgeConfig, directory) => {
|
|
24559
|
-
const { checkHivePromotions } = await import("./hive-promoter-
|
|
24822
|
+
const { checkHivePromotions } = await import("./hive-promoter-0yg3gxg6.js");
|
|
24560
24823
|
return checkHivePromotions(entries, knowledgeConfig, directory);
|
|
24561
24824
|
},
|
|
24562
24825
|
applyProposalTriage: async (directory, triage) => {
|
|
@@ -24565,7 +24828,7 @@ var _internals28 = {
|
|
|
24565
24828
|
activateProposal,
|
|
24566
24829
|
listSkills: listSkills2,
|
|
24567
24830
|
sanitizeSlug
|
|
24568
|
-
} = await import("./skill-generator-
|
|
24831
|
+
} = await import("./skill-generator-bvph9d98.js");
|
|
24569
24832
|
const result = {
|
|
24570
24833
|
approved: [],
|
|
24571
24834
|
rejected: [],
|
|
@@ -25790,9 +26053,9 @@ function hashContent(content) {
|
|
|
25790
26053
|
return createHash9("sha1").update(content).digest("hex");
|
|
25791
26054
|
}
|
|
25792
26055
|
async function loadConfigForPolicyCurator(directory) {
|
|
25793
|
-
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-
|
|
26056
|
+
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-b046zss2.js");
|
|
25794
26057
|
try {
|
|
25795
|
-
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-
|
|
26058
|
+
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-vjqe1z2w.js");
|
|
25796
26059
|
const { config: loadedConfig } = loadPluginConfigWithMeta2(directory);
|
|
25797
26060
|
return KnowledgeConfigSchema2.parse(loadedConfig.knowledge ?? {});
|
|
25798
26061
|
} catch {
|
|
@@ -29801,7 +30064,7 @@ async function runFinalizeStage(ctx) {
|
|
|
29801
30064
|
}
|
|
29802
30065
|
}
|
|
29803
30066
|
try {
|
|
29804
|
-
const { CuratorConfigSchema: CCS } = await import("./schema-
|
|
30067
|
+
const { CuratorConfigSchema: CCS } = await import("./schema-b046zss2.js");
|
|
29805
30068
|
const { config: pmLoadedConfig } = _internals39.loadPluginConfigWithMeta(ctx.directory);
|
|
29806
30069
|
const curatorCfg = CCS.parse(pmLoadedConfig.curator ?? {});
|
|
29807
30070
|
if (curatorCfg.enabled && curatorCfg.postmortem_enabled) {
|
|
@@ -32074,9 +32337,9 @@ var _internals44 = {
|
|
|
32074
32337
|
loadPluginConfigWithMeta,
|
|
32075
32338
|
loadCuratorDeps: async () => {
|
|
32076
32339
|
const [{ CuratorConfigSchema }, curator, { createCuratorLLMDelegate: createCuratorLLMDelegate2 }] = await Promise.all([
|
|
32077
|
-
import("./schema-
|
|
32078
|
-
import("./curator-
|
|
32079
|
-
import("./curator-llm-factory-
|
|
32340
|
+
import("./schema-b046zss2.js"),
|
|
32341
|
+
import("./curator-zp5qq24c.js"),
|
|
32342
|
+
import("./curator-llm-factory-pr4bdfqw.js")
|
|
32080
32343
|
]);
|
|
32081
32344
|
return { CuratorConfigSchema, curator, createCuratorLLMDelegate: createCuratorLLMDelegate2 };
|
|
32082
32345
|
}
|
|
@@ -32981,7 +33244,7 @@ async function computeCurationDiagnostics(directory, entries) {
|
|
|
32981
33244
|
let scanCompleted = false;
|
|
32982
33245
|
let scanRemaining = 0;
|
|
32983
33246
|
try {
|
|
32984
|
-
const { getScanStatus } = await import("./scan-cursor-
|
|
33247
|
+
const { getScanStatus } = await import("./scan-cursor-7tqsvbp8.js");
|
|
32985
33248
|
const status = await getScanStatus(directory);
|
|
32986
33249
|
scanGeneration = status.generation;
|
|
32987
33250
|
scanCompleted = status.completed;
|
|
@@ -32991,17 +33254,17 @@ async function computeCurationDiagnostics(directory, entries) {
|
|
|
32991
33254
|
try {
|
|
32992
33255
|
const { isLinked: isLinked2 } = await import("./knowledge-link-q46k9adx.js");
|
|
32993
33256
|
if (isLinked2(directory)) {
|
|
32994
|
-
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-
|
|
33257
|
+
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-b046zss2.js");
|
|
32995
33258
|
let config;
|
|
32996
33259
|
try {
|
|
32997
|
-
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-
|
|
33260
|
+
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-vjqe1z2w.js");
|
|
32998
33261
|
const loaded = loadPluginConfigWithMeta2(directory);
|
|
32999
33262
|
config = KnowledgeConfigSchema2.parse(loaded.config.knowledge ?? {});
|
|
33000
33263
|
} catch {
|
|
33001
33264
|
config = KnowledgeConfigSchema2.parse({});
|
|
33002
33265
|
}
|
|
33003
33266
|
const { cohortConfigFingerprint: cohortConfigFingerprint2 } = await import("./config-fingerprint-v22xh5a2.js");
|
|
33004
|
-
const { buildConfigFingerprintInput: buildConfigFingerprintInput2, readCohortConfigFingerprint } = await import("./curation-policy-
|
|
33267
|
+
const { buildConfigFingerprintInput: buildConfigFingerprintInput2, readCohortConfigFingerprint } = await import("./curation-policy-7n6t8he4.js");
|
|
33005
33268
|
const currentFp = cohortConfigFingerprint2(buildConfigFingerprintInput2(config));
|
|
33006
33269
|
const cohortFp = await readCohortConfigFingerprint(directory);
|
|
33007
33270
|
configMatch = cohortFp === null ? null : currentFp === cohortFp;
|
|
@@ -34212,7 +34475,7 @@ async function handleDoctorCommand(directory, args) {
|
|
|
34212
34475
|
const result = runConfigDoctor(config, directory);
|
|
34213
34476
|
let output;
|
|
34214
34477
|
if (enableAutoFix && result.hasAutoFixableIssues) {
|
|
34215
|
-
const { runConfigDoctorWithFixes } = await import("./config-doctor-
|
|
34478
|
+
const { runConfigDoctorWithFixes } = await import("./config-doctor-shc0n4q1.js");
|
|
34216
34479
|
const fixResult = await runConfigDoctorWithFixes(directory, config, true, {
|
|
34217
34480
|
applyLossy: true
|
|
34218
34481
|
});
|
|
@@ -36366,7 +36629,7 @@ function countCodeLines(content) {
|
|
|
36366
36629
|
return lines.length;
|
|
36367
36630
|
}
|
|
36368
36631
|
function isTestFile(filePath) {
|
|
36369
|
-
const
|
|
36632
|
+
const basename12 = path61.basename(filePath);
|
|
36370
36633
|
const _ext = path61.extname(filePath).toLowerCase();
|
|
36371
36634
|
const testPatterns = [
|
|
36372
36635
|
".test.",
|
|
@@ -36382,7 +36645,7 @@ function isTestFile(filePath) {
|
|
|
36382
36645
|
".spec.jsx"
|
|
36383
36646
|
];
|
|
36384
36647
|
for (const pattern of testPatterns) {
|
|
36385
|
-
if (
|
|
36648
|
+
if (basename12.includes(pattern)) {
|
|
36386
36649
|
return true;
|
|
36387
36650
|
}
|
|
36388
36651
|
}
|
|
@@ -45860,8 +46123,8 @@ function toSafeRelativePath(rootDir, targetPath) {
|
|
|
45860
46123
|
if (!relative11 || relative11 === ".")
|
|
45861
46124
|
return ".";
|
|
45862
46125
|
if (relative11.startsWith("..") || path78.isAbsolute(relative11)) {
|
|
45863
|
-
const
|
|
45864
|
-
return
|
|
46126
|
+
const basename17 = path78.basename(targetPath).replace(/\\/g, "/");
|
|
46127
|
+
return basename17 || ".";
|
|
45865
46128
|
}
|
|
45866
46129
|
return relative11;
|
|
45867
46130
|
}
|
|
@@ -47717,21 +47980,21 @@ function hasCompoundTestExtension(filename) {
|
|
|
47717
47980
|
const lower = filename.toLowerCase();
|
|
47718
47981
|
return COMPOUND_TEST_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
47719
47982
|
}
|
|
47720
|
-
function isLanguageSpecificTestFile(
|
|
47721
|
-
const lower =
|
|
47983
|
+
function isLanguageSpecificTestFile(basename18) {
|
|
47984
|
+
const lower = basename18.toLowerCase();
|
|
47722
47985
|
if (lower.endsWith("_test.go"))
|
|
47723
47986
|
return true;
|
|
47724
47987
|
if (lower.endsWith(".py") && (lower.startsWith("test_") || lower.endsWith("_test.py")))
|
|
47725
47988
|
return true;
|
|
47726
47989
|
if (lower.endsWith("_spec.rb"))
|
|
47727
47990
|
return true;
|
|
47728
|
-
if (lower.endsWith(".java") && (/^Test[A-Z]/.test(
|
|
47991
|
+
if (lower.endsWith(".java") && (/^Test[A-Z]/.test(basename18) || basename18.endsWith("Test.java") || basename18.endsWith("Tests.java") || lower.endsWith("it.java")))
|
|
47729
47992
|
return true;
|
|
47730
47993
|
if (lower.endsWith(".cs") && (lower.endsWith("test.cs") || lower.endsWith("tests.cs")))
|
|
47731
47994
|
return true;
|
|
47732
|
-
if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(
|
|
47995
|
+
if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(basename18) || lower.endsWith("test.kt") || lower.endsWith("tests.kt")))
|
|
47733
47996
|
return true;
|
|
47734
|
-
if (lower.endsWith(".php") && (/^Test[A-Z]/.test(
|
|
47997
|
+
if (lower.endsWith(".php") && (/^Test[A-Z]/.test(basename18) || basename18.endsWith("Test.php") || basename18.endsWith("Tests.php")))
|
|
47735
47998
|
return true;
|
|
47736
47999
|
if (lower.endsWith(".tests.ps1"))
|
|
47737
48000
|
return true;
|
|
@@ -47739,23 +48002,23 @@ function isLanguageSpecificTestFile(basename17) {
|
|
|
47739
48002
|
}
|
|
47740
48003
|
function isConventionTestFilePath(filePath) {
|
|
47741
48004
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
47742
|
-
const
|
|
47743
|
-
return hasCompoundTestExtension(
|
|
48005
|
+
const basename18 = path82.basename(filePath);
|
|
48006
|
+
return hasCompoundTestExtension(basename18) || basename18.includes(".spec.") || basename18.includes(".test.") || isLanguageSpecificTestFile(basename18) || isTestDirectoryPath(normalizedPath);
|
|
47744
48007
|
}
|
|
47745
48008
|
function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
47746
48009
|
const testFiles = [];
|
|
47747
48010
|
for (const file of sourceFiles) {
|
|
47748
48011
|
const absoluteFile = resolveWorkspacePath(file, workingDir);
|
|
47749
48012
|
const relativeFile = path82.relative(workingDir, absoluteFile);
|
|
47750
|
-
const
|
|
48013
|
+
const basename18 = path82.basename(absoluteFile);
|
|
47751
48014
|
const dirname30 = path82.dirname(absoluteFile);
|
|
47752
48015
|
const preferRelativeOutput = !path82.isAbsolute(file);
|
|
47753
48016
|
if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file)) {
|
|
47754
48017
|
dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
|
|
47755
48018
|
continue;
|
|
47756
48019
|
}
|
|
47757
|
-
const nameWithoutExt =
|
|
47758
|
-
const ext = path82.extname(
|
|
48020
|
+
const nameWithoutExt = basename18.replace(/\.[^.]+$/, "");
|
|
48021
|
+
const ext = path82.extname(basename18);
|
|
47759
48022
|
const genericTestNames = [
|
|
47760
48023
|
`${nameWithoutExt}.spec${ext}`,
|
|
47761
48024
|
`${nameWithoutExt}.test${ext}`
|
|
@@ -47766,7 +48029,7 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
|
47766
48029
|
...languageSpecificTestNames
|
|
47767
48030
|
].map((candidateName) => path82.join(dirname30, candidateName));
|
|
47768
48031
|
const testDirectoryNames = [
|
|
47769
|
-
|
|
48032
|
+
basename18,
|
|
47770
48033
|
...genericTestNames,
|
|
47771
48034
|
...languageSpecificTestNames
|
|
47772
48035
|
];
|
|
@@ -57551,7 +57814,7 @@ function buildDetailedHelp(commandName, entry) {
|
|
|
57551
57814
|
async function handleHelpCommand(ctx) {
|
|
57552
57815
|
const targetCommand = ctx.args.join(" ");
|
|
57553
57816
|
if (!targetCommand) {
|
|
57554
|
-
const { buildHelpText } = await import("./index-
|
|
57817
|
+
const { buildHelpText } = await import("./index-4rje8dc0.js");
|
|
57555
57818
|
return buildHelpText();
|
|
57556
57819
|
}
|
|
57557
57820
|
const tokens = targetCommand.split(/\s+/);
|
|
@@ -57560,7 +57823,7 @@ async function handleHelpCommand(ctx) {
|
|
|
57560
57823
|
return _internals18.buildDetailedHelp(resolved.key, resolved.entry);
|
|
57561
57824
|
}
|
|
57562
57825
|
const similar = _internals18.findSimilarCommands(targetCommand);
|
|
57563
|
-
const { buildHelpText: fullHelp } = await import("./index-
|
|
57826
|
+
const { buildHelpText: fullHelp } = await import("./index-4rje8dc0.js");
|
|
57564
57827
|
if (similar.length > 0) {
|
|
57565
57828
|
return `Command '/swarm ${targetCommand}' not found.
|
|
57566
57829
|
|
|
@@ -57737,7 +58000,7 @@ var COMMAND_REGISTRY = {
|
|
|
57737
58000
|
},
|
|
57738
58001
|
"guardrail explain": {
|
|
57739
58002
|
handler: async (ctx) => {
|
|
57740
|
-
const { handleGuardrailExplain } = await import("./guardrail-explain-
|
|
58003
|
+
const { handleGuardrailExplain } = await import("./guardrail-explain-thddj6jv.js");
|
|
57741
58004
|
return handleGuardrailExplain(ctx.directory, ctx.args);
|
|
57742
58005
|
},
|
|
57743
58006
|
description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
|
|
@@ -57747,7 +58010,7 @@ var COMMAND_REGISTRY = {
|
|
|
57747
58010
|
},
|
|
57748
58011
|
"guardrail-explain": {
|
|
57749
58012
|
handler: async (ctx) => {
|
|
57750
|
-
const { handleGuardrailExplain } = await import("./guardrail-explain-
|
|
58013
|
+
const { handleGuardrailExplain } = await import("./guardrail-explain-thddj6jv.js");
|
|
57751
58014
|
return handleGuardrailExplain(ctx.directory, ctx.args);
|
|
57752
58015
|
},
|
|
57753
58016
|
description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
|
|
@@ -57757,7 +58020,7 @@ var COMMAND_REGISTRY = {
|
|
|
57757
58020
|
},
|
|
57758
58021
|
"guardrail-log": {
|
|
57759
58022
|
handler: async (ctx) => {
|
|
57760
|
-
const { handleGuardrailLog } = await import("./guardrail-log-
|
|
58023
|
+
const { handleGuardrailLog } = await import("./guardrail-log-w8fmxe1x.js");
|
|
57761
58024
|
return handleGuardrailLog(ctx.directory, ctx.args);
|
|
57762
58025
|
},
|
|
57763
58026
|
description: "Read the guardrail decision log (use --blocks-only for blocks)",
|
|
@@ -58203,7 +58466,7 @@ Subcommands:
|
|
|
58203
58466
|
handler: (ctx) => handleAbortPrWorkflowCommand(ctx.directory, ctx.args, ctx.sessionID),
|
|
58204
58467
|
description: "Clear a stuck PR_REVIEW/PR_FEEDBACK mechanical gate and stop the auto-resume loop [mode] [reason]",
|
|
58205
58468
|
args: "[PR_REVIEW|PR_FEEDBACK] [reason...]",
|
|
58206
|
-
details: "Human-only escape hatch for an unrecoverable PR_REVIEW or PR_FEEDBACK mechanical gate. When the architect cannot reach complete_pr_workflow \u2014 for example a compound `git fetch && git checkout` was rejected as read-only shell syntax, the PR head cannot be fetched, or the working tree is on the wrong branch \u2014 running this clears the durable gate state for the current session and stops the auto-resume loop without depending on the trapped model. The agent itself cannot run this command; it must call the abort_pr_workflow tool (or ask you to run this command). Both paths funnel into the same fail-closed abortPrWorkflow hook, which refuses while the workflow is armed for publication or while PR workflow lanes are still in flight. An audit event is appended to .swarm/events.jsonl.",
|
|
58469
|
+
details: "Human-only escape hatch for an unrecoverable PR_REVIEW or PR_FEEDBACK mechanical gate. When the architect cannot reach complete_pr_workflow \u2014 for example a compound `git fetch && git checkout` was rejected as read-only shell syntax, the PR head cannot be fetched, or the working tree is on the wrong branch \u2014 running this clears the durable gate state for the current session and stops the auto-resume loop without depending on the trapped model. The agent itself cannot run this command; it must call the abort_pr_workflow tool (or ask you to run this command). Both paths funnel into the same fail-closed abortPrWorkflow hook, which refuses while the workflow is armed for publication or while PR workflow lanes are still in flight. When checkout preparation preserved a stash, the result instructs the caller to run prepare_pr_workflow_checkout operation=restore after the clear. An audit event is appended to .swarm/events.jsonl.",
|
|
58207
58470
|
category: "utility",
|
|
58208
58471
|
toolPolicy: "restricted"
|
|
58209
58472
|
},
|
|
@@ -59646,15 +59909,15 @@ HARD CONSTRAINTS (apply regardless of skill load success):
|
|
|
59646
59909
|
- Test execution, explorer lanes, reviewer dispatch, and critic challenge are all permitted within this mode
|
|
59647
59910
|
- Quality is the only metric \u2014 there is no speed, efficiency, or time exception; time, tokens, and agent dispatches are irrelevant to correctness
|
|
59648
59911
|
- FOLLOW THE SKILL EXACTLY: execute every phase of the loaded SKILL.md in order with no shortcuts, no phase-skipping, and no premature synthesis. If a required coverage phase cannot complete, apply the skill's coverage gate (retry or verified equivalent alternative). If the gap still cannot be closed, stop and surface the lane failure to the user as BLOCKED; do not produce a degraded review, partial verdict, or final synthesis.
|
|
59649
|
-
- CHECK OUT THE EXACT PR HEAD LOCALLY before dispatching explorer lanes: resolve the authoritative full PR head SHA, verify the working tree is clean (git status --porcelain), and if it is dirty call \`prepare_pr_workflow_checkout\` before checkout \u2014 either with no \`paths\` to auto-discover and preserve every dirty and untracked change in one auditable stash, or with an explicit exact dirty tracked path set. It returns the stash OID and recovery command; an already-clean tree is a no-op. Do NOT run \`git stash\` through shell. Then use standalone commands: fetch the PR head, verify \`git cat-file -
|
|
59912
|
+
- CHECK OUT THE EXACT PR HEAD LOCALLY before dispatching explorer lanes: resolve the authoritative full PR head SHA, verify the working tree is clean (git status --porcelain), and if it is dirty call \`prepare_pr_workflow_checkout\` before checkout \u2014 either with no \`paths\` to auto-discover and preserve every dirty and untracked change in one auditable stash, or with an explicit exact dirty tracked path set. It returns the stash OID and recovery command; an already-clean tree is a no-op. Do NOT run \`git stash\` through shell. Then use standalone commands: fetch the PR head, verify it portably with \`git rev-parse --verify <full_pr_head_sha>^0\` followed by \`git cat-file -t <full_pr_head_sha>\` (which must print \`commit\`), run \`git switch --detach <full_pr_head_sha>\`, confirm HEAD equals that SHA, and bind it through the PR-review controller. Do not use \`--track FETCH_HEAD\`. Explorers read the working-tree filesystem (Read/Glob/Grep), so without this checkout they read the base branch and produce invalid candidates. Always pass the base..head commit range in explorer delegations. Under Profile A, do not create a scratch context-pack file after the gate activates: put bounded shared scope, obligations, and deterministic signals in \`common_prompt\`; every lane must inspect the exact bound diff itself.
|
|
59650
59913
|
- Treat the controller Git-state result as final for the attempt: \`clean\` proceeds; \`stashable\` permits one preparation call; \`recovery-required\` or \`indeterminate\` means report the typed \`required_action\`, abort/clear any already-active gate, and STOP unless \`retryable: true\`. Never loop on stash against an unmerged index or in-progress Git operation.
|
|
59651
59914
|
- 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.
|
|
59652
|
-
- 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.
|
|
59915
|
+
- 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. After the second failed retry, collect every lane to settlement, do not probe downstream writers or micro lanes, call \`abort_pr_workflow\` with \`mode: "PR_REVIEW"\`, \`kind: "recovery"\`, and a non-empty one-line \`reason\` naming the failed lane and exhausted retries, then call \`prepare_pr_workflow_checkout\` with \`operation: "restore"\` before reporting the blocker.
|
|
59653
59916
|
- 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.
|
|
59654
59917
|
- 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. The first micro dispatch MUST pass the complete exact eleven-row \`trigger_evaluation\`, which freezes it for this session; any subsequent same-session micro batch may omit it and reuse the frozen ledger, while an explicitly supplied copy must remain exactly identical. 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.
|
|
59655
59918
|
- 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.
|
|
59656
|
-
- 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.
|
|
59657
|
-
- 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),
|
|
59919
|
+
- 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. When completion reports checkout restoration is required, call \`prepare_pr_workflow_checkout\` with \`operation: "restore"\` before returning to the user.
|
|
59920
|
+
- 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), the merge-base bind is unreachable, or the bounded lane retries above are exhausted, call \`abort_pr_workflow\` with \`mode: "PR_REVIEW"\`, \`kind: "recovery"\`, and a one-line \`reason\` instead of looping. The tool accepts unbound and bound workflows but 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 force-clear via \`/swarm abort-pr-workflow\`. Do NOT abort while useful bounded recovery work remains.
|
|
59658
59921
|
- Honor any free-text instructions that follow the closing bracket of the signal as additional reviewer focus, without weakening the validation ladder above.
|
|
59659
59922
|
|
|
59660
59923
|
### MODE: PR_FEEDBACK
|
|
@@ -59682,7 +59945,7 @@ HARD CONSTRAINTS (apply regardless of skill load success):
|
|
|
59682
59945
|
- DO NOT PUBLISH EARLY: commits, pushes, PR comments/updates, review-thread mutations, and other remote writes remain blocked until every ordered feedback gate settles positively on the same revision digest.
|
|
59683
59946
|
- After every local gate passes, create the reviewed commit with one standalone \`git commit\`, then call \`complete_pr_workflow\` once. It requires a clean index/worktree and a non-merge direct child whose sole parent is the immutable intake head before binding that post-commit HEAD and arming publication. Push only with \`git push <bound-remote> <bound-commit>:refs/heads/<bound-branch>\`; the literal bound commit and exact bound branch are required, and force flags, wrappers, aliases, fetch-based local-ref forgery, extra refspecs, \`git -C\`, and other remote writes fail closed. Perform read-only remote checks, then call \`complete_pr_workflow\` a second time with the same immutable verification \`pr_head_sha\`; the second call clears only after both the actual remote ref and its local tracking ref resolve to the exact bound commit. Only then perform any explicitly authorized PR comment/body/thread writes.
|
|
59684
59947
|
- Patch only confirmed items plus the tests/docs they require; report closure status for every ledger item including disproved ones.
|
|
59685
|
-
- ABORT IF UNRECOVERABLE (pre-armed only): if the PR head cannot be fetched/checked out
|
|
59948
|
+
- ABORT IF UNRECOVERABLE (pre-armed only): if the PR head cannot be fetched/checked out, the verification bind is unreachable, or bounded recovery is exhausted, call \`abort_pr_workflow\` with \`mode: "PR_FEEDBACK"\`, \`kind: "recovery"\`, and a one-line \`reason\` instead of looping. The tool accepts unbound and bound workflows, refuses while PR workflow lanes are in flight (collect their results first), and refuses once \`prFeedbackReadyToPublish\` is armed \u2014 after arming, you MUST complete the workflow with \`complete_pr_workflow\` (or push the bound commit first); aborting an armed gate would drop the immutable-commit binding and leave a half-published commit. Do NOT abort while useful bounded recovery work remains.
|
|
59686
59949
|
- Do NOT resolve or mark GitHub review threads resolved unless the user explicitly instructs it.
|
|
59687
59950
|
- Honor any free-text instructions that follow the closing bracket of the signal as additional scope, without dropping any ledger item.
|
|
59688
59951
|
- Quality is the only metric \u2014 there is no speed, efficiency, or time exception; time, tokens, and agent dispatches are irrelevant to correctness
|