@deftai/directive-core 0.72.0 → 0.73.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/category-b-namespace/index.js +16 -2
- package/dist/codebase/default-extractor.js +9 -1
- package/dist/codebase/map.js +1 -1
- package/dist/doctor/checks.d.ts +9 -0
- package/dist/doctor/checks.js +85 -5
- package/dist/doctor/main.js +2 -1
- package/dist/eval/health.js +7 -1
- package/dist/eval/readback.js +7 -1
- package/dist/init-deposit/gitignore.js +6 -0
- package/dist/init-deposit/hygiene.d.ts +24 -0
- package/dist/init-deposit/hygiene.js +55 -0
- package/dist/init-deposit/refresh.js +22 -6
- package/dist/init-deposit/scaffold.d.ts +11 -1
- package/dist/init-deposit/scaffold.js +4 -4
- package/dist/integration-e2e/helpers.js +7 -7
- package/dist/layout/index.d.ts +1 -1
- package/dist/layout/index.js +1 -1
- package/dist/layout/resolve.d.ts +47 -31
- package/dist/layout/resolve.js +93 -36
- package/dist/lifecycle/lifecycle-hygiene.d.ts +1 -1
- package/dist/lifecycle/lifecycle-hygiene.js +1 -1
- package/dist/migrate-preflight/index.js +10 -10
- package/dist/policy/resolve.d.ts +2 -2
- package/dist/policy/resolve.js +11 -3
- package/dist/policy/wip.d.ts +1 -1
- package/dist/policy/wip.js +15 -3
- package/dist/preflight-cache/evaluate.js +1 -1
- package/dist/release/native-steps.js +14 -2
- package/dist/render/export-spec.js +2 -2
- package/dist/render/project-render.js +40 -9
- package/dist/scope/decompose.js +12 -2
- package/dist/session/resume-conditions.js +2 -2
- package/dist/session/ritual-sentinel.d.ts +1 -1
- package/dist/session/ritual-sentinel.js +6 -4
- package/dist/slice/constants.d.ts +1 -1
- package/dist/slice/constants.js +1 -1
- package/dist/slice/project-context.js +1 -1
- package/dist/spec-authority/resolver.js +9 -2
- package/dist/swarm/complete-cohort.js +14 -1
- package/dist/swarm/readiness.js +33 -3
- package/dist/task-surface/index.js +3 -3
- package/dist/triage/actions/candidates-log.d.ts +1 -1
- package/dist/triage/bootstrap/gitignore.js +22 -10
- package/dist/triage/bootstrap/index.d.ts +1 -1
- package/dist/triage/bootstrap/index.js +9 -3
- package/dist/triage/cache-path.d.ts +1 -1
- package/dist/triage/cache-path.js +32 -6
- package/dist/triage/classify/index.d.ts +1 -1
- package/dist/triage/classify/index.js +11 -4
- package/dist/triage/help/registry-data.d.ts +2 -2
- package/dist/triage/help/registry-data.js +2 -2
- package/dist/triage/queue/constants.d.ts +4 -4
- package/dist/triage/queue/constants.js +4 -4
- package/dist/triage/queue/scope-walk.js +7 -1
- package/dist/triage/reconcile/reconcile.js +23 -3
- package/dist/triage/reconcile/types.d.ts +1 -1
- package/dist/triage/reconcile/types.js +1 -1
- package/dist/triage/refresh/refresh.js +9 -2
- package/dist/triage/scope/constants.d.ts +2 -2
- package/dist/triage/scope/constants.js +2 -2
- package/dist/triage/scope/renderers.js +7 -1
- package/dist/triage/smoketest/index.js +9 -6
- package/dist/triage/subscribe/index.d.ts +2 -2
- package/dist/triage/subscribe/index.js +2 -2
- package/dist/triage/summary/index.d.ts +2 -2
- package/dist/triage/summary/index.js +1 -1
- package/dist/triage/summary/reconcilable.d.ts +1 -1
- package/dist/triage/welcome/constants.d.ts +2 -2
- package/dist/triage/welcome/constants.js +2 -2
- package/dist/triage/welcome/onboard.js +8 -1
- package/dist/triage/welcome/prior-state.js +14 -2
- package/dist/triage/welcome/summary.js +14 -2
- package/dist/triage/welcome/writers.js +7 -1
- package/dist/validate-content/validate-strategy-output.js +11 -1
- package/dist/vbrief-build/parity-scenarios.js +3 -3
- package/dist/vbrief-reconcile/labels.js +16 -1
- package/dist/vbrief-reconcile/parity-scenarios.js +10 -10
- package/dist/vbrief-reconcile/umbrellas.js +16 -1
- package/dist/vbrief-validate/main.js +13 -1
- package/dist/vbrief-validate/precutover.js +33 -5
- package/dist/vbrief-validate/staleness.d.ts +1 -1
- package/dist/vbrief-validate/staleness.js +5 -2
- package/dist/verify-source/code-structure-validate.js +7 -1
- package/dist/xbrief-migrate/signpost.d.ts +4 -4
- package/dist/xbrief-migrate/signpost.js +11 -15
- package/package.json +3 -3
|
@@ -4,9 +4,9 @@ export declare const MAX_LINE_CHARS = 120;
|
|
|
4
4
|
export { DEFAULT_WIP_CAP, PROJECT_DEFINITION_REL_PATH };
|
|
5
5
|
export declare const CACHE_DIR_NAME = ".deft-cache";
|
|
6
6
|
export declare const CACHE_SOURCE = "github-issue";
|
|
7
|
-
export declare const CANDIDATES_LOG_REL_PATH = "
|
|
7
|
+
export declare const CANDIDATES_LOG_REL_PATH = "xbrief/.triage-cache/candidates.jsonl";
|
|
8
8
|
export { latestDecisions, readAuditLog } from "../actions/candidates-log.js";
|
|
9
|
-
export declare const SUMMARY_HISTORY_REL_PATH = "
|
|
9
|
+
export declare const SUMMARY_HISTORY_REL_PATH = "xbrief/.triage-cache/summary-history.jsonl";
|
|
10
10
|
export declare const SUMMARY_HISTORY_SCHEMA = "deft.triage.summary.v1";
|
|
11
11
|
/** D2 repeat-suppression window for session-start triage one-liner (#1122 / #1279). */
|
|
12
12
|
export declare const D2_SUPPRESSION_WINDOW_HOURS = 4;
|
|
@@ -18,7 +18,7 @@ export const CACHE_DIR_NAME = ".deft-cache";
|
|
|
18
18
|
export const CACHE_SOURCE = "github-issue";
|
|
19
19
|
export const CANDIDATES_LOG_REL_PATH = AUDIT_LOG_REL_PATH;
|
|
20
20
|
export { latestDecisions, readAuditLog } from "../actions/candidates-log.js";
|
|
21
|
-
export const SUMMARY_HISTORY_REL_PATH = "
|
|
21
|
+
export const SUMMARY_HISTORY_REL_PATH = "xbrief/.triage-cache/summary-history.jsonl";
|
|
22
22
|
export const SUMMARY_HISTORY_SCHEMA = "deft.triage.summary.v1";
|
|
23
23
|
/** D2 repeat-suppression window for session-start triage one-liner (#1122 / #1279). */
|
|
24
24
|
export const D2_SUPPRESSION_WINDOW_HOURS = 4;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Lifecycle folders scanned for reconcilable vBRIEFs (#1468). */
|
|
2
2
|
export declare const BACKFILL_FOLDERS: readonly ["proposed", "pending", "active"];
|
|
3
|
-
export declare const CANDIDATES_LOG_REL_PATH = "
|
|
3
|
+
export declare const CANDIDATES_LOG_REL_PATH = "xbrief/.triage-cache/candidates.jsonl";
|
|
4
4
|
/**
|
|
5
5
|
* Count reconcilable issues (#1468) — mirrors `triage_reconcile.count_reconcilable`.
|
|
6
6
|
*/
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
export declare const CACHE_DIR_NAME = ".deft-cache";
|
|
2
2
|
export declare const CACHE_SOURCE = "github-issue";
|
|
3
|
-
export declare const CANDIDATES_RELPATH: readonly ["
|
|
3
|
+
export declare const CANDIDATES_RELPATH: readonly ["xbrief", ".triage-cache", "candidates.jsonl"];
|
|
4
4
|
export declare const WIP_LIFECYCLE_DIRS: readonly ["pending", "active"];
|
|
5
5
|
export declare const AUDIT_LOG_REL_PATH = "meta/policy-changes.log";
|
|
6
6
|
export declare const DEFAULT_WIP_CAP = 20;
|
|
7
7
|
export declare const DEFAULT_RELIEF_AGE_DAYS = 30;
|
|
8
8
|
export declare const TRIAGE_SKILL_PATH = "skills/deft-directive-triage/SKILL.md";
|
|
9
9
|
export declare const WELCOME_AUDIT_TAG = "triage-welcome";
|
|
10
|
-
export declare const SUMMARY_HISTORY_REL_PATH = "
|
|
10
|
+
export declare const SUMMARY_HISTORY_REL_PATH = "xbrief/.triage-cache/summary-history.jsonl";
|
|
11
11
|
export declare const SUMMARY_HISTORY_SCHEMA = "deft.triage.summary.v1";
|
|
12
12
|
export declare const EMPTY_CACHE_LINE = "[triage] cache empty -- run task triage:bootstrap";
|
|
13
13
|
export declare const MAX_LINE_CHARS = 120;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
export const CACHE_DIR_NAME = ".deft-cache";
|
|
2
2
|
export const CACHE_SOURCE = "github-issue";
|
|
3
|
-
export const CANDIDATES_RELPATH = ["
|
|
3
|
+
export const CANDIDATES_RELPATH = ["xbrief", ".triage-cache", "candidates.jsonl"];
|
|
4
4
|
export const WIP_LIFECYCLE_DIRS = ["pending", "active"];
|
|
5
5
|
export const AUDIT_LOG_REL_PATH = "meta/policy-changes.log";
|
|
6
6
|
export const DEFAULT_WIP_CAP = 20;
|
|
7
7
|
export const DEFAULT_RELIEF_AGE_DAYS = 30;
|
|
8
8
|
export const TRIAGE_SKILL_PATH = "skills/deft-directive-triage/SKILL.md";
|
|
9
9
|
export const WELCOME_AUDIT_TAG = "triage-welcome";
|
|
10
|
-
export const SUMMARY_HISTORY_REL_PATH = "
|
|
10
|
+
export const SUMMARY_HISTORY_REL_PATH = "xbrief/.triage-cache/summary-history.jsonl";
|
|
11
11
|
export const SUMMARY_HISTORY_SCHEMA = "deft.triage.summary.v1";
|
|
12
12
|
export const EMPTY_CACHE_LINE = "[triage] cache empty -- run task triage:bootstrap";
|
|
13
13
|
export const MAX_LINE_CHARS = 120;
|
|
@@ -43,7 +43,14 @@ export function runOnboardMode(projectRoot, options = {}) {
|
|
|
43
43
|
out(`[welcome] --wip-cap must be a positive integer, got ${JSON.stringify(options.wipCap)}.`);
|
|
44
44
|
return failure(2);
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
let pdPath;
|
|
47
|
+
try {
|
|
48
|
+
pdPath = resolveProjectDefinitionPath(projectRoot);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
out(`[welcome] No xbrief/ layout found at ${projectRoot}. Run \`deft migrate:xbrief\` to convert your project from the legacy vbrief/ layout, or run project setup first (deft-directive-setup).`);
|
|
52
|
+
return failure(2);
|
|
53
|
+
}
|
|
47
54
|
if (!existsSync(pdPath)) {
|
|
48
55
|
out(`[welcome] No project definition found at ${pdPath}. Run project setup first (deft-directive-setup) before onboarding triage.`);
|
|
49
56
|
return failure(2);
|
|
@@ -5,7 +5,13 @@ import { readPlanPolicy } from "../../policy/plan-extensions.js";
|
|
|
5
5
|
import { resolveCandidatesLogPath } from "../cache-path.js";
|
|
6
6
|
import { CACHE_DIR_NAME, CACHE_SOURCE, DEFAULT_WIP_CAP, SUBSCRIPTION_PRESETS, WIP_LIFECYCLE_DIRS, } from "./constants.js";
|
|
7
7
|
function loadProjectDefinition(projectRoot) {
|
|
8
|
-
|
|
8
|
+
let path;
|
|
9
|
+
try {
|
|
10
|
+
path = resolveProjectDefinitionPath(projectRoot);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null; // No xbrief/ layout; no project definition.
|
|
14
|
+
}
|
|
9
15
|
if (!existsSync(path))
|
|
10
16
|
return null;
|
|
11
17
|
try {
|
|
@@ -44,7 +50,13 @@ export function candidatesLogPath(projectRoot) {
|
|
|
44
50
|
}
|
|
45
51
|
function countWip(projectRoot) {
|
|
46
52
|
let total = 0;
|
|
47
|
-
|
|
53
|
+
let root;
|
|
54
|
+
try {
|
|
55
|
+
root = resolveLifecycleRoot(projectRoot);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return 0; // No xbrief/ layout; no WIP.
|
|
59
|
+
}
|
|
48
60
|
for (const sub of WIP_LIFECYCLE_DIRS) {
|
|
49
61
|
const folder = join(root, sub);
|
|
50
62
|
if (!existsSync(folder))
|
|
@@ -74,7 +74,13 @@ function latestDecisions(entries) {
|
|
|
74
74
|
return out;
|
|
75
75
|
}
|
|
76
76
|
function countFilesystemInFlight(projectRoot) {
|
|
77
|
-
|
|
77
|
+
let activeDir;
|
|
78
|
+
try {
|
|
79
|
+
activeDir = resolveLifecycleFolder(projectRoot, "active");
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return 0; // No xbrief/ layout; nothing in-flight.
|
|
83
|
+
}
|
|
78
84
|
if (!existsSync(activeDir))
|
|
79
85
|
return 0;
|
|
80
86
|
let count = 0;
|
|
@@ -94,7 +100,13 @@ function countFilesystemInFlight(projectRoot) {
|
|
|
94
100
|
return count;
|
|
95
101
|
}
|
|
96
102
|
function isTriageScopeConfigured(projectRoot) {
|
|
97
|
-
|
|
103
|
+
let path;
|
|
104
|
+
try {
|
|
105
|
+
path = resolveProjectDefinitionPath(projectRoot);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return false; // No xbrief/ layout; triage scope not configured.
|
|
109
|
+
}
|
|
98
110
|
if (!existsSync(path))
|
|
99
111
|
return false;
|
|
100
112
|
try {
|
|
@@ -143,7 +143,13 @@ function daysInPending(path, now) {
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
export function previewWipRelief(projectRoot, olderThanDays = DEFAULT_RELIEF_AGE_DAYS) {
|
|
146
|
-
|
|
146
|
+
let pendingDir;
|
|
147
|
+
try {
|
|
148
|
+
pendingDir = resolveLifecycleFolder(projectRoot, "pending");
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return { olderThanDays, eligibleCount: 0, eligibleFiles: [], skippedCount: 0 };
|
|
152
|
+
}
|
|
147
153
|
if (!existsSync(pendingDir)) {
|
|
148
154
|
return { olderThanDays, eligibleCount: 0, eligibleFiles: [], skippedCount: 0 };
|
|
149
155
|
}
|
|
@@ -29,7 +29,17 @@ export function validateStrategyOutput(projectRoot, strict = false) {
|
|
|
29
29
|
const errors = [];
|
|
30
30
|
// Layout-aware (#2109 part 2a): resolve the lifecycle dir/suffix dynamically;
|
|
31
31
|
// identical to vbrief/ on today's tree.
|
|
32
|
-
|
|
32
|
+
let layout;
|
|
33
|
+
try {
|
|
34
|
+
layout = resolveLifecycleLayout(root);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
if (strict) {
|
|
38
|
+
errors.push("xbrief/ directory missing entirely. v0.20 strategies must emit at least " +
|
|
39
|
+
"xbrief/proposed/ (with date-prefixed files) + PROJECT-DEFINITION.xbrief.json.");
|
|
40
|
+
}
|
|
41
|
+
return errors;
|
|
42
|
+
}
|
|
33
43
|
const vbriefDir = layout.root;
|
|
34
44
|
if (!existsSync(vbriefDir)) {
|
|
35
45
|
if (strict) {
|
|
@@ -195,10 +195,10 @@ export function runParityScenario(name, ctx) {
|
|
|
195
195
|
}, { ipIndex: 1, dependencies: ["ip-0"], specRef: "../specification.vbrief.json" }),
|
|
196
196
|
};
|
|
197
197
|
case "project-definition-roundtrip": {
|
|
198
|
-
const vbriefDir = join(ctx.fixtureRoot, "
|
|
198
|
+
const vbriefDir = join(ctx.fixtureRoot, "xbrief");
|
|
199
199
|
mkdirSync(vbriefDir, { recursive: true });
|
|
200
200
|
const seed = {
|
|
201
|
-
|
|
201
|
+
xBRIEFInfo: { version: "0.8" },
|
|
202
202
|
plan: {
|
|
203
203
|
title: "Parity project",
|
|
204
204
|
status: "running",
|
|
@@ -206,7 +206,7 @@ export function runParityScenario(name, ctx) {
|
|
|
206
206
|
items: [],
|
|
207
207
|
},
|
|
208
208
|
};
|
|
209
|
-
const pdPath = join(vbriefDir, "PROJECT-DEFINITION.
|
|
209
|
+
const pdPath = join(vbriefDir, "PROJECT-DEFINITION.xbrief.json");
|
|
210
210
|
writeFileSync(pdPath, pythonJsonPretty(seed), "utf8");
|
|
211
211
|
let roundtrip = {};
|
|
212
212
|
projectDefinitionMutationLock(ctx.fixtureRoot, () => {
|
|
@@ -84,7 +84,22 @@ function hasUnresolvedDeps(swarm, knownIds) {
|
|
|
84
84
|
}
|
|
85
85
|
export function reconcileLabels(projectRoot, options = {}) {
|
|
86
86
|
const root = resolve(projectRoot);
|
|
87
|
-
|
|
87
|
+
let vbriefDir;
|
|
88
|
+
try {
|
|
89
|
+
vbriefDir = resolveLifecycleRoot(root);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return [
|
|
93
|
+
2,
|
|
94
|
+
{
|
|
95
|
+
changed: [],
|
|
96
|
+
unchanged: [],
|
|
97
|
+
skipped_no_ref: [],
|
|
98
|
+
errors: [],
|
|
99
|
+
dry_run: options.dryRun ?? false,
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
}
|
|
88
103
|
if (!existsSync(vbriefDir)) {
|
|
89
104
|
return [
|
|
90
105
|
2,
|
|
@@ -79,8 +79,8 @@ function specWith(items) {
|
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
81
|
function writeBrief(root, storyId, folder, extra = {}) {
|
|
82
|
-
const path = join(root, "
|
|
83
|
-
mkdirSync(join(root, "
|
|
82
|
+
const path = join(root, "xbrief", folder, `2026-05-21-${storyId}.xbrief.json`);
|
|
83
|
+
mkdirSync(join(root, "xbrief", folder), { recursive: true });
|
|
84
84
|
const statusMap = {
|
|
85
85
|
proposed: "proposed",
|
|
86
86
|
pending: "pending",
|
|
@@ -89,7 +89,7 @@ function writeBrief(root, storyId, folder, extra = {}) {
|
|
|
89
89
|
cancelled: "cancelled",
|
|
90
90
|
};
|
|
91
91
|
const data = {
|
|
92
|
-
|
|
92
|
+
xBRIEFInfo: { version: "0.8" },
|
|
93
93
|
plan: {
|
|
94
94
|
id: storyId,
|
|
95
95
|
title: storyId,
|
|
@@ -215,7 +215,7 @@ export function runParityScenario(name, options) {
|
|
|
215
215
|
};
|
|
216
216
|
}
|
|
217
217
|
case "graph-dry-run": {
|
|
218
|
-
mkdirSync(join(root, "
|
|
218
|
+
mkdirSync(join(root, "xbrief", "proposed"), { recursive: true });
|
|
219
219
|
writeBrief(root, "dep-done", "completed");
|
|
220
220
|
writeBrief(root, "child", "proposed", {
|
|
221
221
|
metadata: {
|
|
@@ -246,7 +246,7 @@ export function runParityScenario(name, options) {
|
|
|
246
246
|
};
|
|
247
247
|
}
|
|
248
248
|
case "graph-cycle": {
|
|
249
|
-
mkdirSync(join(root, "
|
|
249
|
+
mkdirSync(join(root, "xbrief", "proposed"), { recursive: true });
|
|
250
250
|
for (const id of ["a", "b"]) {
|
|
251
251
|
writeBrief(root, id, "proposed", {
|
|
252
252
|
metadata: {
|
|
@@ -274,12 +274,12 @@ export function runParityScenario(name, options) {
|
|
|
274
274
|
};
|
|
275
275
|
}
|
|
276
276
|
case "graph-missing-proposed": {
|
|
277
|
-
mkdirSync(join(root, "
|
|
277
|
+
mkdirSync(join(root, "xbrief"), { recursive: true });
|
|
278
278
|
const [code] = reconcileGraph(root, { dryRun: true });
|
|
279
279
|
return { scenario: name, ok: true, payload: { exitCode: code } };
|
|
280
280
|
}
|
|
281
281
|
case "labels-blocked-dry-run": {
|
|
282
|
-
mkdirSync(join(root, "
|
|
282
|
+
mkdirSync(join(root, "xbrief", "active"), { recursive: true });
|
|
283
283
|
writeBrief(root, "blk", "active", {
|
|
284
284
|
status: "blocked",
|
|
285
285
|
references: [
|
|
@@ -303,7 +303,7 @@ export function runParityScenario(name, options) {
|
|
|
303
303
|
};
|
|
304
304
|
}
|
|
305
305
|
case "labels-utf8-dry-run": {
|
|
306
|
-
mkdirSync(join(root, "
|
|
306
|
+
mkdirSync(join(root, "xbrief", "active"), { recursive: true });
|
|
307
307
|
writeBrief(root, "utf8", "active", {
|
|
308
308
|
references: [
|
|
309
309
|
{
|
|
@@ -322,7 +322,7 @@ export function runParityScenario(name, options) {
|
|
|
322
322
|
};
|
|
323
323
|
}
|
|
324
324
|
case "umbrellas-create-dry-run": {
|
|
325
|
-
mkdirSync(join(root, "
|
|
325
|
+
mkdirSync(join(root, "xbrief", "active"), { recursive: true });
|
|
326
326
|
writeBrief(root, "child-a", "active", {
|
|
327
327
|
metadata: { kind: "story", swarm: { depends_on: [] } },
|
|
328
328
|
});
|
|
@@ -350,7 +350,7 @@ export function runParityScenario(name, options) {
|
|
|
350
350
|
};
|
|
351
351
|
}
|
|
352
352
|
case "umbrellas-unchanged": {
|
|
353
|
-
mkdirSync(join(root, "
|
|
353
|
+
mkdirSync(join(root, "xbrief", "active"), { recursive: true });
|
|
354
354
|
writeBrief(root, "child-b", "active", {
|
|
355
355
|
metadata: { kind: "story", swarm: { depends_on: [] } },
|
|
356
356
|
});
|
|
@@ -333,7 +333,22 @@ export function nowIso(date = new Date()) {
|
|
|
333
333
|
}
|
|
334
334
|
export function reconcileUmbrellas(projectRoot, options = {}) {
|
|
335
335
|
const root = resolve(projectRoot);
|
|
336
|
-
|
|
336
|
+
let vbriefDir;
|
|
337
|
+
try {
|
|
338
|
+
vbriefDir = resolveLifecycleRoot(root);
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
return [
|
|
342
|
+
2,
|
|
343
|
+
{
|
|
344
|
+
changed: [],
|
|
345
|
+
unchanged: [],
|
|
346
|
+
skipped_no_ref: [],
|
|
347
|
+
errors: [],
|
|
348
|
+
dry_run: options.dryRun ?? false,
|
|
349
|
+
},
|
|
350
|
+
];
|
|
351
|
+
}
|
|
337
352
|
if (!existsSync(vbriefDir)) {
|
|
338
353
|
return [
|
|
339
354
|
2,
|
|
@@ -42,7 +42,19 @@ export function runValidate(argv) {
|
|
|
42
42
|
return 2;
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
let resolvedDir;
|
|
46
|
+
if (vbriefDir !== null) {
|
|
47
|
+
resolvedDir = vbriefDir;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
try {
|
|
51
|
+
resolvedDir = resolveLifecycleLayout(projectRoot ?? process.cwd()).root;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
process.stdout.write(`OK: No xbrief/ layout found at ${projectRoot ?? process.cwd()} -- skipping validation\n`);
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
46
58
|
if (!existsSync(resolvedDir)) {
|
|
47
59
|
process.stdout.write(`OK: No vbrief directory at ${resolvedDir} -- skipping validation\n`);
|
|
48
60
|
return 0;
|
|
@@ -8,7 +8,14 @@ export { DEPRECATION_SENTINEL as DEPRECATED_REDIRECT_SENTINEL };
|
|
|
8
8
|
const DEPRECATION_REDIRECT_PURPOSE = "<!-- Purpose: deprecation redirect -->";
|
|
9
9
|
const LIFECYCLE_FOLDERS = ["proposed", "pending", "active", "completed", "cancelled"];
|
|
10
10
|
export function missingLifecycleFolders(projectRoot) {
|
|
11
|
-
|
|
11
|
+
let lifecycleRoot;
|
|
12
|
+
try {
|
|
13
|
+
lifecycleRoot = resolveLifecycleRoot(projectRoot);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
// No xbrief/ layout present -- all lifecycle folders are missing.
|
|
17
|
+
return [...LIFECYCLE_FOLDERS];
|
|
18
|
+
}
|
|
12
19
|
return LIFECYCLE_FOLDERS.filter((folder) => !existsSync(join(lifecycleRoot, folder)));
|
|
13
20
|
}
|
|
14
21
|
function hasCompleteLifecycle(projectRoot) {
|
|
@@ -20,9 +27,15 @@ export function isDeprecationRedirect(content) {
|
|
|
20
27
|
}
|
|
21
28
|
/** Full-spec generated export (layout-resolved specification artifact source line). */
|
|
22
29
|
export function isGeneratedSpecificationExport(projectRoot, content) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
30
|
+
if (!content.includes(GENERATED_SPEC_PURPOSE) || !contentHasGeneratedSpecSource(content)) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
return existsSync(resolveSpecArtifactPath(projectRoot));
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
26
39
|
}
|
|
27
40
|
/** Return true for a fully current generated spec export (full-spec or greenfield). */
|
|
28
41
|
export function isCurrentGeneratedSpecification(projectRoot, content) {
|
|
@@ -91,7 +104,22 @@ export function detectPreCutover(projectRoot) {
|
|
|
91
104
|
reasons.push("PROJECT.md is a pre-v0.20 hand-authored doc (not a deprecation redirect)");
|
|
92
105
|
}
|
|
93
106
|
}
|
|
94
|
-
|
|
107
|
+
// Since #2112, resolveLifecycleLayout throws when no xbrief/ layout is present.
|
|
108
|
+
// Catch that error so the doctor can still diagnose pre-cutover state on unmigrated trees.
|
|
109
|
+
// However, an empty (greenfield) directory with no pre-cutover artifacts is NOT pre-cutover.
|
|
110
|
+
let layout = null;
|
|
111
|
+
try {
|
|
112
|
+
layout = resolveLifecycleLayout(projectRoot);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// No xbrief/ layout found. Only report a pre-cutover reason if legacy artifacts
|
|
116
|
+
// (SPECIFICATION.md / PROJECT.md) were already detected above; a bare empty directory
|
|
117
|
+
// without any artifacts is greenfield, not pre-cutover.
|
|
118
|
+
if (reasons.length > 0) {
|
|
119
|
+
reasons.push("xbrief/ lifecycle layout not found -- run `deft migrate:xbrief` first");
|
|
120
|
+
}
|
|
121
|
+
return { preCutover: reasons.length > 0, reasons };
|
|
122
|
+
}
|
|
95
123
|
if (existsSync(layout.root)) {
|
|
96
124
|
const missing = missingLifecycleFolders(projectRoot);
|
|
97
125
|
if (missing.length > 0) {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/** Warn if PRD.md or SPECIFICATION.md are stale relative to
|
|
1
|
+
/** Warn if PRD.md or SPECIFICATION.md are stale relative to the spec artifact (#398). */
|
|
2
2
|
export declare function checkRenderStaleness(vbriefDir: string): string[];
|
|
3
3
|
//# sourceMappingURL=staleness.d.ts.map
|
|
@@ -66,11 +66,14 @@ function checkSpecStaleness(specMdPath, narratives, items, title) {
|
|
|
66
66
|
}
|
|
67
67
|
return [];
|
|
68
68
|
}
|
|
69
|
-
/** Warn if PRD.md or SPECIFICATION.md are stale relative to
|
|
69
|
+
/** Warn if PRD.md or SPECIFICATION.md are stale relative to the spec artifact (#398). */
|
|
70
70
|
export function checkRenderStaleness(vbriefDir) {
|
|
71
71
|
const warnings = [];
|
|
72
72
|
const projectRoot = join(vbriefDir, "..");
|
|
73
|
-
|
|
73
|
+
// Prefer the migrated xbrief artifact; fall back to the legacy vbrief artifact.
|
|
74
|
+
const specPath = existsSync(join(vbriefDir, "specification.xbrief.json"))
|
|
75
|
+
? join(vbriefDir, "specification.xbrief.json")
|
|
76
|
+
: join(vbriefDir, "specification.vbrief.json");
|
|
74
77
|
if (!existsSync(specPath)) {
|
|
75
78
|
return warnings;
|
|
76
79
|
}
|
|
@@ -545,7 +545,13 @@ export function discoverCodeStructurePaths(projectRoot) {
|
|
|
545
545
|
paths.set(projectDef.replace(/\\/g, "/"), projectDef);
|
|
546
546
|
}
|
|
547
547
|
}
|
|
548
|
-
|
|
548
|
+
let vbriefRoot;
|
|
549
|
+
try {
|
|
550
|
+
vbriefRoot = resolveLifecycleRoot(projectRoot);
|
|
551
|
+
}
|
|
552
|
+
catch {
|
|
553
|
+
return [...paths.values()];
|
|
554
|
+
}
|
|
549
555
|
if (existsSync(vbriefRoot)) {
|
|
550
556
|
const stack = [vbriefRoot];
|
|
551
557
|
while (stack.length > 0) {
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
export declare function xbriefMigrationGuidance(): string;
|
|
3
3
|
/**
|
|
4
4
|
* One-line doctor / ritual signpost mirroring `renderPrecutoverLine` (#2110),
|
|
5
|
-
* reporting an unambiguous convergence state (#2270).
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* reporting an unambiguous convergence state (#2270 / #2112). As of #2112
|
|
6
|
+
* (0.73.0 MINOR), the legacy vbrief read path is removed; the `legacy-only` and
|
|
7
|
+
* `dual-populated` states are still reported here so the doctor can direct
|
|
8
|
+
* unmigrated-project operators to `deft migrate:xbrief` before the engine runs.
|
|
9
9
|
*/
|
|
10
10
|
export declare function renderXbriefMigrationLine(projectRoot: string): string;
|
|
11
11
|
//# sourceMappingURL=signpost.d.ts.map
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { detectXbriefConvergence } from "./detect.js";
|
|
2
2
|
/** Operator guidance for the TS-native xbrief rename (#2034 / #2110). */
|
|
3
3
|
export function xbriefMigrationGuidance() {
|
|
4
4
|
return "Run `deft migrate:xbrief` (or `task migrate:xbrief`) to convert vbrief/ to xbrief/ safely.";
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
7
|
* One-line doctor / ritual signpost mirroring `renderPrecutoverLine` (#2110),
|
|
8
|
-
* reporting an unambiguous convergence state (#2270).
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* reporting an unambiguous convergence state (#2270 / #2112). As of #2112
|
|
9
|
+
* (0.73.0 MINOR), the legacy vbrief read path is removed; the `legacy-only` and
|
|
10
|
+
* `dual-populated` states are still reported here so the doctor can direct
|
|
11
|
+
* unmigrated-project operators to `deft migrate:xbrief` before the engine runs.
|
|
12
12
|
*/
|
|
13
13
|
export function renderXbriefMigrationLine(projectRoot) {
|
|
14
14
|
const convergence = detectXbriefConvergence(projectRoot);
|
|
@@ -16,19 +16,15 @@ export function renderXbriefMigrationLine(projectRoot) {
|
|
|
16
16
|
if (convergence.state === "xbrief-marker") {
|
|
17
17
|
return "xBrief migration: converged -- xbrief active, vbrief legacy marker (read-compat).";
|
|
18
18
|
}
|
|
19
|
-
// Ambiguous
|
|
19
|
+
// Ambiguous: canonical xbrief/ (or none) plus a stray empty vbrief/.
|
|
20
20
|
if (convergence.state === "empty-vbrief") {
|
|
21
21
|
return `xBrief migration: converge pending -- xbrief active, empty legacy vbrief/ present. ${xbriefMigrationGuidance()}`;
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
if (
|
|
25
|
-
return
|
|
23
|
+
// Unmigrated: only vbrief/ found, or both roots populated without a marker.
|
|
24
|
+
if (convergence.state === "legacy-only" || convergence.state === "dual-populated") {
|
|
25
|
+
return `xBrief migration: migrate required -- ${convergence.state === "legacy-only" ? "only vbrief/ found, no xbrief/ layout" : "both vbrief/ and xbrief/ found without a migration marker"}. ${xbriefMigrationGuidance()}`;
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const remainder = reasons.length - shown.length;
|
|
30
|
-
const summary = shown.join("; ").replace(/\r?\n/g, " ");
|
|
31
|
-
const tail = remainder > 0 ? `${summary}; …and ${remainder} more marker(s)` : summary;
|
|
32
|
-
return `xBrief migration: legacy vbrief layout detected -- ${tail}. ${xbriefMigrationGuidance()}`;
|
|
27
|
+
// Fully migrated: xbrief active, no legacy vbrief/ present (or empty root with no content).
|
|
28
|
+
return "xBrief migration: none -- xbrief active, vbrief removed.";
|
|
33
29
|
}
|
|
34
30
|
//# sourceMappingURL=signpost.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.73.0",
|
|
4
4
|
"description": "TypeScript engine core for the Directive framework.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -281,8 +281,8 @@
|
|
|
281
281
|
"provenance": true
|
|
282
282
|
},
|
|
283
283
|
"dependencies": {
|
|
284
|
-
"@deftai/directive-content": "^0.
|
|
285
|
-
"@deftai/directive-types": "^0.
|
|
284
|
+
"@deftai/directive-content": "^0.73.0",
|
|
285
|
+
"@deftai/directive-types": "^0.73.0",
|
|
286
286
|
"archiver": "^8.0.0"
|
|
287
287
|
},
|
|
288
288
|
"scripts": {
|