@deftai/directive-runtime 0.129.6 → 0.129.8
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/Taskfile.yml +103 -16
- package/commands.md +8 -3
- package/dist/session-start.d.ts +8 -2
- package/dist/session-start.js +33 -12
- package/dist/vbrief-preflight.d.ts +4 -0
- package/dist/vbrief-preflight.js +34 -0
- package/dist/verify-scope-provenance.d.ts +13 -1
- package/dist/verify-scope-provenance.js +44 -1
- package/docs/getting-started.md +2 -0
- package/docs/scope-provenance.md +15 -1
- package/package.json +1 -1
- package/packs/skills/skills-pack-0.1.json +1 -1
- package/scm/github.md +5 -0
- package/skills/deft-directive-review-cycle/SKILL.md +15 -6
- package/skills/deft-directive-swarm/references/host-claude-code.md +11 -1
- package/skills/deft-directive-swarm/references/host-grok-build.md +3 -2
- package/vendor/directive-content/Taskfile.yml +103 -16
- package/vendor/directive-content/commands.md +8 -3
- package/vendor/directive-content/docs/getting-started.md +2 -0
- package/vendor/directive-content/docs/scope-provenance.md +15 -1
- package/vendor/directive-content/packs/skills/skills-pack-0.1.json +1 -1
- package/vendor/directive-content/scm/github.md +5 -0
- package/vendor/directive-content/skills/deft-directive-review-cycle/SKILL.md +15 -6
- package/vendor/directive-content/skills/deft-directive-swarm/references/host-claude-code.md +11 -1
- package/vendor/directive-content/skills/deft-directive-swarm/references/host-grok-build.md +3 -2
- package/vendor/directive-core/dist/.tsbuildinfo +1 -1
- package/vendor/directive-core/dist/check/context.d.ts.map +1 -1
- package/vendor/directive-core/dist/check/context.js +3 -31
- package/vendor/directive-core/dist/check/context.js.map +1 -1
- package/vendor/directive-core/dist/check/gate-lists.d.ts.map +1 -1
- package/vendor/directive-core/dist/check/gate-lists.js +4 -32
- package/vendor/directive-core/dist/check/gate-lists.js.map +1 -1
- package/vendor/directive-core/dist/check/walk-test-files.d.ts +3 -0
- package/vendor/directive-core/dist/check/walk-test-files.d.ts.map +1 -0
- package/vendor/directive-core/dist/check/walk-test-files.js +67 -0
- package/vendor/directive-core/dist/check/walk-test-files.js.map +1 -0
- package/vendor/directive-core/dist/fs/contained-write.d.ts +8 -2
- package/vendor/directive-core/dist/fs/contained-write.d.ts.map +1 -1
- package/vendor/directive-core/dist/fs/contained-write.js +22 -6
- package/vendor/directive-core/dist/fs/contained-write.js.map +1 -1
- package/vendor/directive-core/dist/hooks/dispatcher.d.ts +7 -1
- package/vendor/directive-core/dist/hooks/dispatcher.d.ts.map +1 -1
- package/vendor/directive-core/dist/hooks/dispatcher.js +180 -10
- package/vendor/directive-core/dist/hooks/dispatcher.js.map +1 -1
- package/vendor/directive-core/dist/hooks/scope.d.ts +54 -0
- package/vendor/directive-core/dist/hooks/scope.d.ts.map +1 -1
- package/vendor/directive-core/dist/hooks/scope.js +83 -2
- package/vendor/directive-core/dist/hooks/scope.js.map +1 -1
- package/vendor/directive-core/dist/init-deposit/agent-hooks.d.ts +26 -1
- package/vendor/directive-core/dist/init-deposit/agent-hooks.d.ts.map +1 -1
- package/vendor/directive-core/dist/init-deposit/agent-hooks.js +52 -3
- package/vendor/directive-core/dist/init-deposit/agent-hooks.js.map +1 -1
- package/vendor/directive-core/dist/init-deposit/host-hook-outer.d.ts +43 -0
- package/vendor/directive-core/dist/init-deposit/host-hook-outer.d.ts.map +1 -1
- package/vendor/directive-core/dist/init-deposit/host-hook-outer.js +148 -0
- package/vendor/directive-core/dist/init-deposit/host-hook-outer.js.map +1 -1
- package/vendor/directive-core/dist/intake/reconcile-issues.d.ts +33 -1
- package/vendor/directive-core/dist/intake/reconcile-issues.d.ts.map +1 -1
- package/vendor/directive-core/dist/intake/reconcile-issues.js +288 -12
- package/vendor/directive-core/dist/intake/reconcile-issues.js.map +1 -1
- package/vendor/directive-core/dist/intake/thin-brief.d.ts +78 -0
- package/vendor/directive-core/dist/intake/thin-brief.d.ts.map +1 -0
- package/vendor/directive-core/dist/intake/thin-brief.js +224 -0
- package/vendor/directive-core/dist/intake/thin-brief.js.map +1 -0
- package/vendor/directive-core/dist/policy/ceremony-dial.d.ts.map +1 -1
- package/vendor/directive-core/dist/policy/ceremony-dial.js +2 -1
- package/vendor/directive-core/dist/policy/ceremony-dial.js.map +1 -1
- package/vendor/directive-core/dist/policy/small-change-lane.d.ts +193 -0
- package/vendor/directive-core/dist/policy/small-change-lane.d.ts.map +1 -0
- package/vendor/directive-core/dist/policy/small-change-lane.js +690 -0
- package/vendor/directive-core/dist/policy/small-change-lane.js.map +1 -0
- package/vendor/directive-core/dist/pr-merge-readiness/slizard-gate.d.ts +10 -5
- package/vendor/directive-core/dist/pr-merge-readiness/slizard-gate.d.ts.map +1 -1
- package/vendor/directive-core/dist/pr-merge-readiness/slizard-gate.js +41 -18
- package/vendor/directive-core/dist/pr-merge-readiness/slizard-gate.js.map +1 -1
- package/vendor/directive-core/dist/preflight/evaluate.d.ts +3 -0
- package/vendor/directive-core/dist/preflight/evaluate.d.ts.map +1 -1
- package/vendor/directive-core/dist/preflight/evaluate.js +15 -0
- package/vendor/directive-core/dist/preflight/evaluate.js.map +1 -1
- package/vendor/directive-core/dist/preflight-gh/pre-push.d.ts +3 -2
- package/vendor/directive-core/dist/preflight-gh/pre-push.d.ts.map +1 -1
- package/vendor/directive-core/dist/preflight-gh/pre-push.js +19 -18
- package/vendor/directive-core/dist/preflight-gh/pre-push.js.map +1 -1
- package/vendor/directive-core/dist/product-first-done-gate/evaluate.d.ts +6 -0
- package/vendor/directive-core/dist/product-first-done-gate/evaluate.d.ts.map +1 -1
- package/vendor/directive-core/dist/product-first-done-gate/evaluate.js +26 -0
- package/vendor/directive-core/dist/product-first-done-gate/evaluate.js.map +1 -1
- package/vendor/directive-core/dist/scope/decomposed-refs.d.ts +7 -0
- package/vendor/directive-core/dist/scope/decomposed-refs.d.ts.map +1 -1
- package/vendor/directive-core/dist/scope/decomposed-refs.js +27 -10
- package/vendor/directive-core/dist/scope/decomposed-refs.js.map +1 -1
- package/vendor/directive-core/dist/scope/delivery-evidence.d.ts +218 -19
- package/vendor/directive-core/dist/scope/delivery-evidence.d.ts.map +1 -1
- package/vendor/directive-core/dist/scope/delivery-evidence.js +973 -107
- package/vendor/directive-core/dist/scope/delivery-evidence.js.map +1 -1
- package/vendor/directive-core/dist/scope/transition.d.ts +7 -1
- package/vendor/directive-core/dist/scope/transition.d.ts.map +1 -1
- package/vendor/directive-core/dist/scope/transition.js +45 -5
- package/vendor/directive-core/dist/scope/transition.js.map +1 -1
- package/vendor/directive-core/dist/scope-provenance/base-fence.d.ts +13 -3
- package/vendor/directive-core/dist/scope-provenance/base-fence.d.ts.map +1 -1
- package/vendor/directive-core/dist/scope-provenance/base-fence.js +25 -2
- package/vendor/directive-core/dist/scope-provenance/base-fence.js.map +1 -1
- package/vendor/directive-core/dist/scope-provenance/evaluate.d.ts +12 -0
- package/vendor/directive-core/dist/scope-provenance/evaluate.d.ts.map +1 -1
- package/vendor/directive-core/dist/scope-provenance/evaluate.js +41 -5
- package/vendor/directive-core/dist/scope-provenance/evaluate.js.map +1 -1
- package/vendor/directive-core/dist/scope-provenance/forge-approval.d.ts +106 -0
- package/vendor/directive-core/dist/scope-provenance/forge-approval.d.ts.map +1 -0
- package/vendor/directive-core/dist/scope-provenance/forge-approval.js +382 -0
- package/vendor/directive-core/dist/scope-provenance/forge-approval.js.map +1 -0
- package/vendor/directive-core/dist/session/orientation-compression.d.ts +18 -2
- package/vendor/directive-core/dist/session/orientation-compression.d.ts.map +1 -1
- package/vendor/directive-core/dist/session/orientation-compression.js +38 -5
- package/vendor/directive-core/dist/session/orientation-compression.js.map +1 -1
- package/vendor/directive-core/dist/session/session-start.d.ts +47 -2
- package/vendor/directive-core/dist/session/session-start.d.ts.map +1 -1
- package/vendor/directive-core/dist/session/session-start.js +149 -1
- package/vendor/directive-core/dist/session/session-start.js.map +1 -1
- package/vendor/directive-core/dist/swarm/approach1-babysitter.d.ts +18 -4
- package/vendor/directive-core/dist/swarm/approach1-babysitter.d.ts.map +1 -1
- package/vendor/directive-core/dist/swarm/approach1-babysitter.js +28 -4
- package/vendor/directive-core/dist/swarm/approach1-babysitter.js.map +1 -1
- package/vendor/directive-core/dist/swarm/finalize-cohort.d.ts +48 -0
- package/vendor/directive-core/dist/swarm/finalize-cohort.d.ts.map +1 -1
- package/vendor/directive-core/dist/swarm/finalize-cohort.js +175 -19
- package/vendor/directive-core/dist/swarm/finalize-cohort.js.map +1 -1
- package/vendor/directive-core/dist/swarm/finalize-owed.d.ts +18 -3
- package/vendor/directive-core/dist/swarm/finalize-owed.d.ts.map +1 -1
- package/vendor/directive-core/dist/swarm/finalize-owed.js +139 -32
- package/vendor/directive-core/dist/swarm/finalize-owed.js.map +1 -1
- package/vendor/directive-core/dist/swarm/launch.d.ts.map +1 -1
- package/vendor/directive-core/dist/swarm/launch.js +4 -8
- package/vendor/directive-core/dist/swarm/launch.js.map +1 -1
- package/vendor/directive-core/dist/swarm/worker-auth-assignment.d.ts +7 -0
- package/vendor/directive-core/dist/swarm/worker-auth-assignment.d.ts.map +1 -1
- package/vendor/directive-core/dist/swarm/worker-auth-assignment.js +16 -1
- package/vendor/directive-core/dist/swarm/worker-auth-assignment.js.map +1 -1
- package/vendor/directive-core/dist/task-surface/index.d.ts.map +1 -1
- package/vendor/directive-core/dist/task-surface/index.js +41 -0
- package/vendor/directive-core/dist/task-surface/index.js.map +1 -1
- package/vendor/directive-core/dist/triage/evaluate/worktrees.d.ts.map +1 -1
- package/vendor/directive-core/dist/triage/evaluate/worktrees.js +219 -60
- package/vendor/directive-core/dist/triage/evaluate/worktrees.js.map +1 -1
- package/vendor/directive-core/dist/vbrief-reconcile/types.d.ts +5 -1
- package/vendor/directive-core/dist/vbrief-reconcile/types.d.ts.map +1 -1
- package/vendor/directive-core/dist/vbrief-reconcile/umbrellas.d.ts.map +1 -1
- package/vendor/directive-core/dist/vbrief-reconcile/umbrellas.js +19 -7
- package/vendor/directive-core/dist/vbrief-reconcile/umbrellas.js.map +1 -1
|
@@ -12,13 +12,36 @@ import { policyColonInvocation } from "../policy/policy-invocation.js";
|
|
|
12
12
|
import { ENV_ALLOW_BOT_MERGE, evaluateAgentMerge, resolveHumanMergePolicy, } from "../policy/require-human-merge.js";
|
|
13
13
|
import { parseAllDeftStoryMarks } from "../pr-closing-keywords/main.js";
|
|
14
14
|
import { defaultRunGh, fetchClosingIssuesReferences } from "../pr-protected-issues/gh.js";
|
|
15
|
-
import { classifyStoredDeliveryDisposition, evidenceFromPrPayload, verifyDeliveryAncestry, } from "../scope/delivery-evidence.js";
|
|
15
|
+
import { classifyStoredDeliveryDisposition, defaultFetchPrChangedFiles, evidenceFromPrPayload, isDeliveryBriefLandPath, SPEC_DRIFT_LEDGER_REL, verifyDeliveryAncestry, } from "../scope/delivery-evidence.js";
|
|
16
16
|
import { materializeRetainedBrief, SOURCE_RECOVERY_REMEDIATION } from "./brief-transport.js";
|
|
17
17
|
import { completeCohort } from "./complete-cohort.js";
|
|
18
18
|
import { EXIT_CONFIG_ERROR, EXIT_GATE_FAILED, EXIT_INCOMPLETE, EXIT_OK } from "./constants.js";
|
|
19
19
|
import { cancelledBriefReferencesIssue, completedBriefReferencesIssue, resolveStories, terminalBriefReferencesIssue, } from "./launch.js";
|
|
20
20
|
import { evaluateLifecycleDiff, expectedLifecycleRels, parseStagedXbriefPaths, } from "./lifecycle-diff.js";
|
|
21
21
|
import { runText } from "./subprocess.js";
|
|
22
|
+
import { detectWorktreePm, installWorktreePackages, worktreeHasRuntimePin, } from "./worktree-pm-install.js";
|
|
23
|
+
/**
|
|
24
|
+
* Install packages on the lifecycle checkout before the completion sweep (#5882).
|
|
25
|
+
* Gated by the runtime pin, as linked-worktree prep gates it. npm/pnpm failure is
|
|
26
|
+
* `failed`; Yarn/Bun is diagnosed-only (`skipped-unsupported-pm`).
|
|
27
|
+
*/
|
|
28
|
+
export function installLifecycleCheckoutPackages(checkout, runner) {
|
|
29
|
+
if (!worktreeHasRuntimePin(checkout)) {
|
|
30
|
+
return { kind: "not-pinned" };
|
|
31
|
+
}
|
|
32
|
+
if (detectWorktreePm(checkout) === null) {
|
|
33
|
+
return { kind: "skipped-unsupported-pm" };
|
|
34
|
+
}
|
|
35
|
+
const installed = installWorktreePackages(checkout, { runner });
|
|
36
|
+
if (!installed.ok) {
|
|
37
|
+
return { kind: "failed", pm: installed.pm, message: installed.message };
|
|
38
|
+
}
|
|
39
|
+
return { kind: "installed", pm: installed.pm };
|
|
40
|
+
}
|
|
41
|
+
export function formatLifecycleInstallDisposition(disposition) {
|
|
42
|
+
const pm = "pm" in disposition && disposition.pm !== null ? ` pm=${disposition.pm}` : "";
|
|
43
|
+
return `lifecycle install disposition=${disposition.kind}${pm}`;
|
|
44
|
+
}
|
|
22
45
|
function splitCsv(values) {
|
|
23
46
|
const out = [];
|
|
24
47
|
for (const value of values) {
|
|
@@ -342,9 +365,13 @@ function collectProductPullRequestOrigins(projectRoot, prNumbers) {
|
|
|
342
365
|
/**
|
|
343
366
|
* PR-body `deft-story: N` may bind only when this PR is delivery for N (#4864 / #4919):
|
|
344
367
|
* TIP_NONTERMINAL or completed brief for N with metadata.productPullRequest === this PR.
|
|
345
|
-
* Absent stamp never binds (unrelated completed issue must not close).
|
|
368
|
+
* Absent stamp never binds on its own (unrelated completed issue must not close).
|
|
369
|
+
* Also reports a stamp naming another PR and whether an unstamped active brief
|
|
370
|
+
* exists, for the operator-story bind at the call site (#6212).
|
|
346
371
|
*/
|
|
347
372
|
function deftStoryMarkBindsDelivery(projectRoot, issue, prNumber) {
|
|
373
|
+
let otherPr = null;
|
|
374
|
+
let unstampedActive = false;
|
|
348
375
|
for (const folder of MARK_READER_FOLDERS) {
|
|
349
376
|
const dir = resolve(projectRoot, folder);
|
|
350
377
|
if (!existsSync(dir)) {
|
|
@@ -367,8 +394,15 @@ function deftStoryMarkBindsDelivery(projectRoot, issue, prNumber) {
|
|
|
367
394
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
368
395
|
continue;
|
|
369
396
|
}
|
|
370
|
-
|
|
371
|
-
|
|
397
|
+
const stamped = productPullRequestFromPlan(plan);
|
|
398
|
+
if (stamped === prNumber) {
|
|
399
|
+
return { stampMatch: true, otherPr: null, unstampedActive: false };
|
|
400
|
+
}
|
|
401
|
+
if (stamped !== null) {
|
|
402
|
+
otherPr ??= stamped;
|
|
403
|
+
}
|
|
404
|
+
else if (folder.endsWith("/active")) {
|
|
405
|
+
unstampedActive = true;
|
|
372
406
|
}
|
|
373
407
|
}
|
|
374
408
|
catch {
|
|
@@ -376,7 +410,16 @@ function deftStoryMarkBindsDelivery(projectRoot, issue, prNumber) {
|
|
|
376
410
|
}
|
|
377
411
|
}
|
|
378
412
|
}
|
|
379
|
-
return false;
|
|
413
|
+
return { stampMatch: false, otherPr, unstampedActive };
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Lifecycle-shaped PR (#6212): every changed path is in the delivery-brief-land
|
|
417
|
+
* family (`isDeliveryBriefLandPath`, shared with the delivery gate, #680 F1).
|
|
418
|
+
* Activation and lifecycle PRs carry `deft-story` marks too, so they never bind
|
|
419
|
+
* as delivery. An empty list stays lifecycle-shaped, so it never binds either.
|
|
420
|
+
*/
|
|
421
|
+
export function isLifecycleShapedPr(files) {
|
|
422
|
+
return files.every((f) => isDeliveryBriefLandPath(f.filename));
|
|
380
423
|
}
|
|
381
424
|
/**
|
|
382
425
|
* Persist productPullRequest on an active brief before leftover-complete so the
|
|
@@ -760,6 +803,11 @@ function derivedLifecycleRelsFromSweep(projectRoot, storyPaths, sweep) {
|
|
|
760
803
|
out.push(posixProjectRel(projectRoot, resolvedPath));
|
|
761
804
|
};
|
|
762
805
|
if (sweep !== null) {
|
|
806
|
+
// scope:complete writes the spec-drift ledger as verb output; it travels in
|
|
807
|
+
// the same lifecycle commit as the completed briefs (#680 P5 / #6231).
|
|
808
|
+
if (sweep.stories.some((rec) => rec.ok)) {
|
|
809
|
+
out.push(SPEC_DRIFT_LEDGER_REL);
|
|
810
|
+
}
|
|
763
811
|
for (const rec of [...sweep.stories, ...sweep.parents]) {
|
|
764
812
|
if (!rec.ok || rec.path.trim().length === 0) {
|
|
765
813
|
continue;
|
|
@@ -1167,6 +1215,8 @@ function waitForLifecycleLand(args) {
|
|
|
1167
1215
|
export function finalizeCohort(args) {
|
|
1168
1216
|
const projectRoot = resolve(args.projectRoot ?? process.cwd());
|
|
1169
1217
|
const runGh = args.runGh ?? defaultRunGh;
|
|
1218
|
+
const fetchPrChangedFiles = args.fetchPrChangedFiles ??
|
|
1219
|
+
((prNumber, repository) => defaultFetchPrChangedFiles(prNumber, repository, runGh));
|
|
1170
1220
|
const runGit = args.runGit ?? runText;
|
|
1171
1221
|
const gitRunner = asGitRunner(runGit);
|
|
1172
1222
|
const prNumbers = [...(args.prNumbers ?? [])].sort((a, b) => a - b);
|
|
@@ -1356,7 +1406,17 @@ export function finalizeCohort(args) {
|
|
|
1356
1406
|
const validatedPrs = [];
|
|
1357
1407
|
/** Full-story close intent from PR-body `deft-story: N` (#4864). Not Tracking/Refs scrape. */
|
|
1358
1408
|
const fullStoryCloseIssues = new Set();
|
|
1409
|
+
/**
|
|
1410
|
+
* A failed changed-file lookup for a mark is a real forge error: it still stops
|
|
1411
|
+
* the stamp and sweep for the whole batch in every write mode (#6212).
|
|
1412
|
+
*/
|
|
1413
|
+
let deftStoryMarkLookupFailed = false;
|
|
1414
|
+
/** Refused marks: non-fatal; only that story stays pending (#680 P3). */
|
|
1415
|
+
const refusedMarks = [];
|
|
1359
1416
|
let closingLookupFailed = false;
|
|
1417
|
+
// Operator --stories issues: their deft-story marks may bind before the sweep stamp (#6212).
|
|
1418
|
+
const operatorResolved = storyTokens.length > 0 ? resolveStories(projectRoot, storyTokens) : null;
|
|
1419
|
+
const operatorStoryIssues = new Set(collectOriginIssueNumbers(operatorResolved?.resolved.map((story) => story.path) ?? [], storyTokens));
|
|
1360
1420
|
if (prNumbers.length > 0 && errors.length === 0) {
|
|
1361
1421
|
if (repo === null || repo.length === 0) {
|
|
1362
1422
|
errors.push("--repo OWNER/REPO is required when --pr is supplied (or set $GH_REPO).");
|
|
@@ -1408,11 +1468,47 @@ export function finalizeCohort(args) {
|
|
|
1408
1468
|
evidenceByIssue.set(issue, prEvidence);
|
|
1409
1469
|
}
|
|
1410
1470
|
const prBody = typeof snap.payload.body === "string" ? snap.payload.body : "";
|
|
1471
|
+
let prFiles;
|
|
1411
1472
|
for (const issue of parseAllDeftStoryMarks(prBody)) {
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1473
|
+
const mark = deftStoryMarkBindsDelivery(projectRoot, issue, prNumber);
|
|
1474
|
+
let bound = mark.stampMatch;
|
|
1475
|
+
let lookupFailed = false;
|
|
1476
|
+
let reason = `need active or completed brief #${String(issue)} with ` +
|
|
1477
|
+
`metadata.productPullRequest=${String(prNumber)}`;
|
|
1478
|
+
// Operator named N in --stories (#6212): an unstamped active brief binds this
|
|
1479
|
+
// validated PR, and the sweep stamps it. A lifecycle-shaped PR never binds.
|
|
1480
|
+
if (!bound && operatorStoryIssues.has(issue)) {
|
|
1481
|
+
if (mark.otherPr !== null) {
|
|
1482
|
+
reason =
|
|
1483
|
+
`brief #${String(issue)} is already stamped ` +
|
|
1484
|
+
`metadata.productPullRequest=${String(mark.otherPr)}`;
|
|
1485
|
+
}
|
|
1486
|
+
else if (mark.unstampedActive) {
|
|
1487
|
+
if (prFiles === undefined) {
|
|
1488
|
+
prFiles = fetchPrChangedFiles(prNumber, repo);
|
|
1489
|
+
}
|
|
1490
|
+
if (prFiles === null) {
|
|
1491
|
+
reason = `changed-file lookup for PR #${String(prNumber)} failed`;
|
|
1492
|
+
lookupFailed = true;
|
|
1493
|
+
}
|
|
1494
|
+
else if (isLifecycleShapedPr(prFiles)) {
|
|
1495
|
+
reason = `PR #${String(prNumber)} changes only lifecycle paths: briefs, CHANGELOG.md, spec-drift ledger`;
|
|
1496
|
+
}
|
|
1497
|
+
else {
|
|
1498
|
+
bound = true;
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
if (!bound) {
|
|
1503
|
+
const detail = `#${String(issue)}: deft-story mark on PR #${String(prNumber)} does not bind PR delivery ` +
|
|
1504
|
+
`(${reason}) (#4864).`;
|
|
1505
|
+
if (lookupFailed) {
|
|
1506
|
+
deftStoryMarkLookupFailed = true;
|
|
1507
|
+
errors.push(detail);
|
|
1508
|
+
}
|
|
1509
|
+
else {
|
|
1510
|
+
refusedMarks.push({ issue, pr: prNumber, detail });
|
|
1511
|
+
}
|
|
1416
1512
|
continue;
|
|
1417
1513
|
}
|
|
1418
1514
|
fullStoryCloseIssues.add(issue);
|
|
@@ -1432,7 +1528,10 @@ export function finalizeCohort(args) {
|
|
|
1432
1528
|
evidenceByIssue.set(issue, bound);
|
|
1433
1529
|
}
|
|
1434
1530
|
}
|
|
1435
|
-
if (storyTokens.length === 0 &&
|
|
1531
|
+
if (storyTokens.length === 0 &&
|
|
1532
|
+
closingIssues.size === 0 &&
|
|
1533
|
+
fullStoryCloseIssues.size === 0 &&
|
|
1534
|
+
refusedMarks.length === 0) {
|
|
1436
1535
|
errors.push("empty cohort: pass --pr <numbers> and/or --stories <ids|paths> " +
|
|
1437
1536
|
"(or record full-story close intent via deft-story: N / productPullRequest).");
|
|
1438
1537
|
}
|
|
@@ -1448,8 +1547,8 @@ export function finalizeCohort(args) {
|
|
|
1448
1547
|
};
|
|
1449
1548
|
// Operator-supplied story tokens keep the hard-error contract: an explicit
|
|
1450
1549
|
// --stories token that does not resolve to an active brief is a real error.
|
|
1451
|
-
if (
|
|
1452
|
-
const resolved =
|
|
1550
|
+
if (operatorResolved !== null) {
|
|
1551
|
+
const resolved = operatorResolved;
|
|
1453
1552
|
for (const story of resolved.resolved) {
|
|
1454
1553
|
addStory(story.path);
|
|
1455
1554
|
}
|
|
@@ -1550,9 +1649,32 @@ export function finalizeCohort(args) {
|
|
|
1550
1649
|
errors.push(`#${issue}: full-story close intent recorded but no active or completed brief references this issue (#4864).`);
|
|
1551
1650
|
}
|
|
1552
1651
|
}
|
|
1652
|
+
// Refused marks (#680 P3): an issue no other PR bound stays pending. Its story
|
|
1653
|
+
// leaves the batch before stamp/sweep (#6212: never single-PR defaultEvidence),
|
|
1654
|
+
// and its origin is never closed. The other stories go on.
|
|
1655
|
+
// A stray mark with no brief for its issue warns only; it leaves nothing pending.
|
|
1656
|
+
// Selected story paths count too, so a nested brief path is never missed.
|
|
1657
|
+
const pendingMarkIssues = new Set(refusedMarks
|
|
1658
|
+
.map((m) => m.issue)
|
|
1659
|
+
.filter((issue) => !fullStoryCloseIssues.has(issue) &&
|
|
1660
|
+
!closingIssues.has(issue) &&
|
|
1661
|
+
(storyPaths.some((p) => githubIssueFromBrief(p) === issue) ||
|
|
1662
|
+
localBriefReferencesIssue(projectRoot, issue))));
|
|
1663
|
+
for (let i = storyPaths.length - 1; i >= 0; i -= 1) {
|
|
1664
|
+
const origin = githubIssueFromBrief(storyPaths[i] ?? "");
|
|
1665
|
+
if (origin !== null && pendingMarkIssues.has(origin)) {
|
|
1666
|
+
storyPaths.splice(i, 1);
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
for (const mark of refusedMarks) {
|
|
1670
|
+
warnings.push(pendingMarkIssues.has(mark.issue)
|
|
1671
|
+
? `${mark.detail} Story left pending, not stamped or swept (#680).`
|
|
1672
|
+
: mark.detail);
|
|
1673
|
+
}
|
|
1674
|
+
const markPending = pendingMarkIssues.size > 0;
|
|
1553
1675
|
const originIssues = collectOriginIssueNumbers(storyPaths, storyTokens, [
|
|
1554
1676
|
...fullStoryCloseIssues,
|
|
1555
|
-
]);
|
|
1677
|
+
]).filter((issue) => !pendingMarkIssues.has(issue));
|
|
1556
1678
|
if (storyPaths.length === 0) {
|
|
1557
1679
|
if (errors.length === 0 && originIssues.length > 0) {
|
|
1558
1680
|
const originClose = closeOriginsAfterLeftoverComplete({
|
|
@@ -1571,7 +1693,7 @@ export function finalizeCohort(args) {
|
|
|
1571
1693
|
? originClosePendingState(originClose.pendingIssues, deliveryBranch)
|
|
1572
1694
|
: null;
|
|
1573
1695
|
const originFailed = errors.length > 0;
|
|
1574
|
-
const originPending = !originFailed && pending !== null;
|
|
1696
|
+
const originPending = !originFailed && (pending !== null || markPending);
|
|
1575
1697
|
return buildResponse({
|
|
1576
1698
|
projectRoot,
|
|
1577
1699
|
dryRun,
|
|
@@ -1589,6 +1711,7 @@ export function finalizeCohort(args) {
|
|
|
1589
1711
|
errors,
|
|
1590
1712
|
warnings,
|
|
1591
1713
|
pending,
|
|
1714
|
+
refusedMarks,
|
|
1592
1715
|
ok: !originFailed && !originPending,
|
|
1593
1716
|
emitJson: args.emitJson ?? false,
|
|
1594
1717
|
exitCode: originFailed ? EXIT_GATE_FAILED : originPending ? EXIT_INCOMPLETE : EXIT_OK,
|
|
@@ -1614,10 +1737,13 @@ export function finalizeCohort(args) {
|
|
|
1614
1737
|
errors,
|
|
1615
1738
|
warnings,
|
|
1616
1739
|
pending: null,
|
|
1617
|
-
|
|
1740
|
+
refusedMarks,
|
|
1741
|
+
ok: cleanNoop && !markPending,
|
|
1618
1742
|
emitJson: args.emitJson ?? false,
|
|
1619
1743
|
exitCode: cleanNoop
|
|
1620
|
-
?
|
|
1744
|
+
? markPending
|
|
1745
|
+
? EXIT_INCOMPLETE
|
|
1746
|
+
: EXIT_OK
|
|
1621
1747
|
: errors.some((e) => e.includes("not merged") || e.includes("delivery"))
|
|
1622
1748
|
? EXIT_GATE_FAILED
|
|
1623
1749
|
: EXIT_CONFIG_ERROR,
|
|
@@ -1642,6 +1768,7 @@ export function finalizeCohort(args) {
|
|
|
1642
1768
|
errors,
|
|
1643
1769
|
warnings,
|
|
1644
1770
|
pending: null,
|
|
1771
|
+
refusedMarks,
|
|
1645
1772
|
ok: false,
|
|
1646
1773
|
emitJson: args.emitJson ?? false,
|
|
1647
1774
|
exitCode: EXIT_GATE_FAILED,
|
|
@@ -1705,6 +1832,7 @@ export function finalizeCohort(args) {
|
|
|
1705
1832
|
let pendingOrigin = null;
|
|
1706
1833
|
let createdSweepBranch = null;
|
|
1707
1834
|
let lifecycle = null;
|
|
1835
|
+
let lifecycleInstall = null;
|
|
1708
1836
|
// Closure assignment is invisible to finally control flow, so a bare let stays null.
|
|
1709
1837
|
const held = { outcome: null };
|
|
1710
1838
|
const respond = (partial) => {
|
|
@@ -1721,6 +1849,7 @@ export function finalizeCohort(args) {
|
|
|
1721
1849
|
errors,
|
|
1722
1850
|
warnings,
|
|
1723
1851
|
pending: partial.pending ?? null,
|
|
1852
|
+
refusedMarks,
|
|
1724
1853
|
emitJson: args.emitJson ?? false,
|
|
1725
1854
|
...partial,
|
|
1726
1855
|
});
|
|
@@ -1768,11 +1897,32 @@ export function finalizeCohort(args) {
|
|
|
1768
1897
|
if (branchResult.branch === branchName) {
|
|
1769
1898
|
createdSweepBranch = branchName;
|
|
1770
1899
|
}
|
|
1900
|
+
// Sweep is required: install packages so Taskfile includes that
|
|
1901
|
+
// resolve through node_modules exist on this checkout (#5882).
|
|
1902
|
+
lifecycleInstall = installLifecycleCheckoutPackages(prepared.checkout, args.pmRunner);
|
|
1903
|
+
if (lifecycleInstall.kind === "failed") {
|
|
1904
|
+
errors.push(`environment/setup: ${formatLifecycleInstallDisposition(lifecycleInstall)}: ` +
|
|
1905
|
+
`${oneLine(lifecycleInstall.message)}. The completion sweep did not run; ` +
|
|
1906
|
+
"this is not an acceptance commands-failed result. Fix the package install " +
|
|
1907
|
+
`on origin/${deliveryBranch}, then re-run swarm:finalize-cohort.`);
|
|
1908
|
+
skipSweep = true;
|
|
1909
|
+
}
|
|
1910
|
+
else if (lifecycleInstall.kind === "skipped-unsupported-pm") {
|
|
1911
|
+
warnings.push(`${formatLifecycleInstallDisposition(lifecycleInstall)}: Yarn/Bun lifecycle ` +
|
|
1912
|
+
"checkout was not installed (AC5.7). Taskfile includes that resolve through " +
|
|
1913
|
+
"node_modules can be missing during the completion sweep.");
|
|
1914
|
+
}
|
|
1771
1915
|
}
|
|
1772
1916
|
}
|
|
1773
1917
|
}
|
|
1774
1918
|
}
|
|
1775
1919
|
}
|
|
1920
|
+
// --no-commit skips the error stop above, but a mark whose lookup failed must
|
|
1921
|
+
// never let the single-PR default evidence stamp that PR; a retry would then
|
|
1922
|
+
// bind it (#6212). Refused marks already left the batch above (#680 P3).
|
|
1923
|
+
if (!dryRun && deftStoryMarkLookupFailed) {
|
|
1924
|
+
skipSweep = true;
|
|
1925
|
+
}
|
|
1776
1926
|
let sweep = null;
|
|
1777
1927
|
if (!skipSweep) {
|
|
1778
1928
|
// Stamp on the sweep root (checkout or project) so leftover-complete preserves bind (#4864).
|
|
@@ -1824,7 +1974,9 @@ export function finalizeCohort(args) {
|
|
|
1824
1974
|
});
|
|
1825
1975
|
sweep = sweepResult.sweep;
|
|
1826
1976
|
if (sweepResult.exitCode !== 0) {
|
|
1827
|
-
errors.push(
|
|
1977
|
+
errors.push(lifecycleInstall === null
|
|
1978
|
+
? "cohort completion sweep failed."
|
|
1979
|
+
: `cohort completion sweep failed (${formatLifecycleInstallDisposition(lifecycleInstall)}).`);
|
|
1828
1980
|
if (sweep !== null) {
|
|
1829
1981
|
errors.push(...sweepDetailLines(sweep));
|
|
1830
1982
|
}
|
|
@@ -1977,7 +2129,7 @@ export function finalizeCohort(args) {
|
|
|
1977
2129
|
pendingOrigin = originClosePendingState(originIssues, deliveryBranch);
|
|
1978
2130
|
}
|
|
1979
2131
|
const failed = errors.length > 0;
|
|
1980
|
-
const incomplete = !failed && pendingOrigin !== null;
|
|
2132
|
+
const incomplete = !failed && (pendingOrigin !== null || markPending);
|
|
1981
2133
|
return respond({
|
|
1982
2134
|
sweep,
|
|
1983
2135
|
commitSha,
|
|
@@ -2025,6 +2177,7 @@ function withReportedFailure(previous, error, emitJson) {
|
|
|
2025
2177
|
errors: [...result.errors, error],
|
|
2026
2178
|
warnings: [...result.warnings],
|
|
2027
2179
|
pending: result.pending,
|
|
2180
|
+
refusedMarks: result.refused_marks ?? [],
|
|
2028
2181
|
ok: false,
|
|
2029
2182
|
emitJson,
|
|
2030
2183
|
exitCode: EXIT_GATE_FAILED,
|
|
@@ -2048,6 +2201,7 @@ function buildResponse(input) {
|
|
|
2048
2201
|
errors: input.errors,
|
|
2049
2202
|
warnings: input.warnings,
|
|
2050
2203
|
pending: input.pending,
|
|
2204
|
+
refused_marks: input.refusedMarks ?? [],
|
|
2051
2205
|
ok: input.ok,
|
|
2052
2206
|
};
|
|
2053
2207
|
if (input.emitJson) {
|
|
@@ -2126,7 +2280,9 @@ function buildResponse(input) {
|
|
|
2126
2280
|
? `Result: FINALIZE CLEAN -- cohort briefs swept to completed/.${skipNote}`
|
|
2127
2281
|
: input.pending !== null
|
|
2128
2282
|
? "Result: FINALIZE INCOMPLETE -- origin-close pending."
|
|
2129
|
-
:
|
|
2283
|
+
: input.errors.length === 0 && (input.refusedMarks ?? []).length > 0
|
|
2284
|
+
? "Result: FINALIZE INCOMPLETE -- refused deft-story mark(s); see warnings above."
|
|
2285
|
+
: "Result: FINALIZE INCOMPLETE -- see errors above.");
|
|
2130
2286
|
return {
|
|
2131
2287
|
exitCode: input.exitCode,
|
|
2132
2288
|
stdout: `${lines.join("\n")}\n`,
|