@deftai/directive-core 0.71.1 → 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.
Files changed (94) hide show
  1. package/dist/category-b-namespace/index.js +16 -2
  2. package/dist/codebase/default-extractor.js +9 -1
  3. package/dist/codebase/map.js +1 -1
  4. package/dist/doctor/checks.d.ts +9 -0
  5. package/dist/doctor/checks.js +85 -5
  6. package/dist/doctor/main.js +2 -1
  7. package/dist/eval/health.js +7 -1
  8. package/dist/eval/readback.js +7 -1
  9. package/dist/events/attribution-enrichment.d.ts +35 -0
  10. package/dist/events/attribution-enrichment.js +74 -0
  11. package/dist/events/attribution-ledger.js +6 -1
  12. package/dist/init-deposit/gitignore.js +6 -0
  13. package/dist/init-deposit/hygiene.d.ts +24 -0
  14. package/dist/init-deposit/hygiene.js +55 -0
  15. package/dist/init-deposit/refresh.js +22 -6
  16. package/dist/init-deposit/scaffold.d.ts +11 -1
  17. package/dist/init-deposit/scaffold.js +4 -4
  18. package/dist/integration-e2e/helpers.js +7 -7
  19. package/dist/layout/index.d.ts +1 -1
  20. package/dist/layout/index.js +1 -1
  21. package/dist/layout/resolve.d.ts +47 -31
  22. package/dist/layout/resolve.js +93 -36
  23. package/dist/lifecycle/lifecycle-hygiene.d.ts +1 -1
  24. package/dist/lifecycle/lifecycle-hygiene.js +1 -1
  25. package/dist/migrate-preflight/index.js +10 -10
  26. package/dist/policy/index.js +3 -3
  27. package/dist/policy/resolve.d.ts +2 -2
  28. package/dist/policy/resolve.js +11 -3
  29. package/dist/policy/value-feedback-autoenable.d.ts +33 -0
  30. package/dist/policy/value-feedback-autoenable.js +67 -0
  31. package/dist/policy/value-feedback.d.ts +30 -5
  32. package/dist/policy/value-feedback.js +52 -34
  33. package/dist/policy/wip.d.ts +1 -1
  34. package/dist/policy/wip.js +15 -3
  35. package/dist/preflight-cache/evaluate.js +1 -1
  36. package/dist/release/native-steps.js +14 -2
  37. package/dist/render/export-spec.js +2 -2
  38. package/dist/render/project-render.js +40 -9
  39. package/dist/scope/decompose.js +12 -2
  40. package/dist/session/resume-conditions.js +2 -2
  41. package/dist/session/ritual-sentinel.d.ts +1 -1
  42. package/dist/session/ritual-sentinel.js +6 -4
  43. package/dist/slice/constants.d.ts +1 -1
  44. package/dist/slice/constants.js +1 -1
  45. package/dist/slice/project-context.js +1 -1
  46. package/dist/spec-authority/resolver.js +9 -2
  47. package/dist/swarm/complete-cohort.js +14 -1
  48. package/dist/swarm/readiness.js +33 -3
  49. package/dist/task-surface/index.js +3 -3
  50. package/dist/triage/actions/candidates-log.d.ts +1 -1
  51. package/dist/triage/bootstrap/gitignore.js +22 -10
  52. package/dist/triage/bootstrap/index.d.ts +1 -1
  53. package/dist/triage/bootstrap/index.js +9 -3
  54. package/dist/triage/cache-path.d.ts +1 -1
  55. package/dist/triage/cache-path.js +32 -6
  56. package/dist/triage/classify/index.d.ts +1 -1
  57. package/dist/triage/classify/index.js +11 -4
  58. package/dist/triage/help/registry-data.d.ts +2 -2
  59. package/dist/triage/help/registry-data.js +2 -2
  60. package/dist/triage/queue/constants.d.ts +4 -4
  61. package/dist/triage/queue/constants.js +4 -4
  62. package/dist/triage/queue/scope-walk.js +7 -1
  63. package/dist/triage/reconcile/reconcile.js +23 -3
  64. package/dist/triage/reconcile/types.d.ts +1 -1
  65. package/dist/triage/reconcile/types.js +1 -1
  66. package/dist/triage/refresh/refresh.js +9 -2
  67. package/dist/triage/scope/constants.d.ts +2 -2
  68. package/dist/triage/scope/constants.js +2 -2
  69. package/dist/triage/scope/renderers.js +7 -1
  70. package/dist/triage/smoketest/index.js +9 -6
  71. package/dist/triage/subscribe/index.d.ts +2 -2
  72. package/dist/triage/subscribe/index.js +2 -2
  73. package/dist/triage/summary/index.d.ts +2 -2
  74. package/dist/triage/summary/index.js +1 -1
  75. package/dist/triage/summary/reconcilable.d.ts +1 -1
  76. package/dist/triage/welcome/constants.d.ts +2 -2
  77. package/dist/triage/welcome/constants.js +2 -2
  78. package/dist/triage/welcome/onboard.js +8 -1
  79. package/dist/triage/welcome/prior-state.js +14 -2
  80. package/dist/triage/welcome/summary.js +14 -2
  81. package/dist/triage/welcome/writers.js +7 -1
  82. package/dist/validate-content/validate-strategy-output.js +11 -1
  83. package/dist/vbrief-build/parity-scenarios.js +3 -3
  84. package/dist/vbrief-reconcile/labels.js +16 -1
  85. package/dist/vbrief-reconcile/parity-scenarios.js +10 -10
  86. package/dist/vbrief-reconcile/umbrellas.js +16 -1
  87. package/dist/vbrief-validate/main.js +13 -1
  88. package/dist/vbrief-validate/precutover.js +33 -5
  89. package/dist/vbrief-validate/staleness.d.ts +1 -1
  90. package/dist/vbrief-validate/staleness.js +5 -2
  91. package/dist/verify-source/code-structure-validate.js +7 -1
  92. package/dist/xbrief-migrate/signpost.d.ts +4 -4
  93. package/dist/xbrief-migrate/signpost.js +11 -15
  94. package/package.json +3 -3
@@ -74,7 +74,13 @@ function latestDecisions(entries) {
74
74
  return out;
75
75
  }
76
76
  function countFilesystemInFlight(projectRoot) {
77
- const activeDir = resolveLifecycleFolder(projectRoot, "active");
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
- const path = resolveProjectDefinitionPath(projectRoot);
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
- const pendingDir = resolveLifecycleFolder(projectRoot, "pending");
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
- const layout = resolveLifecycleLayout(root);
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, "vbrief");
198
+ const vbriefDir = join(ctx.fixtureRoot, "xbrief");
199
199
  mkdirSync(vbriefDir, { recursive: true });
200
200
  const seed = {
201
- vBRIEFInfo: { version: "0.6" },
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.vbrief.json");
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
- const vbriefDir = resolveLifecycleRoot(root);
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, "vbrief", folder, `2026-05-21-${storyId}.vbrief.json`);
83
- mkdirSync(join(root, "vbrief", folder), { recursive: true });
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
- vBRIEFInfo: { version: "0.6" },
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, "vbrief", "proposed"), { recursive: true });
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, "vbrief", "proposed"), { recursive: true });
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, "vbrief"), { recursive: true });
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, "vbrief", "active"), { recursive: true });
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, "vbrief", "active"), { recursive: true });
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, "vbrief", "active"), { recursive: true });
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, "vbrief", "active"), { recursive: true });
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
- const vbriefDir = resolveLifecycleRoot(root);
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
- const resolvedDir = vbriefDir ?? resolveLifecycleLayout(projectRoot ?? process.cwd()).root;
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
- const lifecycleRoot = resolveLifecycleRoot(projectRoot);
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
- return (content.includes(GENERATED_SPEC_PURPOSE) &&
24
- contentHasGeneratedSpecSource(content) &&
25
- existsSync(resolveSpecArtifactPath(projectRoot)));
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
- const layout = resolveLifecycleLayout(projectRoot);
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 specification.vbrief.json (#398). */
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 specification.vbrief.json (#398). */
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
- const specPath = join(vbriefDir, "specification.vbrief.json");
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
- const vbriefRoot = resolveLifecycleRoot(projectRoot);
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). A migrated project reads
6
- * as `xbrief active` plus either `vbrief legacy marker` or `vbrief removed`; a
7
- * stray empty legacy root is called out as a pending convergence rather than a
8
- * generic "run migrate" dead end never a dual-empty-root ambiguity.
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 { detectLegacyVbriefLayout, detectXbriefConvergence } from "./detect.js";
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). A migrated project reads
9
- * as `xbrief active` plus either `vbrief legacy marker` or `vbrief removed`; a
10
- * stray empty legacy root is called out as a pending convergence rather than a
11
- * generic "run migrate" dead end never a dual-empty-root ambiguity.
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 dual-empty root: canonical xbrief/ (or none) plus a stray empty vbrief/.
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
- const { legacyLayout, reasons } = detectLegacyVbriefLayout(projectRoot);
24
- if (!legacyLayout) {
25
- return "xBrief migration: none -- xbrief active, vbrief removed.";
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
- const maxReasons = 3;
28
- const shown = reasons.slice(0, maxReasons);
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.71.1",
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.71.1",
285
- "@deftai/directive-types": "^0.71.1",
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": {