@deftai/directive-core 0.99.0 → 0.101.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/authz/actions.d.ts +6 -0
- package/dist/authz/actions.js +3 -0
- package/dist/authz/classify.js +241 -30
- package/dist/authz/decompose-apply.d.ts +81 -0
- package/dist/authz/decompose-apply.js +377 -0
- package/dist/authz/index.d.ts +3 -1
- package/dist/authz/index.js +3 -1
- package/dist/authz/store.d.ts +53 -0
- package/dist/authz/store.js +219 -1
- package/dist/authz/types.d.ts +19 -2
- package/dist/authz/types.js +5 -1
- package/dist/check/cached-orchestrator.d.ts +26 -5
- package/dist/check/cached-orchestrator.js +228 -6
- package/dist/check/gate-lists.d.ts +10 -4
- package/dist/check/gate-lists.js +19 -5
- package/dist/check/index.d.ts +3 -2
- package/dist/check/index.js +2 -1
- package/dist/check/named-cause.d.ts +45 -0
- package/dist/check/named-cause.js +121 -0
- package/dist/check/orchestrator.js +6 -1
- package/dist/doctor/checks.d.ts +44 -0
- package/dist/doctor/checks.js +428 -2
- package/dist/doctor/help.d.ts +5 -0
- package/dist/doctor/help.js +31 -0
- package/dist/doctor/index.d.ts +2 -0
- package/dist/doctor/index.js +2 -0
- package/dist/doctor/main.d.ts +6 -7
- package/dist/doctor/main.js +130 -79
- package/dist/doctor/session-coda.d.ts +82 -0
- package/dist/doctor/session-coda.js +151 -0
- package/dist/doctor/types.d.ts +16 -0
- package/dist/doctor/which.d.ts +20 -1
- package/dist/doctor/which.js +67 -1
- package/dist/finish-loop/pr-finish-loop.d.ts +13 -2
- package/dist/finish-loop/pr-finish-loop.js +116 -5
- package/dist/finish-loop/types.d.ts +1 -1
- package/dist/hooks/dispatcher.d.ts +9 -5
- package/dist/hooks/dispatcher.js +23 -15
- package/dist/hooks/readonly.d.ts +6 -3
- package/dist/hooks/readonly.js +44 -25
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/init-deposit/agent-hooks.d.ts +20 -0
- package/dist/init-deposit/agent-hooks.js +51 -31
- package/dist/init-deposit/gitignore.js +2 -0
- package/dist/intake/issue-ingest.js +36 -0
- package/dist/lifecycle/completed-consistency.d.ts +60 -0
- package/dist/lifecycle/completed-consistency.js +443 -0
- package/dist/lifecycle/completed-tracked-on-delivery.d.ts +47 -0
- package/dist/lifecycle/completed-tracked-on-delivery.js +415 -0
- package/dist/lifecycle/index.d.ts +2 -0
- package/dist/lifecycle/index.js +2 -0
- package/dist/literal-acceptance/capture.d.ts +52 -0
- package/dist/literal-acceptance/capture.js +658 -0
- package/dist/literal-acceptance/evaluate.d.ts +47 -0
- package/dist/literal-acceptance/evaluate.js +193 -0
- package/dist/literal-acceptance/index.d.ts +12 -0
- package/dist/literal-acceptance/index.js +12 -0
- package/dist/literal-acceptance/run.d.ts +38 -0
- package/dist/literal-acceptance/run.js +216 -0
- package/dist/literal-acceptance/safety.d.ts +24 -0
- package/dist/literal-acceptance/safety.js +251 -0
- package/dist/literal-acceptance/types.d.ts +82 -0
- package/dist/literal-acceptance/types.js +26 -0
- package/dist/policy/ac-pass-banking.d.ts +58 -0
- package/dist/policy/ac-pass-banking.js +150 -0
- package/dist/policy/ceremony-dial.d.ts +96 -11
- package/dist/policy/ceremony-dial.js +250 -23
- package/dist/policy/index.d.ts +2 -0
- package/dist/policy/index.js +16 -1
- package/dist/policy/merge-approval-head.d.ts +129 -0
- package/dist/policy/merge-approval-head.js +461 -0
- package/dist/policy/require-human-merge.d.ts +5 -0
- package/dist/policy/require-human-merge.js +5 -0
- package/dist/pr-merge-readiness/ci-gate.d.ts +20 -3
- package/dist/pr-merge-readiness/ci-gate.js +65 -5
- package/dist/pr-merge-readiness/compute.d.ts +13 -0
- package/dist/pr-merge-readiness/compute.js +99 -14
- package/dist/pr-merge-readiness/gh.d.ts +49 -0
- package/dist/pr-merge-readiness/gh.js +268 -0
- package/dist/pr-merge-readiness/index.d.ts +2 -2
- package/dist/pr-merge-readiness/index.js +2 -2
- package/dist/pr-merge-readiness/test-gh-fixtures.helpers.js +4 -0
- package/dist/pr-wait-mergeable/cascade.d.ts +11 -0
- package/dist/pr-wait-mergeable/cascade.js +115 -1
- package/dist/pr-wait-mergeable/main.d.ts +4 -0
- package/dist/pr-wait-mergeable/main.js +4 -0
- package/dist/pr-wait-mergeable/types.d.ts +3 -1
- package/dist/pr-wait-mergeable/wrappers.d.ts +6 -1
- package/dist/pr-wait-mergeable/wrappers.js +7 -1
- package/dist/preflight/evaluate.d.ts +15 -1
- package/dist/preflight/evaluate.js +33 -2
- package/dist/preflight-cache/evaluate.d.ts +3 -0
- package/dist/preflight-cache/evaluate.js +3 -0
- package/dist/product-first-done-gate/acceptance.d.ts +25 -0
- package/dist/product-first-done-gate/acceptance.js +247 -0
- package/dist/product-first-done-gate/check-mode.d.ts +45 -0
- package/dist/product-first-done-gate/check-mode.js +148 -0
- package/dist/product-first-done-gate/evaluate.d.ts +50 -0
- package/dist/product-first-done-gate/evaluate.js +255 -0
- package/dist/product-first-done-gate/index.d.ts +11 -0
- package/dist/product-first-done-gate/index.js +11 -0
- package/dist/product-first-done-gate/types.d.ts +64 -0
- package/dist/product-first-done-gate/types.js +61 -0
- package/dist/render/framework-commands.js +4 -0
- package/dist/run-summary/emit.d.ts +56 -0
- package/dist/run-summary/emit.js +171 -0
- package/dist/run-summary/index.d.ts +4 -0
- package/dist/run-summary/index.js +4 -0
- package/dist/run-summary/path.d.ts +25 -0
- package/dist/run-summary/path.js +78 -0
- package/dist/run-summary/types.d.ts +76 -0
- package/dist/run-summary/types.js +21 -0
- package/dist/scope/acceptance-evidence.d.ts +110 -0
- package/dist/scope/acceptance-evidence.js +422 -0
- package/dist/scope/coverage-map.d.ts +98 -0
- package/dist/scope/coverage-map.js +558 -0
- package/dist/scope/decompose.d.ts +5 -0
- package/dist/scope/decompose.js +134 -11
- package/dist/scope/index.d.ts +3 -0
- package/dist/scope/index.js +3 -0
- package/dist/scope/parent-lineage.d.ts +125 -0
- package/dist/scope/parent-lineage.js +626 -0
- package/dist/scope/transition.d.ts +8 -0
- package/dist/scope/transition.js +61 -2
- package/dist/session/ac-pass-banking.d.ts +221 -0
- package/dist/session/ac-pass-banking.js +761 -0
- package/dist/session/active-cli.d.ts +79 -0
- package/dist/session/active-cli.js +382 -0
- package/dist/session/deposit-sha.d.ts +49 -0
- package/dist/session/deposit-sha.js +121 -0
- package/dist/session/effort-budget.d.ts +136 -0
- package/dist/session/effort-budget.js +352 -0
- package/dist/session/index.d.ts +7 -0
- package/dist/session/index.js +7 -0
- package/dist/session/orientation-compression.d.ts +127 -0
- package/dist/session/orientation-compression.js +425 -0
- package/dist/session/orientation-state.d.ts +29 -0
- package/dist/session/orientation-state.js +85 -0
- package/dist/session/session-start.d.ts +31 -0
- package/dist/session/session-start.js +228 -2
- package/dist/session/toolchain-preflight.d.ts +64 -0
- package/dist/session/toolchain-preflight.js +151 -0
- package/dist/session/verify-session-ritual.d.ts +14 -0
- package/dist/session/verify-session-ritual.js +33 -0
- package/dist/story-ready/evaluate.d.ts +11 -0
- package/dist/story-ready/evaluate.js +41 -3
- package/dist/triage/actions/candidates-log.d.ts +1 -2
- package/dist/triage/actions/candidates-log.js +37 -6
- package/dist/triage/bootstrap/gitignore.d.ts +1 -1
- package/dist/triage/bootstrap/gitignore.js +8 -2
- package/dist/triage/bootstrap/index.js +15 -3
- package/dist/triage/classify/index.d.ts +1 -0
- package/dist/triage/classify/index.js +2 -0
- package/dist/triage/classify/label-mirror.js +16 -0
- package/dist/triage/classify/mirror-discovery-tip.d.ts +95 -0
- package/dist/triage/classify/mirror-discovery-tip.js +234 -0
- package/dist/triage/reconcile/reconcile.js +28 -6
- package/dist/triage/summary/index.js +12 -4
- package/dist/triage/welcome/default-mode.js +16 -0
- package/dist/ts-check-lane/run-lane.js +21 -4
- package/dist/vbrief-validate/conformance.js +7 -0
- package/dist/vitest-runner/coverage-debt-teardown.js +16 -4
- package/dist/xbrief-migrate/migrate-project.d.ts +4 -0
- package/dist/xbrief-migrate/migrate-project.js +92 -3
- package/package.json +15 -3
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import {
|
|
4
|
-
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { mkdirSync } from "node:fs";
|
|
4
|
+
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
5
|
+
import { containedWrite } from "../../fs/contained-write.js";
|
|
5
6
|
import { resolveLifecycleFolder } from "../../layout/resolve.js";
|
|
6
7
|
import { resolveCandidatesLogPath } from "../cache-path.js";
|
|
7
8
|
import { auditKey, existingAuditRefs, scanLifecycleRefs } from "./audit.js";
|
|
@@ -127,9 +128,30 @@ function buildReconcileEntry(repo, issueNumber, sourceFolder) {
|
|
|
127
128
|
"with a github-issue reference but no prior decision in the audit log",
|
|
128
129
|
};
|
|
129
130
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Containment root for reconcile audit appends (#3288 / #3245).
|
|
133
|
+
* Prefer project root when the log is nested under it; otherwise parent dir.
|
|
134
|
+
*/
|
|
135
|
+
function containmentRootForAudit(projectRoot, auditPath) {
|
|
136
|
+
const rootAbs = resolve(projectRoot);
|
|
137
|
+
const logAbs = resolve(auditPath);
|
|
138
|
+
const parent = dirname(logAbs);
|
|
139
|
+
mkdirSync(parent, { recursive: true });
|
|
140
|
+
const rel = relative(rootAbs, logAbs);
|
|
141
|
+
if (rel.length > 0 && !rel.startsWith("..") && !isAbsolute(rel)) {
|
|
142
|
+
return rootAbs;
|
|
143
|
+
}
|
|
144
|
+
return resolve(parent);
|
|
145
|
+
}
|
|
146
|
+
/** #3288 / #3245 / #2980: product append refuses leaf symlink follow. */
|
|
147
|
+
function appendAuditEntry(projectRoot, auditPath, entry) {
|
|
148
|
+
const root = containmentRootForAudit(projectRoot, auditPath);
|
|
149
|
+
containedWrite({
|
|
150
|
+
root,
|
|
151
|
+
target: auditPath,
|
|
152
|
+
data: `${JSON.stringify(entry)}\n`,
|
|
153
|
+
mode: "append",
|
|
154
|
+
});
|
|
133
155
|
}
|
|
134
156
|
export function reconcile(projectRoot, options = {}) {
|
|
135
157
|
const root = resolve(projectRoot);
|
|
@@ -158,7 +180,7 @@ export function reconcile(projectRoot, options = {}) {
|
|
|
158
180
|
for (const item of items) {
|
|
159
181
|
const entry = buildReconcileEntry(item.repo, item.issueNumber, item.folder);
|
|
160
182
|
try {
|
|
161
|
-
appendAuditEntry(auditPath, entry);
|
|
183
|
+
appendAuditEntry(root, auditPath, entry);
|
|
162
184
|
}
|
|
163
185
|
catch (err) {
|
|
164
186
|
return {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import {
|
|
3
|
-
import { join, resolve as pathResolve } from "node:path";
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join, resolve as pathResolve } from "node:path";
|
|
4
|
+
import { containedWrite } from "../../fs/contained-write.js";
|
|
4
5
|
import { hasArtifactSuffix, resolveLifecycleFolder, resolveProjectDefinitionPath, } from "../../layout/resolve.js";
|
|
5
6
|
import { readPlanPolicy } from "../../policy/plan-extensions.js";
|
|
6
7
|
import { loadProjectDefinition, PROJECT_DEFINITION_REL_PATH } from "../../policy/resolve.js";
|
|
@@ -542,8 +543,15 @@ export function appendHistory(historyPath, result, line, options = {}) {
|
|
|
542
543
|
});
|
|
543
544
|
const payload = pythonStyleStringify(record);
|
|
544
545
|
try {
|
|
545
|
-
|
|
546
|
-
|
|
546
|
+
// #3288 / #3245 / #2980: refuse leaf symlink follow (in-tree diversion).
|
|
547
|
+
const parent = dirname(historyPath);
|
|
548
|
+
mkdirSync(parent, { recursive: true });
|
|
549
|
+
containedWrite({
|
|
550
|
+
root: pathResolve(parent),
|
|
551
|
+
target: historyPath,
|
|
552
|
+
data: `${payload}\n`,
|
|
553
|
+
mode: "append",
|
|
554
|
+
});
|
|
547
555
|
}
|
|
548
556
|
catch {
|
|
549
557
|
// observability only — never crash the ritual
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { ensureTriageCacheHydrated } from "../../cache/empty-populate.js";
|
|
3
3
|
import { maybeSelfHealCache } from "../../cache/fetch.js";
|
|
4
|
+
import { maybeFormatMirrorDiscoveryTip } from "../classify/mirror-discovery-tip.js";
|
|
4
5
|
import { FIRST_TIME_NUDGE, INCOMPLETE_NUDGE_TEMPLATE } from "./constants.js";
|
|
5
6
|
import { classifyOnboarding, detectPriorState } from "./prior-state.js";
|
|
6
7
|
import { emitOneliner } from "./summary.js";
|
|
@@ -52,6 +53,21 @@ export function runDefaultMode(projectRoot, options = {}) {
|
|
|
52
53
|
else if (label === "incomplete") {
|
|
53
54
|
out(INCOMPLETE_NUDGE_TEMPLATE.replace("{missing}", missing.join(" + ")).replace(canonicalOnboard, onboardCommand));
|
|
54
55
|
}
|
|
56
|
+
// #3124: throttled SCM label-mirror discovery tip (existence + get-the-most).
|
|
57
|
+
// Cold session:start runs this welcome path; re-arm skips welcome so tip is not spammed.
|
|
58
|
+
try {
|
|
59
|
+
const tip = maybeFormatMirrorDiscoveryTip(projectRoot, {
|
|
60
|
+
...(options.now !== undefined ? { now: options.now } : {}),
|
|
61
|
+
});
|
|
62
|
+
if (tip.length > 0) {
|
|
63
|
+
for (const line of tip.trimEnd().split(/\r?\n/)) {
|
|
64
|
+
out(line);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Discovery tip is advisory — never fail welcome.
|
|
70
|
+
}
|
|
55
71
|
return outcome;
|
|
56
72
|
}
|
|
57
73
|
//# sourceMappingURL=default-mode.js.map
|
|
@@ -51,12 +51,12 @@ export function shouldUseShellForCommand(command, platform = process.platform) {
|
|
|
51
51
|
return platform === "win32" && /\.(?:cmd|bat)$/i.test(command);
|
|
52
52
|
}
|
|
53
53
|
/** Default runner: an inherited-stdio pnpm invocation. */
|
|
54
|
-
function defaultRunner(argv, cwd) {
|
|
54
|
+
function defaultRunner(argv, cwd, envOverride) {
|
|
55
55
|
const [command, ...rest] = argv;
|
|
56
56
|
const commandPath = command ?? "";
|
|
57
57
|
const result = spawnSync(commandPath, rest, {
|
|
58
58
|
cwd,
|
|
59
|
-
env: sanitizeTsLaneEnv(process.env),
|
|
59
|
+
env: envOverride ?? sanitizeTsLaneEnv(process.env),
|
|
60
60
|
stdio: "inherit",
|
|
61
61
|
shell: shouldUseShellForCommand(commandPath),
|
|
62
62
|
});
|
|
@@ -108,10 +108,27 @@ export function runTsLane(projectRoot, options) {
|
|
|
108
108
|
}
|
|
109
109
|
for (const command of LANE_COMMANDS) {
|
|
110
110
|
const argv = [pnpm, ...command];
|
|
111
|
+
// Soft-pass via lane-private env (vitest 3 CAC rejects unknown CLI debt tokens).
|
|
112
|
+
// vitest.config reads DEFT_TS_LANE_COVERAGE_DEBT without DEFT_RELEASE_PREFLIGHT
|
|
113
|
+
// (sanitizeTsLaneEnv strips preflight). Refs #2573 / #2618.
|
|
114
|
+
const prevDebt = process.env.DEFT_TS_LANE_COVERAGE_DEBT;
|
|
111
115
|
if (debtIssue !== null && command[1] === "test") {
|
|
112
|
-
|
|
116
|
+
process.env.DEFT_TS_LANE_COVERAGE_DEBT = String(debtIssue);
|
|
117
|
+
}
|
|
118
|
+
let result;
|
|
119
|
+
try {
|
|
120
|
+
result = runner(argv, projectRoot);
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
if (debtIssue !== null && command[1] === "test") {
|
|
124
|
+
if (prevDebt === undefined) {
|
|
125
|
+
delete process.env.DEFT_TS_LANE_COVERAGE_DEBT;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
process.env.DEFT_TS_LANE_COVERAGE_DEBT = prevDebt;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
113
131
|
}
|
|
114
|
-
const result = runner(argv, projectRoot);
|
|
115
132
|
const code = result.status;
|
|
116
133
|
// A null status means the child was terminated by a signal (SIGKILL / OOM /
|
|
117
134
|
// SIGTERM) before it could exit. Mapping that to 0 would silently pass a
|
|
@@ -28,6 +28,8 @@ export const PLAN_CORE = new Set([
|
|
|
28
28
|
"changeLog",
|
|
29
29
|
"sequence",
|
|
30
30
|
"fork",
|
|
31
|
+
// Product-first done-gate (#3284): plan.acceptance.commands / none_stated / source_rung.
|
|
32
|
+
"acceptance",
|
|
31
33
|
]);
|
|
32
34
|
export const ITEM_CORE = new Set([
|
|
33
35
|
"id",
|
|
@@ -62,6 +64,11 @@ export const ITEM_CORE = new Set([
|
|
|
62
64
|
"lastModifiedBy",
|
|
63
65
|
"lockedBy",
|
|
64
66
|
"items",
|
|
67
|
+
// Optional PlanItem.effort enum S/M/L/XL (#1581) — schema-validated, core not extension.
|
|
68
|
+
"effort",
|
|
69
|
+
// #3305 Option B: acceptance evidence/disposition are NOT ITEM_CORE.
|
|
70
|
+
// Canonical keys: plan.items[].x-directive/evidence and x-directive/disposition
|
|
71
|
+
// (accepted via EXTENSION_PREFIXES). Bare evidence/disposition fail #1620.
|
|
65
72
|
]);
|
|
66
73
|
export const EXTENSION_PREFIXES = ["x-directive/", "x-vbrief/", "x-xbrief/"];
|
|
67
74
|
export function renderFinding(finding) {
|
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { COVERAGE_GOAL, countRecentCoverageDebtMentions, formatCoverageAttribution, formatOveruseWarning, metricsBelowGoal, readCoverageTotalsFromReport, resolveCoverageDebtIssue, } from "./coverage-debt.js";
|
|
4
|
-
|
|
5
|
-
export default async function coverageDebtTeardown() {
|
|
4
|
+
function resolveDebtIssueForTeardown() {
|
|
6
5
|
const resolution = resolveCoverageDebtIssue(process.argv, process.env);
|
|
7
|
-
if (resolution.kind === "
|
|
8
|
-
return;
|
|
6
|
+
if (resolution.kind === "valid")
|
|
7
|
+
return resolution;
|
|
9
8
|
if (resolution.kind === "invalid") {
|
|
10
9
|
throw new Error(`coverage-debt: ${resolution.reason}`);
|
|
11
10
|
}
|
|
11
|
+
const laneRaw = process.env.DEFT_TS_LANE_COVERAGE_DEBT;
|
|
12
|
+
if (laneRaw !== undefined && /^\d+$/.test(laneRaw.trim())) {
|
|
13
|
+
const issue = Number.parseInt(laneRaw.trim(), 10);
|
|
14
|
+
if (issue > 0)
|
|
15
|
+
return { kind: "valid", issue };
|
|
16
|
+
}
|
|
17
|
+
return { kind: "none" };
|
|
18
|
+
}
|
|
19
|
+
/** Vitest globalTeardown: enforce coverage goal or debt soft-pass (#2573). */
|
|
20
|
+
export default async function coverageDebtTeardown() {
|
|
21
|
+
const resolution = resolveDebtIssueForTeardown();
|
|
22
|
+
if (resolution.kind === "none")
|
|
23
|
+
return;
|
|
12
24
|
const repoRoot = join(import.meta.dirname, "..", "..", "..", "..");
|
|
13
25
|
const totals = readCoverageTotalsFromReport(join(repoRoot, "coverage"));
|
|
14
26
|
if (totals === null) {
|
|
@@ -24,6 +24,10 @@ export type XbriefMigrationOutcome = {
|
|
|
24
24
|
readonly kind: "migrated";
|
|
25
25
|
readonly backupDir: string;
|
|
26
26
|
readonly files: number;
|
|
27
|
+
} | {
|
|
28
|
+
readonly kind: "rewritten";
|
|
29
|
+
readonly files: number;
|
|
30
|
+
readonly message: string;
|
|
27
31
|
} | {
|
|
28
32
|
readonly kind: "converged";
|
|
29
33
|
readonly action: VbriefConvergeAction;
|
|
@@ -5,12 +5,12 @@ import { assertWriteTargetSafe, ProjectionContainmentError } from "../fs/project
|
|
|
5
5
|
import { checkGitClean } from "../migrate-preflight/index.js";
|
|
6
6
|
import { applyAgentsRefresh } from "../platform/agents-md.js";
|
|
7
7
|
import { patchAgentsMdHeader, renderHeaderPatchSummary } from "./agents-header.js";
|
|
8
|
-
import { LEGACY_ARTIFACT_DIR, LEGACY_ARTIFACT_SUFFIX, MIGRATED_ARTIFACT_DIR, MIGRATED_ARTIFACT_SUFFIX, OBSOLETE_FRAMEWORK_NARRATIVE_FILENAME, VBRIEF_DEPRECATION_MARKER_BODY, VBRIEF_DEPRECATION_MARKER_FILENAME, } from "./constants.js";
|
|
8
|
+
import { LEGACY_ARTIFACT_DIR, LEGACY_ARTIFACT_SUFFIX, LEGACY_VBRIEF_VERSION, MIGRATED_ARTIFACT_DIR, MIGRATED_ARTIFACT_SUFFIX, OBSOLETE_FRAMEWORK_NARRATIVE_FILENAME, VBRIEF_DEPRECATION_MARKER_BODY, VBRIEF_DEPRECATION_MARKER_FILENAME, } from "./constants.js";
|
|
9
9
|
import { detectLegacyVbriefLayout, detectXbriefConvergence } from "./detect.js";
|
|
10
10
|
import { hasVbriefDeprecationMarker, isDirectory, isEffectivelyEmptyDir } from "./fs-helpers.js";
|
|
11
11
|
import { assertMigrationSourceSafe } from "./migration-containment.js";
|
|
12
12
|
import { renderXbriefMigrationLine, xbriefMigrationGuidance } from "./signpost.js";
|
|
13
|
-
import { rewriteEmbeddedTokens, transformArtifactV06ToV08Transactional } from "./transforms.js";
|
|
13
|
+
import { readDeclaredArtifactVersion, rewriteEmbeddedTokens, transformArtifactV06ToV08Transactional, } from "./transforms.js";
|
|
14
14
|
function collectFiles(root, acc = []) {
|
|
15
15
|
if (!isDirectory(root)) {
|
|
16
16
|
return acc;
|
|
@@ -74,6 +74,65 @@ function writeMigratedFile(projectRoot, srcPath, destPath) {
|
|
|
74
74
|
mode: "replace",
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Plan in-place hybrid envelope rewrites for xbrief lifecycle `.xbrief.json`
|
|
79
|
+
* artifacts that still declare `xBRIEFInfo@0.6` after the layout rename
|
|
80
|
+
* (#3236 / #2974). Pure probe — no writes. Already-0.8 artifacts, schema
|
|
81
|
+
* deposits, and non-0.6 JSON are skipped so residual #2368 schema-only markers
|
|
82
|
+
* still route to `directive update`.
|
|
83
|
+
*/
|
|
84
|
+
function planHybridEnvelopeRewrites(projectRoot) {
|
|
85
|
+
const migratedDir = join(projectRoot, MIGRATED_ARTIFACT_DIR);
|
|
86
|
+
const artifactPaths = collectFiles(migratedDir).filter((path) => path.endsWith(MIGRATED_ARTIFACT_SUFFIX));
|
|
87
|
+
const pending = [];
|
|
88
|
+
for (const filePath of artifactPaths) {
|
|
89
|
+
let parsed;
|
|
90
|
+
try {
|
|
91
|
+
parsed = JSON.parse(readFileSync(filePath, "utf8"));
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
95
|
+
return {
|
|
96
|
+
ok: false,
|
|
97
|
+
error: `failed to parse hybrid envelope candidate ${relative(projectRoot, filePath)}: ${detail}`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// Only residual v0.6 envelopes are rewrite candidates; skip 0.8 and unknown.
|
|
101
|
+
if (readDeclaredArtifactVersion(parsed) !== LEGACY_VBRIEF_VERSION) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const result = transformArtifactV06ToV08Transactional(parsed);
|
|
105
|
+
if (!result.ok) {
|
|
106
|
+
return {
|
|
107
|
+
ok: false,
|
|
108
|
+
error: `failed to transform hybrid envelope ${relative(projectRoot, filePath)}: ${result.error}`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (!result.changed) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
pending.push({
|
|
115
|
+
path: filePath,
|
|
116
|
+
body: `${JSON.stringify(result.artifact, null, 2)}\n`,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return { ok: true, pending };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Apply a previously planned hybrid-envelope rewrite set via containedWrite (#3236).
|
|
123
|
+
*/
|
|
124
|
+
function applyHybridEnvelopeRewrites(projectRoot, pending) {
|
|
125
|
+
const root = resolve(projectRoot);
|
|
126
|
+
for (const entry of pending) {
|
|
127
|
+
containedWrite({
|
|
128
|
+
root,
|
|
129
|
+
target: entry.path,
|
|
130
|
+
data: entry.body,
|
|
131
|
+
mode: "replace",
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return pending.length;
|
|
135
|
+
}
|
|
77
136
|
function backupMigrationInputs(projectRoot, legacyDir, migratedDir) {
|
|
78
137
|
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
79
138
|
const backupRoot = join(projectRoot, ".deft", `xbrief-migrate-backup-${stamp}`);
|
|
@@ -256,6 +315,33 @@ export function runXbriefMigration(args, _io) {
|
|
|
256
315
|
default:
|
|
257
316
|
break;
|
|
258
317
|
}
|
|
318
|
+
// #3236: on already-xbrief trees with no vbrief/, rewrite residual hybrid
|
|
319
|
+
// xBRIEFInfo@0.6 envelopes in place (transform accepts hybrid — #2974).
|
|
320
|
+
// Run before the layout-detection short-circuit so compact `"version":"0.6"`
|
|
321
|
+
// JSON that the string-scan may miss still gets rewritten.
|
|
322
|
+
if (convergence.xbriefHasContent && !convergence.vbriefPresent) {
|
|
323
|
+
const plan = planHybridEnvelopeRewrites(projectRoot);
|
|
324
|
+
if (!plan.ok) {
|
|
325
|
+
return { kind: "config", message: plan.error };
|
|
326
|
+
}
|
|
327
|
+
if (plan.pending.length > 0) {
|
|
328
|
+
if (!args.force) {
|
|
329
|
+
const git = checkGitClean(projectRoot);
|
|
330
|
+
if (git.status === "WARN") {
|
|
331
|
+
return {
|
|
332
|
+
kind: "refused",
|
|
333
|
+
message: `${git.message} ${xbriefMigrationGuidance()} Pass --force to override.`,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const files = applyHybridEnvelopeRewrites(projectRoot, plan.pending);
|
|
338
|
+
return {
|
|
339
|
+
kind: "rewritten",
|
|
340
|
+
files,
|
|
341
|
+
message: `Rewrote ${files} hybrid xBRIEFInfo@0.6 envelope(s) in place to xBRIEFInfo@0.8 under '${MIGRATED_ARTIFACT_DIR}/'.`,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
}
|
|
259
345
|
const detection = detectLegacyVbriefLayout(projectRoot);
|
|
260
346
|
if (!detection.legacyLayout) {
|
|
261
347
|
return {
|
|
@@ -266,7 +352,7 @@ export function runXbriefMigration(args, _io) {
|
|
|
266
352
|
if (!isDirectory(legacyDir)) {
|
|
267
353
|
// Already on canonical xbrief/ with no vbrief/ tree — residual markers (e.g. a
|
|
268
354
|
// stale deposited v0.6 schema under xbrief/schemas/) are refreshed by update,
|
|
269
|
-
// not migrate:xbrief (#2368).
|
|
355
|
+
// not migrate:xbrief (#2368). Hybrid 0.6 envelopes were handled above (#3236).
|
|
270
356
|
if (convergence.xbriefHasContent && !convergence.vbriefPresent) {
|
|
271
357
|
return {
|
|
272
358
|
kind: "noop",
|
|
@@ -319,6 +405,9 @@ export function emitXbriefMigration(outcome, io, options = {}) {
|
|
|
319
405
|
io.writeOut(`Migrated ${outcome.files} file(s) from ${LEGACY_ARTIFACT_DIR}/ to ${MIGRATED_ARTIFACT_DIR}/.\n` +
|
|
320
406
|
`Backup written to ${outcome.backupDir}.\n`);
|
|
321
407
|
return 0;
|
|
408
|
+
case "rewritten":
|
|
409
|
+
io.writeOut(`${outcome.message}\n`);
|
|
410
|
+
return 0;
|
|
322
411
|
case "converged":
|
|
323
412
|
io.writeOut(`${outcome.message}\n`);
|
|
324
413
|
return 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.101.0",
|
|
4
4
|
"description": "TypeScript engine core for the Directive framework.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -283,6 +283,14 @@
|
|
|
283
283
|
"types": "./dist/lifecycle/index.d.ts",
|
|
284
284
|
"default": "./dist/lifecycle/index.js"
|
|
285
285
|
},
|
|
286
|
+
"./literal-acceptance": {
|
|
287
|
+
"types": "./dist/literal-acceptance/index.d.ts",
|
|
288
|
+
"default": "./dist/literal-acceptance/index.js"
|
|
289
|
+
},
|
|
290
|
+
"./product-first-done-gate": {
|
|
291
|
+
"types": "./dist/product-first-done-gate/index.d.ts",
|
|
292
|
+
"default": "./dist/product-first-done-gate/index.js"
|
|
293
|
+
},
|
|
286
294
|
"./packs": {
|
|
287
295
|
"types": "./dist/packs/index.d.ts",
|
|
288
296
|
"default": "./dist/packs/index.js"
|
|
@@ -311,6 +319,10 @@
|
|
|
311
319
|
"types": "./dist/check/index.d.ts",
|
|
312
320
|
"default": "./dist/check/index.js"
|
|
313
321
|
},
|
|
322
|
+
"./run-summary": {
|
|
323
|
+
"types": "./dist/run-summary/index.d.ts",
|
|
324
|
+
"default": "./dist/run-summary/index.js"
|
|
325
|
+
},
|
|
314
326
|
"./preflight-gh": {
|
|
315
327
|
"types": "./dist/preflight-gh/index.d.ts",
|
|
316
328
|
"default": "./dist/preflight-gh/index.js"
|
|
@@ -366,8 +378,8 @@
|
|
|
366
378
|
"provenance": true
|
|
367
379
|
},
|
|
368
380
|
"dependencies": {
|
|
369
|
-
"@deftai/directive-content": "^0.
|
|
370
|
-
"@deftai/directive-types": "^0.
|
|
381
|
+
"@deftai/directive-content": "^0.101.0",
|
|
382
|
+
"@deftai/directive-types": "^0.101.0",
|
|
371
383
|
"archiver": "^8.0.0"
|
|
372
384
|
},
|
|
373
385
|
"scripts": {
|