opencode-swarm 7.140.0 → 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/commit-pr/SKILL.md +7 -0
- package/.opencode/skills/issue-ingest/SKILL.md +32 -1
- package/.opencode/skills/swarm-pr-feedback/SKILL.md +21 -5
- package/.opencode/skills/swarm-pr-review/SKILL.md +75 -17
- package/dist/background/candidate-contract.d.ts +3 -0
- package/dist/cli/{config-doctor-3bm8pvnb.js → config-doctor-shc0n4q1.js} +3 -3
- package/dist/cli/{core-jtj9j6z5.js → core-p5cyej3r.js} +2 -2
- package/dist/cli/{curation-policy-zkvkg4np.js → curation-policy-7n6t8he4.js} +2 -2
- package/dist/cli/{curator-llm-factory-kbqtk2wm.js → curator-llm-factory-pr4bdfqw.js} +14 -14
- package/dist/cli/{curator-78ejknh2.js → curator-zp5qq24c.js} +14 -14
- package/dist/cli/{guardrail-explain-d6emse0z.js → guardrail-explain-thddj6jv.js} +15 -15
- package/dist/cli/{guardrail-log-27d6r2xq.js → guardrail-log-w8fmxe1x.js} +4 -4
- package/dist/cli/{hive-promoter-r5x4a2ta.js → hive-promoter-0yg3gxg6.js} +14 -14
- package/dist/cli/{index-43k8ntw7.js → index-0tevsq1a.js} +2 -2
- package/dist/cli/{index-ma8vk3v1.js → index-1j5my2qy.js} +2 -2
- package/dist/cli/{index-s38e38qy.js → index-325zmfyj.js} +4 -4
- package/dist/cli/{index-scbp2d32.js → index-4rje8dc0.js} +16 -16
- package/dist/cli/{index-6yv6wda9.js → index-7ax0bkqh.js} +1 -1
- package/dist/cli/{index-hbnsmnb0.js → index-898f16j9.js} +1 -1
- package/dist/cli/{index-f8dhh53t.js → index-9txca231.js} +3 -3
- package/dist/cli/{index-0pqh1710.js → index-bmmzfrc7.js} +2 -2
- package/dist/cli/{index-0jbhmm8b.js → index-chx92bes.js} +2 -2
- package/dist/cli/{index-p6h8f59v.js → index-jfp1bgt1.js} +1 -1
- package/dist/cli/{index-r4t92q32.js → index-n408rw7n.js} +9 -1
- package/dist/cli/{index-mb34t6w9.js → index-n831qq2v.js} +459 -178
- package/dist/cli/{index-6ykm2eha.js → index-t59cdg48.js} +1 -1
- package/dist/cli/{index-chycpf9y.js → index-vjqe1z2w.js} +3 -3
- package/dist/cli/{index-zebb6yw0.js → index-x6mtyj6q.js} +2 -2
- package/dist/cli/{index-6d7jkk1s.js → index-y98ngs4g.js} +2 -2
- package/dist/cli/{index-bdnrrx3w.js → index-yt3vz0e1.js} +1 -1
- package/dist/cli/{index-fxw0zvxc.js → index-z6md3t8m.js} +5 -5
- package/dist/cli/index.js +14 -14
- package/dist/cli/{knowledge-escalator-qjdfv9sj.js → knowledge-escalator-58tw74js.js} +3 -3
- package/dist/cli/{knowledge-events-7szzg70x.js → knowledge-events-knr0pfq4.js} +1 -1
- package/dist/cli/{knowledge-store-9mr7a9n8.js → knowledge-store-e2srcd89.js} +1 -1
- package/dist/cli/{knowledge-validator-58s2j0dp.js → knowledge-validator-1dtj02q8.js} +4 -4
- package/dist/cli/{scan-cursor-h3j1egky.js → scan-cursor-7tqsvbp8.js} +2 -2
- package/dist/cli/{schema-9ryqandr.js → schema-b046zss2.js} +2 -2
- package/dist/cli/{skill-generator-66ez26yh.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/issue-trace-reducer.d.ts +37 -13
- package/dist/hooks/issue-trace-state.d.ts +38 -9
- package/dist/hooks/issue-trace.d.ts +15 -2
- package/dist/hooks/pr-workflow-gate.d.ts +21 -7
- package/dist/index.js +117 -107
- package/dist/tools/complete-pr-workflow.d.ts +6 -0
- package/dist/tools/gh-evidence.d.ts +6 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/manifest.d.ts +2 -0
- package/dist/tools/prepare-pr-workflow-checkout.d.ts +11 -0
- package/dist/tools/record-issue-publication.d.ts +16 -0
- package/dist/tools/record-issue-reproduction.d.ts +22 -0
- package/dist/tools/tool-metadata.d.ts +8 -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);
|
|
@@ -2340,7 +2484,7 @@ async function clearPrWorkflowGateState(directory, sessionID, expectedRevision)
|
|
|
2340
2484
|
trackedStatesByProjectSession.delete(stateCacheKey(directory, normalizedSessionID));
|
|
2341
2485
|
});
|
|
2342
2486
|
}
|
|
2343
|
-
async function abortPrWorkflow(directory, sessionID, options = {}) {
|
|
2487
|
+
async function abortPrWorkflow(directory, sessionID, options = { kind: "recovery", reason: "" }) {
|
|
2344
2488
|
const state = await requireAnyActiveState(directory, sessionID);
|
|
2345
2489
|
if (options.expectedMode && state.mode !== options.expectedMode) {
|
|
2346
2490
|
throw wrongModeError(state, options.expectedMode);
|
|
@@ -2353,15 +2497,20 @@ async function abortPrWorkflow(directory, sessionID, options = {}) {
|
|
|
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
|
-
const sanitizedReason = typeof options.reason === "string" ? options.reason.trim().slice(0, 500) : undefined;
|
|
2500
|
+
const sanitizedReason = typeof options.reason === "string" && options.reason.trim().length > 0 ? options.reason.trim().slice(0, 500) : undefined;
|
|
2501
|
+
if (sanitizedReason === undefined) {
|
|
2502
|
+
throw new Error(`BLOCKED: ${state.mode} abort requires a non-empty reason (recorded to the audit trail).`);
|
|
2503
|
+
}
|
|
2357
2504
|
const abortEvent = {
|
|
2358
2505
|
type: "pr_workflow_aborted",
|
|
2359
2506
|
timestamp: isoNow(),
|
|
2360
2507
|
sessionID: state.sessionID,
|
|
2361
2508
|
mode: state.mode,
|
|
2509
|
+
kind: options.kind,
|
|
2362
2510
|
...state.prHeadSha ? { prHeadSha: state.prHeadSha } : {},
|
|
2511
|
+
...state.checkoutRecovery ? { hadTerminalRecovery: true } : {},
|
|
2363
2512
|
openLanes: openLanes.length,
|
|
2364
|
-
|
|
2513
|
+
reason: sanitizedReason
|
|
2365
2514
|
};
|
|
2366
2515
|
try {
|
|
2367
2516
|
const eventsPath = validateSwarmPath(directory, "events.jsonl");
|
|
@@ -2375,46 +2524,45 @@ async function abortPrWorkflow(directory, sessionID, options = {}) {
|
|
|
2375
2524
|
openLanes: openLanes.length
|
|
2376
2525
|
};
|
|
2377
2526
|
}
|
|
2378
|
-
async function
|
|
2527
|
+
async function bindPrWorkflowHeadWhileLocked(directory, normalizedSessionID, prHeadSha) {
|
|
2379
2528
|
const normalizedHead = normalizePrHeadSha(prHeadSha);
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
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") {
|
|
2393
2545
|
await assertPrFeedbackTrackingCheckout(directory, normalizedHead);
|
|
2394
2546
|
}
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
};
|
|
2406
|
-
await _test_exports.beforePrFeedbackTrackingPersist?.();
|
|
2407
|
-
return writeStateWhileLocked(directory, nextState);
|
|
2408
|
-
}));
|
|
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);
|
|
2409
2557
|
}
|
|
2410
2558
|
async function assertCurrentCheckoutHead(directory, expectedHead, mode = "PR_REVIEW") {
|
|
2411
2559
|
const normalizedExpected = normalizePrHeadSha(expectedHead);
|
|
2412
2560
|
const currentHead = (await _test_exports.resolveCurrentGitHeadAsync(directory))?.trim();
|
|
2413
2561
|
if (!currentHead) {
|
|
2414
|
-
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).`);
|
|
2415
2563
|
}
|
|
2416
2564
|
if (currentHead.toLowerCase() !== normalizedExpected.toLowerCase()) {
|
|
2417
|
-
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\`.`;
|
|
2418
2566
|
throw new Error(`BLOCKED: current checkout HEAD "${currentHead}" does not match PR head "${normalizedExpected}" ` + `(working directory: "${directory}"). ${remediation}`);
|
|
2419
2567
|
}
|
|
2420
2568
|
return normalizedExpected;
|
|
@@ -2936,6 +3084,7 @@ var _test_exports = {
|
|
|
2936
3084
|
_test_exports.openCheckoutLock = openCheckoutLockFile;
|
|
2937
3085
|
_test_exports.removeCheckoutLock = removeCheckoutLockFile;
|
|
2938
3086
|
_test_exports.checkoutMutationActionTimeoutMs = CHECKOUT_MUTATION_ACTION_TIMEOUT_MS;
|
|
3087
|
+
_test_exports.classifyPrWorkflowGitStateAsync = classifyPrWorkflowGitState;
|
|
2939
3088
|
},
|
|
2940
3089
|
beforeTerminalClear: undefined,
|
|
2941
3090
|
beforePrFeedbackTransitionLock: undefined,
|
|
@@ -2951,6 +3100,7 @@ var _test_exports = {
|
|
|
2951
3100
|
openCheckoutLock: openCheckoutLockFile,
|
|
2952
3101
|
removeCheckoutLock: removeCheckoutLockFile,
|
|
2953
3102
|
checkoutMutationActionTimeoutMs: CHECKOUT_MUTATION_ACTION_TIMEOUT_MS,
|
|
3103
|
+
classifyPrWorkflowGitStateAsync: classifyPrWorkflowGitState,
|
|
2954
3104
|
resolveCurrentGitHead,
|
|
2955
3105
|
resolveCurrentGitHeadAsync,
|
|
2956
3106
|
resolveCurrentUpstreamPushTarget,
|
|
@@ -3063,8 +3213,128 @@ var PR_REVIEW_READ_ONLY_TOOL_NAMES = new Set([
|
|
|
3063
3213
|
"webfetch",
|
|
3064
3214
|
"websearch"
|
|
3065
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
|
+
}
|
|
3066
3336
|
function hasUnsafeShellControlSyntax(command) {
|
|
3067
|
-
return
|
|
3337
|
+
return classifyPrWorkflowShellSyntax(command).unsafe;
|
|
3068
3338
|
}
|
|
3069
3339
|
function isSafeStandaloneGitCommit(command) {
|
|
3070
3340
|
if (hasUnsafeShellControlSyntax(command))
|
|
@@ -3841,7 +4111,8 @@ function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkf
|
|
|
3841
4111
|
const issues = [];
|
|
3842
4112
|
const normalized = normalizeCandidateArtifact(text, fallbackFamily);
|
|
3843
4113
|
const canonicalText = normalized.text;
|
|
3844
|
-
const
|
|
4114
|
+
const repairKinds = normalized.repairKinds;
|
|
4115
|
+
const salvaged = repairKinds.length > 0;
|
|
3845
4116
|
const header = selectCandidateHeader(canonicalText.split(/\r?\n/));
|
|
3846
4117
|
if (header === null || !header.markerBearing || header.family !== fallbackFamily) {
|
|
3847
4118
|
appendBoundedCandidateIssue(issues, `field header: expected one exact canonical ${fallbackFamily} [CANDIDATE] header before discovery rows`);
|
|
@@ -3850,6 +4121,7 @@ function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkf
|
|
|
3850
4121
|
evidence: null,
|
|
3851
4122
|
issues,
|
|
3852
4123
|
salvaged,
|
|
4124
|
+
repairKinds,
|
|
3853
4125
|
failurePredicate: "discovery.header"
|
|
3854
4126
|
};
|
|
3855
4127
|
}
|
|
@@ -3893,6 +4165,7 @@ function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkf
|
|
|
3893
4165
|
evidence: null,
|
|
3894
4166
|
issues,
|
|
3895
4167
|
salvaged,
|
|
4168
|
+
repairKinds,
|
|
3896
4169
|
failurePredicate: "discovery.row"
|
|
3897
4170
|
};
|
|
3898
4171
|
}
|
|
@@ -3910,7 +4183,8 @@ function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkf
|
|
|
3910
4183
|
matchingCandidate.confidence
|
|
3911
4184
|
].join("\x00"),
|
|
3912
4185
|
issues,
|
|
3913
|
-
salvaged
|
|
4186
|
+
salvaged,
|
|
4187
|
+
repairKinds
|
|
3914
4188
|
};
|
|
3915
4189
|
}
|
|
3916
4190
|
const clean = parsed.clean_attestation;
|
|
@@ -3919,7 +4193,8 @@ function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkf
|
|
|
3919
4193
|
covered: true,
|
|
3920
4194
|
evidence: `${clean.coverage_scope}\x00${clean.evidence}`,
|
|
3921
4195
|
issues,
|
|
3922
|
-
salvaged
|
|
4196
|
+
salvaged,
|
|
4197
|
+
repairKinds
|
|
3923
4198
|
};
|
|
3924
4199
|
}
|
|
3925
4200
|
return {
|
|
@@ -3927,6 +4202,7 @@ function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkf
|
|
|
3927
4202
|
evidence: null,
|
|
3928
4203
|
issues,
|
|
3929
4204
|
salvaged,
|
|
4205
|
+
repairKinds,
|
|
3930
4206
|
failurePredicate: hasParseFailure ? "discovery.row" : "discovery.coverage"
|
|
3931
4207
|
};
|
|
3932
4208
|
}
|
|
@@ -3974,7 +4250,7 @@ function validatePrReviewDiscoveryLaneCompletion(input) {
|
|
|
3974
4250
|
if (!analysis.salvaged && analysis.issues.length === 0)
|
|
3975
4251
|
continue;
|
|
3976
4252
|
salvagedLanes.push(workflowLane);
|
|
3977
|
-
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";
|
|
3978
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)"}`);
|
|
3979
4255
|
}
|
|
3980
4256
|
const failedCoverage = analyses.find(({ analysis }) => !analysis.covered);
|
|
@@ -4122,13 +4398,6 @@ function pipeFields(line) {
|
|
|
4122
4398
|
return [];
|
|
4123
4399
|
return line.split("|").map((field) => field.trim());
|
|
4124
4400
|
}
|
|
4125
|
-
async function persistState(directory, state) {
|
|
4126
|
-
const validated = PrWorkflowGateStateSchema.parse(state);
|
|
4127
|
-
await withSessionStateMutation(directory, validated.sessionID, async () => {
|
|
4128
|
-
const nextState = await writeStateWhileLocked(directory, validated);
|
|
4129
|
-
Object.assign(state, nextState);
|
|
4130
|
-
});
|
|
4131
|
-
}
|
|
4132
4401
|
async function writeStateWhileLocked(directory, state, options = {}) {
|
|
4133
4402
|
const validated = PrWorkflowGateStateSchema.parse(state);
|
|
4134
4403
|
const current = await readPrWorkflowGateStateFromDisk(directory, validated.sessionID);
|
|
@@ -4528,6 +4797,9 @@ function isProcessAlive(pid) {
|
|
|
4528
4797
|
}
|
|
4529
4798
|
async function readPrWorkflowGateStateFromDisk(directory, sessionID) {
|
|
4530
4799
|
const filePath = workflowGateStatePath(directory, sessionID);
|
|
4800
|
+
return readPrWorkflowGateStateFileFromDisk(filePath, sessionID);
|
|
4801
|
+
}
|
|
4802
|
+
async function readPrWorkflowGateStateFileFromDisk(filePath, stateLabel) {
|
|
4531
4803
|
let raw;
|
|
4532
4804
|
try {
|
|
4533
4805
|
raw = await fsp2.readFile(filePath, "utf-8");
|
|
@@ -4540,11 +4812,11 @@ async function readPrWorkflowGateStateFromDisk(directory, sessionID) {
|
|
|
4540
4812
|
try {
|
|
4541
4813
|
parsedJson = JSON.parse(raw);
|
|
4542
4814
|
} catch {
|
|
4543
|
-
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`);
|
|
4544
4816
|
}
|
|
4545
4817
|
const parsed = PrWorkflowGateStateSchema.safeParse(parsedJson);
|
|
4546
4818
|
if (!parsed.success) {
|
|
4547
|
-
throw new Error(`BLOCKED: PR workflow gate state for session "${
|
|
4819
|
+
throw new Error(`BLOCKED: PR workflow gate state for session "${stateLabel}" is invalid`);
|
|
4548
4820
|
}
|
|
4549
4821
|
return parsed.data;
|
|
4550
4822
|
}
|
|
@@ -4712,16 +4984,21 @@ init_logger();
|
|
|
4712
4984
|
|
|
4713
4985
|
// src/commands/abort-pr-workflow.ts
|
|
4714
4986
|
var USAGE = [
|
|
4715
|
-
"Usage: /swarm abort-pr-workflow [PR_REVIEW|PR_FEEDBACK]
|
|
4987
|
+
"Usage: /swarm abort-pr-workflow [PR_REVIEW|PR_FEEDBACK] <reason...>",
|
|
4716
4988
|
"",
|
|
4717
4989
|
"Clear an active PR_REVIEW or PR_FEEDBACK mechanical gate for the current session",
|
|
4718
|
-
"and stop the auto-resume loop.
|
|
4719
|
-
"
|
|
4720
|
-
"
|
|
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",
|
|
4993
|
+
"a PR review or feedback workflow is unrecoverably stuck (e.g. the working tree",
|
|
4994
|
+
"cannot reach the PR head, a compound shell command was rejected, or the wake",
|
|
4995
|
+
"budget is suspended).",
|
|
4721
4996
|
"",
|
|
4722
4997
|
"Arguments:",
|
|
4723
4998
|
" mode Optional: PR_REVIEW or PR_FEEDBACK. If omitted, aborts whichever is active.",
|
|
4724
|
-
" reason Optional free-text reason
|
|
4999
|
+
" reason Optional free-text reason recorded to the audit trail (.swarm/events.jsonl).",
|
|
5000
|
+
' If omitted, a default ("user-initiated force abort ...") is recorded so the',
|
|
5001
|
+
" gate always has a non-empty reason. The agent tool call must supply its own.",
|
|
4725
5002
|
"",
|
|
4726
5003
|
"Refuses while the workflow is armed for publication (call complete_pr_workflow",
|
|
4727
5004
|
"instead) or while PR workflow lanes are still in flight (collect their results first)."
|
|
@@ -4733,7 +5010,7 @@ async function handleAbortPrWorkflowCommand(directory, args, sessionID) {
|
|
|
4733
5010
|
const modeToken = tokens[0]?.toUpperCase();
|
|
4734
5011
|
const knownMode = modeToken && KNOWN_MODES.has(modeToken) ? modeToken : undefined;
|
|
4735
5012
|
const reasonStartIndex = knownMode ? 1 : 0;
|
|
4736
|
-
const
|
|
5013
|
+
const explicitReason = tokens.slice(reasonStartIndex).join(" ").trim().slice(0, 500);
|
|
4737
5014
|
if (modeToken && !knownMode) {
|
|
4738
5015
|
return `Error: Unknown mode "${modeToken}". Expected PR_REVIEW or PR_FEEDBACK.
|
|
4739
5016
|
|
|
@@ -4744,13 +5021,15 @@ ${USAGE}`;
|
|
|
4744
5021
|
|
|
4745
5022
|
${USAGE}`;
|
|
4746
5023
|
}
|
|
5024
|
+
const reason = explicitReason || "user-initiated force abort via /swarm abort-pr-workflow (no explicit reason provided)";
|
|
4747
5025
|
try {
|
|
4748
5026
|
const summary = await abortPrWorkflow(directory, sessionID, {
|
|
4749
|
-
|
|
4750
|
-
|
|
5027
|
+
kind: "force",
|
|
5028
|
+
reason,
|
|
5029
|
+
...knownMode ? { expectedMode: knownMode } : {}
|
|
4751
5030
|
});
|
|
4752
5031
|
const headLine = summary.prHeadSha ? ` (was bound to PR head ${summary.prHeadSha})` : " (was not bound to a PR head)";
|
|
4753
|
-
return `Aborted active ${summary.mode} mechanical gate for session ${sessionID}${headLine}. 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).`;
|
|
4754
5033
|
} catch (error2) {
|
|
4755
5034
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
4756
5035
|
return `Error: ${message}
|
|
@@ -14553,8 +14832,8 @@ function dcValidateTargets(targets, cwd) {
|
|
|
14553
14832
|
const lstatBlock = dcLstatAncestorWalk(filesystemTarget, cwd);
|
|
14554
14833
|
if (lstatBlock)
|
|
14555
14834
|
return lstatBlock;
|
|
14556
|
-
const
|
|
14557
|
-
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)) {
|
|
14558
14837
|
continue;
|
|
14559
14838
|
}
|
|
14560
14839
|
if (DC_FS_ROOTS.has(t) || DC_FS_ROOTS.has(t.replace(/\//g, "\\"))) {
|
|
@@ -14640,8 +14919,8 @@ function dcEvaluateRecursiveDeleteTargets(input) {
|
|
|
14640
14919
|
reason: `BLOCKED: Recursive delete target "${safeTarget}" is not strictly contained by the workspace.`
|
|
14641
14920
|
};
|
|
14642
14921
|
}
|
|
14643
|
-
const
|
|
14644
|
-
if (!DC_SAFE_TARGETS.has(
|
|
14922
|
+
const basename6 = path23.basename(resolved);
|
|
14923
|
+
if (!DC_SAFE_TARGETS.has(basename6)) {
|
|
14645
14924
|
return {
|
|
14646
14925
|
allowed: false,
|
|
14647
14926
|
code: "DESTRUCTIVE_TARGET_NOT_SAFE_ARTIFACT",
|
|
@@ -21991,7 +22270,7 @@ function planMerges(active, threshold, maxComparisons, maxMerges) {
|
|
|
21991
22270
|
};
|
|
21992
22271
|
}
|
|
21993
22272
|
async function sweepActiveNearDuplicates(directory, options = {}) {
|
|
21994
|
-
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-
|
|
22273
|
+
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-vjqe1z2w.js");
|
|
21995
22274
|
const { config } = loadPluginConfigWithMeta2(directory);
|
|
21996
22275
|
const sweepConfig = config.learning?.dedup_sweep;
|
|
21997
22276
|
if (sweepConfig?.enabled === false)
|
|
@@ -23075,7 +23354,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
|
|
|
23075
23354
|
}
|
|
23076
23355
|
if (config.skill_generation_enabled === true && skillCandidates.length > 0) {
|
|
23077
23356
|
try {
|
|
23078
|
-
const skillModule = await import("./skill-generator-
|
|
23357
|
+
const skillModule = await import("./skill-generator-bvph9d98.js");
|
|
23079
23358
|
for (const cand of skillCandidates) {
|
|
23080
23359
|
if (cand.confidence < (config.min_skill_confidence ?? DEFAULT_SKILL_MIN_CONFIDENCE)) {
|
|
23081
23360
|
continue;
|
|
@@ -23535,7 +23814,7 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
|
|
|
23535
23814
|
} catch {}
|
|
23536
23815
|
}
|
|
23537
23816
|
if (contradictedEntries.length > 0 && knowledgeConfig.contradiction_threshold_action === "quarantine") {
|
|
23538
|
-
const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-
|
|
23817
|
+
const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-58tw74js.js");
|
|
23539
23818
|
for (const { id } of contradictedEntries) {
|
|
23540
23819
|
try {
|
|
23541
23820
|
await maybeQuarantineOnContradiction(directory, id, knowledgeConfig.contradiction_quarantine_threshold, knowledgeConfig.contradiction_quarantine_window_days);
|
|
@@ -24354,7 +24633,7 @@ async function runCuratorPostMortem(directory, options = {}) {
|
|
|
24354
24633
|
warnings.push("Failed to collect knowledge summary.");
|
|
24355
24634
|
}
|
|
24356
24635
|
try {
|
|
24357
|
-
const { getScanStatus } = await import("./scan-cursor-
|
|
24636
|
+
const { getScanStatus } = await import("./scan-cursor-7tqsvbp8.js");
|
|
24358
24637
|
const scanStatus = await getScanStatus(directory);
|
|
24359
24638
|
if (scanStatus.remaining_estimate > 0) {
|
|
24360
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.`);
|
|
@@ -24526,9 +24805,9 @@ var _internals28 = {
|
|
|
24526
24805
|
verifyPostMortemKnowledgeActions,
|
|
24527
24806
|
repairPostMortemActions,
|
|
24528
24807
|
loadDefaultKnowledgeConfig: async (directory) => {
|
|
24529
|
-
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-
|
|
24808
|
+
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-b046zss2.js");
|
|
24530
24809
|
try {
|
|
24531
|
-
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-
|
|
24810
|
+
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-vjqe1z2w.js");
|
|
24532
24811
|
const loaded = loadPluginConfigWithMeta2(directory);
|
|
24533
24812
|
return KnowledgeConfigSchema2.parse(loaded.config.knowledge ?? {});
|
|
24534
24813
|
} catch {
|
|
@@ -24536,11 +24815,11 @@ var _internals28 = {
|
|
|
24536
24815
|
}
|
|
24537
24816
|
},
|
|
24538
24817
|
applyCuratorKnowledgeUpdates: async (directory, recommendations, knowledgeConfig, generation) => {
|
|
24539
|
-
const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-
|
|
24818
|
+
const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-zp5qq24c.js");
|
|
24540
24819
|
return applyCuratorKnowledgeUpdates2(directory, recommendations, knowledgeConfig, generation);
|
|
24541
24820
|
},
|
|
24542
24821
|
checkHivePromotions: async (entries, knowledgeConfig, directory) => {
|
|
24543
|
-
const { checkHivePromotions } = await import("./hive-promoter-
|
|
24822
|
+
const { checkHivePromotions } = await import("./hive-promoter-0yg3gxg6.js");
|
|
24544
24823
|
return checkHivePromotions(entries, knowledgeConfig, directory);
|
|
24545
24824
|
},
|
|
24546
24825
|
applyProposalTriage: async (directory, triage) => {
|
|
@@ -24549,7 +24828,7 @@ var _internals28 = {
|
|
|
24549
24828
|
activateProposal,
|
|
24550
24829
|
listSkills: listSkills2,
|
|
24551
24830
|
sanitizeSlug
|
|
24552
|
-
} = await import("./skill-generator-
|
|
24831
|
+
} = await import("./skill-generator-bvph9d98.js");
|
|
24553
24832
|
const result = {
|
|
24554
24833
|
approved: [],
|
|
24555
24834
|
rejected: [],
|
|
@@ -25774,9 +26053,9 @@ function hashContent(content) {
|
|
|
25774
26053
|
return createHash9("sha1").update(content).digest("hex");
|
|
25775
26054
|
}
|
|
25776
26055
|
async function loadConfigForPolicyCurator(directory) {
|
|
25777
|
-
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-
|
|
26056
|
+
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-b046zss2.js");
|
|
25778
26057
|
try {
|
|
25779
|
-
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-
|
|
26058
|
+
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-vjqe1z2w.js");
|
|
25780
26059
|
const { config: loadedConfig } = loadPluginConfigWithMeta2(directory);
|
|
25781
26060
|
return KnowledgeConfigSchema2.parse(loadedConfig.knowledge ?? {});
|
|
25782
26061
|
} catch {
|
|
@@ -29785,7 +30064,7 @@ async function runFinalizeStage(ctx) {
|
|
|
29785
30064
|
}
|
|
29786
30065
|
}
|
|
29787
30066
|
try {
|
|
29788
|
-
const { CuratorConfigSchema: CCS } = await import("./schema-
|
|
30067
|
+
const { CuratorConfigSchema: CCS } = await import("./schema-b046zss2.js");
|
|
29789
30068
|
const { config: pmLoadedConfig } = _internals39.loadPluginConfigWithMeta(ctx.directory);
|
|
29790
30069
|
const curatorCfg = CCS.parse(pmLoadedConfig.curator ?? {});
|
|
29791
30070
|
if (curatorCfg.enabled && curatorCfg.postmortem_enabled) {
|
|
@@ -32058,9 +32337,9 @@ var _internals44 = {
|
|
|
32058
32337
|
loadPluginConfigWithMeta,
|
|
32059
32338
|
loadCuratorDeps: async () => {
|
|
32060
32339
|
const [{ CuratorConfigSchema }, curator, { createCuratorLLMDelegate: createCuratorLLMDelegate2 }] = await Promise.all([
|
|
32061
|
-
import("./schema-
|
|
32062
|
-
import("./curator-
|
|
32063
|
-
import("./curator-llm-factory-
|
|
32340
|
+
import("./schema-b046zss2.js"),
|
|
32341
|
+
import("./curator-zp5qq24c.js"),
|
|
32342
|
+
import("./curator-llm-factory-pr4bdfqw.js")
|
|
32064
32343
|
]);
|
|
32065
32344
|
return { CuratorConfigSchema, curator, createCuratorLLMDelegate: createCuratorLLMDelegate2 };
|
|
32066
32345
|
}
|
|
@@ -32965,7 +33244,7 @@ async function computeCurationDiagnostics(directory, entries) {
|
|
|
32965
33244
|
let scanCompleted = false;
|
|
32966
33245
|
let scanRemaining = 0;
|
|
32967
33246
|
try {
|
|
32968
|
-
const { getScanStatus } = await import("./scan-cursor-
|
|
33247
|
+
const { getScanStatus } = await import("./scan-cursor-7tqsvbp8.js");
|
|
32969
33248
|
const status = await getScanStatus(directory);
|
|
32970
33249
|
scanGeneration = status.generation;
|
|
32971
33250
|
scanCompleted = status.completed;
|
|
@@ -32975,17 +33254,17 @@ async function computeCurationDiagnostics(directory, entries) {
|
|
|
32975
33254
|
try {
|
|
32976
33255
|
const { isLinked: isLinked2 } = await import("./knowledge-link-q46k9adx.js");
|
|
32977
33256
|
if (isLinked2(directory)) {
|
|
32978
|
-
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-
|
|
33257
|
+
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-b046zss2.js");
|
|
32979
33258
|
let config;
|
|
32980
33259
|
try {
|
|
32981
|
-
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-
|
|
33260
|
+
const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-vjqe1z2w.js");
|
|
32982
33261
|
const loaded = loadPluginConfigWithMeta2(directory);
|
|
32983
33262
|
config = KnowledgeConfigSchema2.parse(loaded.config.knowledge ?? {});
|
|
32984
33263
|
} catch {
|
|
32985
33264
|
config = KnowledgeConfigSchema2.parse({});
|
|
32986
33265
|
}
|
|
32987
33266
|
const { cohortConfigFingerprint: cohortConfigFingerprint2 } = await import("./config-fingerprint-v22xh5a2.js");
|
|
32988
|
-
const { buildConfigFingerprintInput: buildConfigFingerprintInput2, readCohortConfigFingerprint } = await import("./curation-policy-
|
|
33267
|
+
const { buildConfigFingerprintInput: buildConfigFingerprintInput2, readCohortConfigFingerprint } = await import("./curation-policy-7n6t8he4.js");
|
|
32989
33268
|
const currentFp = cohortConfigFingerprint2(buildConfigFingerprintInput2(config));
|
|
32990
33269
|
const cohortFp = await readCohortConfigFingerprint(directory);
|
|
32991
33270
|
configMatch = cohortFp === null ? null : currentFp === cohortFp;
|
|
@@ -34196,7 +34475,7 @@ async function handleDoctorCommand(directory, args) {
|
|
|
34196
34475
|
const result = runConfigDoctor(config, directory);
|
|
34197
34476
|
let output;
|
|
34198
34477
|
if (enableAutoFix && result.hasAutoFixableIssues) {
|
|
34199
|
-
const { runConfigDoctorWithFixes } = await import("./config-doctor-
|
|
34478
|
+
const { runConfigDoctorWithFixes } = await import("./config-doctor-shc0n4q1.js");
|
|
34200
34479
|
const fixResult = await runConfigDoctorWithFixes(directory, config, true, {
|
|
34201
34480
|
applyLossy: true
|
|
34202
34481
|
});
|
|
@@ -36350,7 +36629,7 @@ function countCodeLines(content) {
|
|
|
36350
36629
|
return lines.length;
|
|
36351
36630
|
}
|
|
36352
36631
|
function isTestFile(filePath) {
|
|
36353
|
-
const
|
|
36632
|
+
const basename12 = path61.basename(filePath);
|
|
36354
36633
|
const _ext = path61.extname(filePath).toLowerCase();
|
|
36355
36634
|
const testPatterns = [
|
|
36356
36635
|
".test.",
|
|
@@ -36366,7 +36645,7 @@ function isTestFile(filePath) {
|
|
|
36366
36645
|
".spec.jsx"
|
|
36367
36646
|
];
|
|
36368
36647
|
for (const pattern of testPatterns) {
|
|
36369
|
-
if (
|
|
36648
|
+
if (basename12.includes(pattern)) {
|
|
36370
36649
|
return true;
|
|
36371
36650
|
}
|
|
36372
36651
|
}
|
|
@@ -40566,7 +40845,7 @@ ${USAGE9}`;
|
|
|
40566
40845
|
const traceState = {
|
|
40567
40846
|
issueNumber: issueInfo.number,
|
|
40568
40847
|
lastTransition: null,
|
|
40569
|
-
|
|
40848
|
+
status: "in_progress"
|
|
40570
40849
|
};
|
|
40571
40850
|
let oldTraceState = null;
|
|
40572
40851
|
try {
|
|
@@ -45844,8 +46123,8 @@ function toSafeRelativePath(rootDir, targetPath) {
|
|
|
45844
46123
|
if (!relative11 || relative11 === ".")
|
|
45845
46124
|
return ".";
|
|
45846
46125
|
if (relative11.startsWith("..") || path78.isAbsolute(relative11)) {
|
|
45847
|
-
const
|
|
45848
|
-
return
|
|
46126
|
+
const basename17 = path78.basename(targetPath).replace(/\\/g, "/");
|
|
46127
|
+
return basename17 || ".";
|
|
45849
46128
|
}
|
|
45850
46129
|
return relative11;
|
|
45851
46130
|
}
|
|
@@ -47701,21 +47980,21 @@ function hasCompoundTestExtension(filename) {
|
|
|
47701
47980
|
const lower = filename.toLowerCase();
|
|
47702
47981
|
return COMPOUND_TEST_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
47703
47982
|
}
|
|
47704
|
-
function isLanguageSpecificTestFile(
|
|
47705
|
-
const lower =
|
|
47983
|
+
function isLanguageSpecificTestFile(basename18) {
|
|
47984
|
+
const lower = basename18.toLowerCase();
|
|
47706
47985
|
if (lower.endsWith("_test.go"))
|
|
47707
47986
|
return true;
|
|
47708
47987
|
if (lower.endsWith(".py") && (lower.startsWith("test_") || lower.endsWith("_test.py")))
|
|
47709
47988
|
return true;
|
|
47710
47989
|
if (lower.endsWith("_spec.rb"))
|
|
47711
47990
|
return true;
|
|
47712
|
-
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")))
|
|
47713
47992
|
return true;
|
|
47714
47993
|
if (lower.endsWith(".cs") && (lower.endsWith("test.cs") || lower.endsWith("tests.cs")))
|
|
47715
47994
|
return true;
|
|
47716
|
-
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")))
|
|
47717
47996
|
return true;
|
|
47718
|
-
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")))
|
|
47719
47998
|
return true;
|
|
47720
47999
|
if (lower.endsWith(".tests.ps1"))
|
|
47721
48000
|
return true;
|
|
@@ -47723,23 +48002,23 @@ function isLanguageSpecificTestFile(basename17) {
|
|
|
47723
48002
|
}
|
|
47724
48003
|
function isConventionTestFilePath(filePath) {
|
|
47725
48004
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
47726
|
-
const
|
|
47727
|
-
return hasCompoundTestExtension(
|
|
48005
|
+
const basename18 = path82.basename(filePath);
|
|
48006
|
+
return hasCompoundTestExtension(basename18) || basename18.includes(".spec.") || basename18.includes(".test.") || isLanguageSpecificTestFile(basename18) || isTestDirectoryPath(normalizedPath);
|
|
47728
48007
|
}
|
|
47729
48008
|
function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
47730
48009
|
const testFiles = [];
|
|
47731
48010
|
for (const file of sourceFiles) {
|
|
47732
48011
|
const absoluteFile = resolveWorkspacePath(file, workingDir);
|
|
47733
48012
|
const relativeFile = path82.relative(workingDir, absoluteFile);
|
|
47734
|
-
const
|
|
48013
|
+
const basename18 = path82.basename(absoluteFile);
|
|
47735
48014
|
const dirname30 = path82.dirname(absoluteFile);
|
|
47736
48015
|
const preferRelativeOutput = !path82.isAbsolute(file);
|
|
47737
48016
|
if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file)) {
|
|
47738
48017
|
dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
|
|
47739
48018
|
continue;
|
|
47740
48019
|
}
|
|
47741
|
-
const nameWithoutExt =
|
|
47742
|
-
const ext = path82.extname(
|
|
48020
|
+
const nameWithoutExt = basename18.replace(/\.[^.]+$/, "");
|
|
48021
|
+
const ext = path82.extname(basename18);
|
|
47743
48022
|
const genericTestNames = [
|
|
47744
48023
|
`${nameWithoutExt}.spec${ext}`,
|
|
47745
48024
|
`${nameWithoutExt}.test${ext}`
|
|
@@ -47750,7 +48029,7 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
|
47750
48029
|
...languageSpecificTestNames
|
|
47751
48030
|
].map((candidateName) => path82.join(dirname30, candidateName));
|
|
47752
48031
|
const testDirectoryNames = [
|
|
47753
|
-
|
|
48032
|
+
basename18,
|
|
47754
48033
|
...genericTestNames,
|
|
47755
48034
|
...languageSpecificTestNames
|
|
47756
48035
|
];
|
|
@@ -50815,7 +51094,9 @@ async function handleResetCommand(directory, args) {
|
|
|
50815
51094
|
"plan-export/SWARM_PLAN.json",
|
|
50816
51095
|
"checkpoints.json",
|
|
50817
51096
|
"events.jsonl",
|
|
50818
|
-
"run-memory.jsonl"
|
|
51097
|
+
"run-memory.jsonl",
|
|
51098
|
+
"reproduction.json",
|
|
51099
|
+
"issue-publication.json"
|
|
50819
51100
|
];
|
|
50820
51101
|
const results = [];
|
|
50821
51102
|
try {
|
|
@@ -57533,7 +57814,7 @@ function buildDetailedHelp(commandName, entry) {
|
|
|
57533
57814
|
async function handleHelpCommand(ctx) {
|
|
57534
57815
|
const targetCommand = ctx.args.join(" ");
|
|
57535
57816
|
if (!targetCommand) {
|
|
57536
|
-
const { buildHelpText } = await import("./index-
|
|
57817
|
+
const { buildHelpText } = await import("./index-4rje8dc0.js");
|
|
57537
57818
|
return buildHelpText();
|
|
57538
57819
|
}
|
|
57539
57820
|
const tokens = targetCommand.split(/\s+/);
|
|
@@ -57542,7 +57823,7 @@ async function handleHelpCommand(ctx) {
|
|
|
57542
57823
|
return _internals18.buildDetailedHelp(resolved.key, resolved.entry);
|
|
57543
57824
|
}
|
|
57544
57825
|
const similar = _internals18.findSimilarCommands(targetCommand);
|
|
57545
|
-
const { buildHelpText: fullHelp } = await import("./index-
|
|
57826
|
+
const { buildHelpText: fullHelp } = await import("./index-4rje8dc0.js");
|
|
57546
57827
|
if (similar.length > 0) {
|
|
57547
57828
|
return `Command '/swarm ${targetCommand}' not found.
|
|
57548
57829
|
|
|
@@ -57719,7 +58000,7 @@ var COMMAND_REGISTRY = {
|
|
|
57719
58000
|
},
|
|
57720
58001
|
"guardrail explain": {
|
|
57721
58002
|
handler: async (ctx) => {
|
|
57722
|
-
const { handleGuardrailExplain } = await import("./guardrail-explain-
|
|
58003
|
+
const { handleGuardrailExplain } = await import("./guardrail-explain-thddj6jv.js");
|
|
57723
58004
|
return handleGuardrailExplain(ctx.directory, ctx.args);
|
|
57724
58005
|
},
|
|
57725
58006
|
description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
|
|
@@ -57729,7 +58010,7 @@ var COMMAND_REGISTRY = {
|
|
|
57729
58010
|
},
|
|
57730
58011
|
"guardrail-explain": {
|
|
57731
58012
|
handler: async (ctx) => {
|
|
57732
|
-
const { handleGuardrailExplain } = await import("./guardrail-explain-
|
|
58013
|
+
const { handleGuardrailExplain } = await import("./guardrail-explain-thddj6jv.js");
|
|
57733
58014
|
return handleGuardrailExplain(ctx.directory, ctx.args);
|
|
57734
58015
|
},
|
|
57735
58016
|
description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
|
|
@@ -57739,7 +58020,7 @@ var COMMAND_REGISTRY = {
|
|
|
57739
58020
|
},
|
|
57740
58021
|
"guardrail-log": {
|
|
57741
58022
|
handler: async (ctx) => {
|
|
57742
|
-
const { handleGuardrailLog } = await import("./guardrail-log-
|
|
58023
|
+
const { handleGuardrailLog } = await import("./guardrail-log-w8fmxe1x.js");
|
|
57743
58024
|
return handleGuardrailLog(ctx.directory, ctx.args);
|
|
57744
58025
|
},
|
|
57745
58026
|
description: "Read the guardrail decision log (use --blocks-only for blocks)",
|
|
@@ -58185,7 +58466,7 @@ Subcommands:
|
|
|
58185
58466
|
handler: (ctx) => handleAbortPrWorkflowCommand(ctx.directory, ctx.args, ctx.sessionID),
|
|
58186
58467
|
description: "Clear a stuck PR_REVIEW/PR_FEEDBACK mechanical gate and stop the auto-resume loop [mode] [reason]",
|
|
58187
58468
|
args: "[PR_REVIEW|PR_FEEDBACK] [reason...]",
|
|
58188
|
-
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.",
|
|
58189
58470
|
category: "utility",
|
|
58190
58471
|
toolPolicy: "restricted"
|
|
58191
58472
|
},
|
|
@@ -59628,15 +59909,15 @@ HARD CONSTRAINTS (apply regardless of skill load success):
|
|
|
59628
59909
|
- Test execution, explorer lanes, reviewer dispatch, and critic challenge are all permitted within this mode
|
|
59629
59910
|
- Quality is the only metric \u2014 there is no speed, efficiency, or time exception; time, tokens, and agent dispatches are irrelevant to correctness
|
|
59630
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.
|
|
59631
|
-
- 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.
|
|
59632
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.
|
|
59633
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.
|
|
59634
|
-
- 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.
|
|
59635
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.
|
|
59636
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.
|
|
59637
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.
|
|
59638
|
-
- 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.
|
|
59639
|
-
- 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.
|
|
59640
59921
|
- Honor any free-text instructions that follow the closing bracket of the signal as additional reviewer focus, without weakening the validation ladder above.
|
|
59641
59922
|
|
|
59642
59923
|
### MODE: PR_FEEDBACK
|
|
@@ -59664,7 +59945,7 @@ HARD CONSTRAINTS (apply regardless of skill load success):
|
|
|
59664
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.
|
|
59665
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.
|
|
59666
59947
|
- Patch only confirmed items plus the tests/docs they require; report closure status for every ledger item including disproved ones.
|
|
59667
|
-
- 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.
|
|
59668
59949
|
- Do NOT resolve or mark GitHub review threads resolved unless the user explicitly instructs it.
|
|
59669
59950
|
- Honor any free-text instructions that follow the closing bracket of the signal as additional scope, without dropping any ledger item.
|
|
59670
59951
|
- Quality is the only metric \u2014 there is no speed, efficiency, or time exception; time, tokens, and agent dispatches are irrelevant to correctness
|