@deftai/directive-core 0.90.0 → 0.92.0
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/dist/cache/fetch.js +3 -2
- package/dist/cache/io.d.ts +13 -2
- package/dist/cache/io.js +36 -7
- package/dist/cache/operations.js +5 -3
- package/dist/doctor/main.js +37 -0
- package/dist/doctor/openclaw-skills.d.ts +42 -8
- package/dist/doctor/openclaw-skills.js +281 -38
- package/dist/eval/health.d.ts +10 -1
- package/dist/eval/health.js +16 -16
- package/dist/hooks/dispatcher.d.ts +6 -1
- package/dist/hooks/dispatcher.js +30 -0
- package/dist/init-deposit/gitignore.js +3 -0
- package/dist/init-deposit/hygiene.d.ts +43 -1
- package/dist/init-deposit/hygiene.js +112 -7
- package/dist/init-deposit/init-deposit.d.ts +5 -0
- package/dist/init-deposit/init-deposit.js +37 -2
- package/dist/init-deposit/scaffold.js +3 -1
- package/dist/intake/github-auth-modes.d.ts +1 -1
- package/dist/intake/github-auth-modes.js +25 -3
- package/dist/intake/issue-ingest-cli.js +13 -0
- package/dist/intake/reconcile-issues-cli.js +13 -0
- package/dist/orchestration/probe-session.d.ts +5 -1
- package/dist/orchestration/probe-session.js +24 -13
- package/dist/platform/agents-md.js +1 -1
- package/dist/policy/deft-directive-disable.d.ts +86 -0
- package/dist/policy/deft-directive-disable.js +167 -0
- package/dist/policy/delivery-branch.d.ts +33 -0
- package/dist/policy/delivery-branch.js +124 -0
- package/dist/policy/index.d.ts +2 -0
- package/dist/policy/index.js +17 -1
- package/dist/policy/plan-extensions.d.ts +10 -0
- package/dist/policy/plan-extensions.js +16 -0
- package/dist/pr-closing-keywords/detect.d.ts +13 -0
- package/dist/pr-closing-keywords/detect.js +73 -8
- package/dist/pr-closing-keywords/index.d.ts +2 -2
- package/dist/pr-closing-keywords/index.js +1 -1
- package/dist/pr-closing-keywords/main.js +119 -90
- package/dist/pr-closing-keywords/types.d.ts +10 -0
- package/dist/render/constants.d.ts +16 -0
- package/dist/render/constants.js +16 -1
- package/dist/render/export-spec.d.ts +7 -1
- package/dist/render/export-spec.js +51 -5
- package/dist/render/index.d.ts +3 -2
- package/dist/render/index.js +2 -2
- package/dist/render/roadmap-render.js +164 -60
- package/dist/render/scope-outlook.d.ts +5 -0
- package/dist/render/scope-outlook.js +3 -0
- package/dist/render/spec-render.d.ts +28 -2
- package/dist/render/spec-render.js +111 -14
- package/dist/render/text-utils.d.ts +6 -0
- package/dist/render/text-utils.js +23 -0
- package/dist/scm/binary.d.ts +3 -0
- package/dist/scm/binary.js +10 -2
- package/dist/scm/call.d.ts +5 -0
- package/dist/scm/call.js +5 -0
- package/dist/scm/index.d.ts +1 -0
- package/dist/scm/index.js +1 -0
- package/dist/scm/main.d.ts +5 -0
- package/dist/scm/main.js +27 -0
- package/dist/scm/readiness-cli.d.ts +24 -0
- package/dist/scm/readiness-cli.js +85 -0
- package/dist/scm/readiness.d.ts +104 -0
- package/dist/scm/readiness.js +421 -0
- package/dist/scope/batch-promote.d.ts +26 -0
- package/dist/scope/batch-promote.js +167 -0
- package/dist/scope/brief-io.d.ts +3 -1
- package/dist/scope/brief-io.js +5 -3
- package/dist/scope/delivery-evidence.d.ts +112 -0
- package/dist/scope/delivery-evidence.js +419 -0
- package/dist/scope/index.d.ts +2 -0
- package/dist/scope/index.js +2 -0
- package/dist/scope/main.d.ts +7 -0
- package/dist/scope/main.js +155 -9
- package/dist/scope/registry-artifact-sync.js +4 -1
- package/dist/scope/transition.d.ts +11 -1
- package/dist/scope/transition.js +30 -4
- package/dist/scope/wip-cap-check.d.ts +6 -0
- package/dist/scope/wip-cap-check.js +14 -2
- package/dist/session/ritual-sentinel.js +21 -12
- package/dist/session/session-start-hook.d.ts +3 -0
- package/dist/session/session-start-hook.js +21 -0
- package/dist/session/session-start.d.ts +7 -0
- package/dist/session/session-start.js +85 -0
- package/dist/swarm/complete-cohort.d.ts +20 -1
- package/dist/swarm/complete-cohort.js +56 -9
- package/dist/swarm/finalize-cohort-cli.js +9 -0
- package/dist/swarm/finalize-cohort.d.ts +8 -0
- package/dist/swarm/finalize-cohort.js +217 -21
- package/dist/triage/welcome/index.d.ts +1 -1
- package/dist/triage/welcome/index.js +1 -1
- package/dist/triage/welcome/onboard.js +7 -1
- package/dist/triage/welcome/prior-state.d.ts +7 -0
- package/dist/triage/welcome/prior-state.js +16 -2
- package/dist/triage/welcome/writers.d.ts +10 -0
- package/dist/triage/welcome/writers.js +72 -2
- package/dist/user-config/experimental-rules.d.ts +43 -0
- package/dist/user-config/experimental-rules.js +162 -0
- package/dist/user-config/index.d.ts +1 -0
- package/dist/user-config/index.js +1 -0
- package/dist/verify-source/contained-writes.js +1 -1
- package/package.json +3 -3
|
@@ -3,12 +3,14 @@ import { runningInsideDeftRepo } from "../doctor/paths.js";
|
|
|
3
3
|
import { emitSessionEvalReadback } from "../eval/readback.js";
|
|
4
4
|
import { MIGRATE_COMPLETION_NUDGE, shouldEmitMigrateNudge } from "../init-deposit/migrate.js";
|
|
5
5
|
import { detectEnvironmentContext, environmentContextToDict, formatEnvironmentContext, } from "../platform/shell-context.js";
|
|
6
|
+
import { DEFT_DIRECTIVE_DISABLE_FLAG_NAME, DEFT_DIRECTIVE_DISABLE_STATUS, detectDeftDirectiveDisable, formatDeftDirectiveDisableMessage, isDeftDirectiveDisableActive, } from "../policy/deft-directive-disable.js";
|
|
6
7
|
import { disclosureLine } from "../policy/disclosure.js";
|
|
7
8
|
import { detectNoDeftDirective, NO_DEFT_DIRECTIVE_DISABLED_MESSAGE, NO_DEFT_DIRECTIVE_FLAG_NAME, NO_DEFT_DIRECTIVE_INCONSISTENT_MESSAGE, NO_DEFT_DIRECTIVE_INCONSISTENT_POLICY, } from "../policy/no-deft-directive.js";
|
|
8
9
|
import { humanMergeDisclosureLine, resolveHumanMergePolicy, } from "../policy/require-human-merge.js";
|
|
9
10
|
import { resolvePolicy } from "../policy/resolve.js";
|
|
10
11
|
import { maybeFormatProductSignalConsentPrompt } from "../product-signal/consent-prompt.js";
|
|
11
12
|
import { formatFrameworkCommand } from "../render/framework-commands.js";
|
|
13
|
+
import { formatScmReadinessLines, probeScmReadiness, scmReadinessToDict, } from "../scm/readiness.js";
|
|
12
14
|
import { maybeRunStalenessTickler } from "../staleness-tickler/run.js";
|
|
13
15
|
import { runDefaultMode } from "../triage/welcome/default-mode.js";
|
|
14
16
|
import { resolveUserMdPath } from "../user-config/resolve-user-md.js";
|
|
@@ -292,6 +294,44 @@ export function defaultBranchSync(projectRoot, runGit = defaultGitRunner) {
|
|
|
292
294
|
}
|
|
293
295
|
return { branch, upstream, ahead, behind, warning };
|
|
294
296
|
}
|
|
297
|
+
/**
|
|
298
|
+
* Resolve SCM readiness for session orientation (#2275).
|
|
299
|
+
* Shallow by default (PATH + auth status); deep when optional network is on.
|
|
300
|
+
* Never throws — session:start must not hard-block on SCM absence.
|
|
301
|
+
*/
|
|
302
|
+
function resolveSessionScmReadiness(options, allowOptionalNetwork) {
|
|
303
|
+
const probe = options.probeScm ?? probeScmReadiness;
|
|
304
|
+
try {
|
|
305
|
+
return probe({
|
|
306
|
+
depth: allowOptionalNetwork ? "deep" : "shallow",
|
|
307
|
+
env: options.env,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
catch {
|
|
311
|
+
return {
|
|
312
|
+
ready: false,
|
|
313
|
+
binary: null,
|
|
314
|
+
binaryPath: null,
|
|
315
|
+
authState: "unknown",
|
|
316
|
+
githubAuthMode: "host-gh",
|
|
317
|
+
runtimeMode: "local-unsandboxed",
|
|
318
|
+
injectedTokenPresent: false,
|
|
319
|
+
depth: allowOptionalNetwork ? "deep" : "shallow",
|
|
320
|
+
detail: "SCM readiness probe failed unexpectedly; treating SCM-dependent gates as skipped",
|
|
321
|
+
remediation: null,
|
|
322
|
+
skippedGates: [
|
|
323
|
+
"triage:queue",
|
|
324
|
+
"issue:ingest",
|
|
325
|
+
"pr:*",
|
|
326
|
+
"reconcile:issues",
|
|
327
|
+
"cache:fetch-all",
|
|
328
|
+
"scm:*",
|
|
329
|
+
],
|
|
330
|
+
login: null,
|
|
331
|
+
failureKind: "probe_error",
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
}
|
|
295
335
|
function runReadOnlySessionStart(projectRoot, options, instant, environment) {
|
|
296
336
|
const lines = [];
|
|
297
337
|
const resolveUserMd = options.resolveUserMd ?? ((root) => resolveUserMdPath({ projectRoot: root }));
|
|
@@ -301,9 +341,12 @@ function runReadOnlySessionStart(projectRoot, options, instant, environment) {
|
|
|
301
341
|
const userMdLine = userMd.found
|
|
302
342
|
? `USER.md resolved (${userMd.rung}): ${safePath}`
|
|
303
343
|
: safeDiagnostic;
|
|
344
|
+
// #2275: report SCM availability even on read-only alignment (shallow; no network).
|
|
345
|
+
const scm = resolveSessionScmReadiness(options, false);
|
|
304
346
|
lines.push(READ_ONLY_ALIGNMENT_MESSAGE);
|
|
305
347
|
lines.push(userMdLine);
|
|
306
348
|
lines.push(formatEnvironmentContext(environment));
|
|
349
|
+
lines.push(...formatScmReadinessLines(scm));
|
|
307
350
|
const resultPayload = {
|
|
308
351
|
ready: true,
|
|
309
352
|
exit_code: 0,
|
|
@@ -324,6 +367,7 @@ function runReadOnlySessionStart(projectRoot, options, instant, environment) {
|
|
|
324
367
|
diagnostic: userMd.diagnostic,
|
|
325
368
|
},
|
|
326
369
|
environment: environmentContextToDict(environment),
|
|
370
|
+
scm: scmReadinessToDict(scm),
|
|
327
371
|
message: READ_ONLY_RESULT_MESSAGE,
|
|
328
372
|
};
|
|
329
373
|
return { code: 0, payload: resultPayload, lines };
|
|
@@ -364,10 +408,13 @@ function runSessionRearm(projectRoot, options, instant, environment) {
|
|
|
364
408
|
? `USER.md resolved (${userMd.rung}): ${safePath}`
|
|
365
409
|
: safeDiagnostic;
|
|
366
410
|
const alignmentMessage = `${READ_ONLY_ALIGNMENT_MESSAGE} ${userMdLine}`;
|
|
411
|
+
// #2275: re-arm still reports SCM state (shallow; no network).
|
|
412
|
+
const scm = resolveSessionScmReadiness(options, false);
|
|
367
413
|
const lines = [
|
|
368
414
|
READ_ONLY_ALIGNMENT_MESSAGE,
|
|
369
415
|
userMdLine,
|
|
370
416
|
formatEnvironmentContext(environment),
|
|
417
|
+
...formatScmReadinessLines(scm),
|
|
371
418
|
REARM_SKIPPED_FAT_PATH_MESSAGE,
|
|
372
419
|
];
|
|
373
420
|
// Light branch-policy disclosure (local only) so re-arm still surfaces policy state.
|
|
@@ -484,6 +531,7 @@ function runSessionRearm(projectRoot, options, instant, environment) {
|
|
|
484
531
|
diagnostic: userMd.diagnostic,
|
|
485
532
|
},
|
|
486
533
|
environment: environmentContextToDict(environment),
|
|
534
|
+
scm: scmReadinessToDict(scm),
|
|
487
535
|
message: code === 0 ? "session ritual re-armed" : "session ritual re-arm failed",
|
|
488
536
|
},
|
|
489
537
|
lines,
|
|
@@ -496,6 +544,36 @@ export function runSessionStart(projectRoot, options = {}) {
|
|
|
496
544
|
const runGit = options.runGit ?? defaultGitRunner;
|
|
497
545
|
const environment = (options.probeEnvironment ?? detectEnvironmentContext)();
|
|
498
546
|
const ceremonyTier = options.ceremonyTier ?? COLD_CEREMONY_TIER;
|
|
547
|
+
// #3039: local (untracked) test kill-switch — skip ritual write; deposit may remain.
|
|
548
|
+
// Recovery requires delete + new session. Tracked flags do not short-circuit.
|
|
549
|
+
if (isDeftDirectiveDisableActive(projectRoot)) {
|
|
550
|
+
const killSwitch = detectDeftDirectiveDisable(projectRoot);
|
|
551
|
+
const optOutAlso = detectNoDeftDirective(projectRoot);
|
|
552
|
+
const message = formatDeftDirectiveDisableMessage({
|
|
553
|
+
permanentOptOutAlsoPresent: optOutAlso.present,
|
|
554
|
+
trackedByGit: false,
|
|
555
|
+
});
|
|
556
|
+
const lines = message.split("\n");
|
|
557
|
+
return {
|
|
558
|
+
code: 0,
|
|
559
|
+
payload: {
|
|
560
|
+
ready: false,
|
|
561
|
+
exit_code: 0,
|
|
562
|
+
disabled: true,
|
|
563
|
+
disabled_via: DEFT_DIRECTIVE_DISABLE_FLAG_NAME,
|
|
564
|
+
status: DEFT_DIRECTIVE_DISABLE_STATUS,
|
|
565
|
+
kill_switch: true,
|
|
566
|
+
inconsistent: false,
|
|
567
|
+
deposit_present: killSwitch.depositPresent,
|
|
568
|
+
tracked_by_git: false,
|
|
569
|
+
permanent_opt_out_also_present: optOutAlso.present,
|
|
570
|
+
posture,
|
|
571
|
+
environment: environmentContextToDict(environment),
|
|
572
|
+
message,
|
|
573
|
+
},
|
|
574
|
+
lines,
|
|
575
|
+
};
|
|
576
|
+
}
|
|
499
577
|
// #2926: official root opt-out wins locally — skip Directive session ritual.
|
|
500
578
|
// disabled = skip ritual (exit 0 clean / 1 inconsistent). ready stays false so
|
|
501
579
|
// automation does not treat opt-out as "session fully initialized for work".
|
|
@@ -594,6 +672,12 @@ export function runSessionStart(projectRoot, options = {}) {
|
|
|
594
672
|
stepTimings.push({ name: "alignment", duration_ms: 0, skipped: true });
|
|
595
673
|
}
|
|
596
674
|
lines.push(formatEnvironmentContext(environment));
|
|
675
|
+
// #2275: SCM tooling + auth readiness — shallow on hot path, deep with --with-network.
|
|
676
|
+
// Never fails session:start; reports which SCM-dependent gates are skipped.
|
|
677
|
+
const scmStepStarted = performance.now();
|
|
678
|
+
const scm = resolveSessionScmReadiness(options, allowOptionalNetwork);
|
|
679
|
+
lines.push(...formatScmReadinessLines(scm));
|
|
680
|
+
stepTimings.push({ name: "scm_readiness", duration_ms: elapsedMs(scmStepStarted) });
|
|
597
681
|
if (!quickSteps.branch_policy) {
|
|
598
682
|
const stepStarted = performance.now();
|
|
599
683
|
const result = resolvePolicy(projectRoot);
|
|
@@ -793,6 +877,7 @@ export function runSessionStart(projectRoot, options = {}) {
|
|
|
793
877
|
diagnostic: userMd.diagnostic,
|
|
794
878
|
},
|
|
795
879
|
environment: environmentContextToDict(environment),
|
|
880
|
+
scm: scmReadinessToDict(scm),
|
|
796
881
|
message: code === 0 ? "session ritual recorded" : "session ritual failed",
|
|
797
882
|
};
|
|
798
883
|
// #2994: local process-cost event (best-effort; never blocks ceremony).
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
import { type DeliveryEvidenceInput, type NonDeliveryDisposition } from "../scope/delivery-evidence.js";
|
|
2
|
+
/** Per-story or default delivery evidence for cohort completion (#3041). */
|
|
3
|
+
export interface CohortDeliveryContext {
|
|
4
|
+
/** Evidence keyed by absolute story path (preferred). */
|
|
5
|
+
readonly evidenceByPath?: ReadonlyMap<string, DeliveryEvidenceInput> | null;
|
|
6
|
+
/** Shared evidence applied to every story when per-path map misses. */
|
|
7
|
+
readonly defaultEvidence?: DeliveryEvidenceInput | null;
|
|
8
|
+
readonly nonDeliveryDisposition?: NonDeliveryDisposition | null;
|
|
9
|
+
readonly assumeEvidenceValidated?: boolean;
|
|
10
|
+
readonly verifier?: string;
|
|
11
|
+
}
|
|
1
12
|
export interface TransitionRecord {
|
|
2
13
|
kind: "story" | "epic";
|
|
3
14
|
path: string;
|
|
@@ -17,7 +28,13 @@ export declare function resolveCohortPaths(positional: readonly string[], cohort
|
|
|
17
28
|
paths: string[];
|
|
18
29
|
errors: string[];
|
|
19
30
|
};
|
|
20
|
-
export
|
|
31
|
+
export interface SweepCohortArgs {
|
|
32
|
+
readonly storyPaths: readonly string[];
|
|
33
|
+
readonly projectRoot: string;
|
|
34
|
+
readonly dryRun: boolean;
|
|
35
|
+
readonly delivery?: CohortDeliveryContext | null;
|
|
36
|
+
}
|
|
37
|
+
export declare function sweepCohort(storyPaths: readonly string[], projectRoot: string, dryRun: boolean, delivery?: CohortDeliveryContext | null): SweepResult;
|
|
21
38
|
export declare function sweepResultToDict(result: SweepResult): Record<string, unknown>;
|
|
22
39
|
export declare function renderSweepText(result: SweepResult): string;
|
|
23
40
|
export declare function completeCohort(args: {
|
|
@@ -26,6 +43,8 @@ export declare function completeCohort(args: {
|
|
|
26
43
|
projectRoot: string;
|
|
27
44
|
dryRun?: boolean;
|
|
28
45
|
emitJson?: boolean;
|
|
46
|
+
/** Per-story delivery evidence; without it code-bearing stories fail closed (#3041). */
|
|
47
|
+
delivery?: CohortDeliveryContext | null;
|
|
29
48
|
}): {
|
|
30
49
|
exitCode: number;
|
|
31
50
|
stdout: string;
|
|
@@ -2,6 +2,7 @@ import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
3
3
|
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
4
4
|
import { detectLifecycleFolder } from "../scope/decomposed-refs.js";
|
|
5
|
+
import { classifyStoredDeliveryDisposition, } from "../scope/delivery-evidence.js";
|
|
5
6
|
import { runTransition } from "../scope/transition.js";
|
|
6
7
|
import { collectChildUris, collectPlanRefs, resolveVbriefRef } from "../scope/vbrief-ref.js";
|
|
7
8
|
import { MAX_FIXPOINT_PASSES, TERMINAL_FOLDERS } from "./constants.js";
|
|
@@ -115,17 +116,38 @@ function parentCandidatesFrom(plan, vbriefDir) {
|
|
|
115
116
|
}
|
|
116
117
|
return out;
|
|
117
118
|
}
|
|
118
|
-
function
|
|
119
|
+
function transitionOptionsFor(storyPath, delivery) {
|
|
120
|
+
if (delivery === null || delivery === undefined) {
|
|
121
|
+
return { verifier: "swarm:complete-cohort" };
|
|
122
|
+
}
|
|
123
|
+
const pathKey = resolve(storyPath);
|
|
124
|
+
const evidence = delivery.evidenceByPath?.get(pathKey) ??
|
|
125
|
+
delivery.evidenceByPath?.get(storyPath) ??
|
|
126
|
+
delivery.defaultEvidence ??
|
|
127
|
+
null;
|
|
128
|
+
return {
|
|
129
|
+
deliveryEvidence: evidence,
|
|
130
|
+
nonDeliveryDisposition: delivery.nonDeliveryDisposition,
|
|
131
|
+
assumeEvidenceValidated: delivery.assumeEvidenceValidated,
|
|
132
|
+
verifier: delivery.verifier ?? "swarm:complete-cohort",
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function completeStory(args) {
|
|
136
|
+
const { storyPath, vbriefDir, projectRoot, settled, dryRun, delivery } = args;
|
|
119
137
|
const folder = detectLifecycleFolder(storyPath);
|
|
120
138
|
const relpath = rel(storyPath, projectRoot);
|
|
121
139
|
if (folder !== null && TERMINAL_FOLDERS.includes(folder)) {
|
|
122
140
|
settled.add(resolve(storyPath));
|
|
141
|
+
const plan = loadPlan(storyPath);
|
|
142
|
+
const disposition = folder === "completed" && plan !== null ? classifyStoredDeliveryDisposition(plan) : "unknown";
|
|
123
143
|
return {
|
|
124
144
|
kind: "story",
|
|
125
145
|
path: relpath,
|
|
126
146
|
action: "noop",
|
|
127
147
|
ok: true,
|
|
128
|
-
detail:
|
|
148
|
+
detail: folder === "completed"
|
|
149
|
+
? `already in ${folder}/ (deliveryDisposition=${disposition})`
|
|
150
|
+
: `already in ${folder}/`,
|
|
129
151
|
};
|
|
130
152
|
}
|
|
131
153
|
if (folder !== "active") {
|
|
@@ -147,7 +169,7 @@ function completeStory(storyPath, vbriefDir, projectRoot, settled, dryRun) {
|
|
|
147
169
|
detail: "would complete active/ -> completed/",
|
|
148
170
|
};
|
|
149
171
|
}
|
|
150
|
-
const result = runTransition("complete", storyPath);
|
|
172
|
+
const result = runTransition("complete", storyPath, new Date(), transitionOptionsFor(storyPath, delivery));
|
|
151
173
|
if (result.ok) {
|
|
152
174
|
settled.add(resolve(join(vbriefDir, "completed", storyPath.split(/[/\\]/).pop() ?? "")));
|
|
153
175
|
}
|
|
@@ -159,7 +181,8 @@ function completeStory(storyPath, vbriefDir, projectRoot, settled, dryRun) {
|
|
|
159
181
|
detail: result.message,
|
|
160
182
|
};
|
|
161
183
|
}
|
|
162
|
-
function completeParent(
|
|
184
|
+
function completeParent(args) {
|
|
185
|
+
const { parentPath, vbriefDir, projectRoot, settled, dryRun, delivery } = args;
|
|
163
186
|
const folder = detectLifecycleFolder(parentPath);
|
|
164
187
|
const relpath = rel(parentPath, projectRoot);
|
|
165
188
|
if (folder !== null && TERMINAL_FOLDERS.includes(folder)) {
|
|
@@ -217,7 +240,7 @@ function completeParent(parentPath, vbriefDir, projectRoot, settled, dryRun) {
|
|
|
217
240
|
}
|
|
218
241
|
current = join(vbriefDir, "active", parentPath.split(/[/\\]/).pop() ?? "");
|
|
219
242
|
}
|
|
220
|
-
const completeResult = runTransition("complete", current);
|
|
243
|
+
const completeResult = runTransition("complete", current, new Date(), transitionOptionsFor(current, delivery));
|
|
221
244
|
if (completeResult.ok) {
|
|
222
245
|
settled.add(resolve(join(vbriefDir, "completed", parentPath.split(/[/\\]/).pop() ?? "")));
|
|
223
246
|
}
|
|
@@ -229,7 +252,12 @@ function completeParent(parentPath, vbriefDir, projectRoot, settled, dryRun) {
|
|
|
229
252
|
detail: completeResult.message,
|
|
230
253
|
};
|
|
231
254
|
}
|
|
232
|
-
export function sweepCohort(storyPaths, projectRoot, dryRun) {
|
|
255
|
+
export function sweepCohort(storyPaths, projectRoot, dryRun, delivery) {
|
|
256
|
+
return sweepCohortWithArgs({ storyPaths, projectRoot, dryRun, delivery: delivery ?? null });
|
|
257
|
+
}
|
|
258
|
+
function sweepCohortWithArgs(args) {
|
|
259
|
+
const { storyPaths, projectRoot, dryRun } = args;
|
|
260
|
+
const delivery = args.delivery ?? null;
|
|
233
261
|
let vbriefDir;
|
|
234
262
|
try {
|
|
235
263
|
vbriefDir = resolveLifecycleRoot(projectRoot);
|
|
@@ -275,7 +303,14 @@ export function sweepCohort(storyPaths, projectRoot, dryRun) {
|
|
|
275
303
|
}
|
|
276
304
|
}
|
|
277
305
|
}
|
|
278
|
-
result.stories.push(completeStory(
|
|
306
|
+
result.stories.push(completeStory({
|
|
307
|
+
storyPath,
|
|
308
|
+
vbriefDir,
|
|
309
|
+
projectRoot,
|
|
310
|
+
settled,
|
|
311
|
+
dryRun,
|
|
312
|
+
delivery,
|
|
313
|
+
}));
|
|
279
314
|
}
|
|
280
315
|
const finalized = new Set();
|
|
281
316
|
let passes = 0;
|
|
@@ -294,7 +329,14 @@ export function sweepCohort(storyPaths, projectRoot, dryRun) {
|
|
|
294
329
|
if (!allChildrenSettled(parentPlan, vbriefDir, settled, dryRun)) {
|
|
295
330
|
continue;
|
|
296
331
|
}
|
|
297
|
-
const record = completeParent(
|
|
332
|
+
const record = completeParent({
|
|
333
|
+
parentPath: candidate,
|
|
334
|
+
vbriefDir,
|
|
335
|
+
projectRoot,
|
|
336
|
+
settled,
|
|
337
|
+
dryRun,
|
|
338
|
+
delivery,
|
|
339
|
+
});
|
|
298
340
|
result.parents.push(record);
|
|
299
341
|
finalized.add(candidate);
|
|
300
342
|
progressed = true;
|
|
@@ -403,7 +445,12 @@ export function completeCohort(args) {
|
|
|
403
445
|
}
|
|
404
446
|
return { exitCode: 2, stdout: "", stderr };
|
|
405
447
|
}
|
|
406
|
-
const result =
|
|
448
|
+
const result = sweepCohortWithArgs({
|
|
449
|
+
storyPaths: paths,
|
|
450
|
+
projectRoot,
|
|
451
|
+
dryRun: args.dryRun ?? false,
|
|
452
|
+
delivery: args.delivery ?? null,
|
|
453
|
+
});
|
|
407
454
|
result.errors.push(...errors);
|
|
408
455
|
if (args.emitJson) {
|
|
409
456
|
return {
|
|
@@ -7,6 +7,7 @@ export function parseFinalizeCohortArgv(argv) {
|
|
|
7
7
|
let repo = null;
|
|
8
8
|
let projectRoot = ".";
|
|
9
9
|
let baseBranch = "master";
|
|
10
|
+
let deliveryBranch = null;
|
|
10
11
|
let label = null;
|
|
11
12
|
let dryRun = false;
|
|
12
13
|
let noCommit = false;
|
|
@@ -51,6 +52,13 @@ export function parseFinalizeCohortArgv(argv) {
|
|
|
51
52
|
baseBranch = next;
|
|
52
53
|
i += 1;
|
|
53
54
|
}
|
|
55
|
+
else if (arg === "--delivery-branch" && next !== undefined) {
|
|
56
|
+
deliveryBranch = next;
|
|
57
|
+
i += 1;
|
|
58
|
+
}
|
|
59
|
+
else if (arg?.startsWith("--delivery-branch=")) {
|
|
60
|
+
deliveryBranch = arg.slice("--delivery-branch=".length);
|
|
61
|
+
}
|
|
54
62
|
else if (arg === "--label" && next !== undefined) {
|
|
55
63
|
label = next;
|
|
56
64
|
i += 1;
|
|
@@ -77,6 +85,7 @@ export function parseFinalizeCohortArgv(argv) {
|
|
|
77
85
|
repo,
|
|
78
86
|
projectRoot,
|
|
79
87
|
baseBranch,
|
|
88
|
+
deliveryBranch,
|
|
80
89
|
label,
|
|
81
90
|
dryRun,
|
|
82
91
|
noCommit,
|
|
@@ -12,6 +12,8 @@ export interface FinalizeCohortResult {
|
|
|
12
12
|
readonly commit_sha: string | null;
|
|
13
13
|
readonly branch: string | null;
|
|
14
14
|
readonly pr_url: string | null;
|
|
15
|
+
readonly delivery_branch: string | null;
|
|
16
|
+
readonly delivery_errors: readonly string[];
|
|
15
17
|
readonly errors: readonly string[];
|
|
16
18
|
readonly warnings: readonly string[];
|
|
17
19
|
readonly ok: boolean;
|
|
@@ -21,7 +23,13 @@ export interface FinalizeCohortArgs {
|
|
|
21
23
|
readonly storyTokens?: readonly string[];
|
|
22
24
|
readonly repo?: string | null;
|
|
23
25
|
readonly projectRoot?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Swarm/PR base for the post-merge lifecycle sweep PR only.
|
|
28
|
+
* Distinct from plan.policy.deliveryBranch (#3041).
|
|
29
|
+
*/
|
|
24
30
|
readonly baseBranch?: string;
|
|
31
|
+
/** Override delivery branch for this run (defaults to policy/git default). */
|
|
32
|
+
readonly deliveryBranch?: string | null;
|
|
25
33
|
readonly label?: string | null;
|
|
26
34
|
readonly dryRun?: boolean;
|
|
27
35
|
readonly noCommit?: boolean;
|