@deftai/directive-core 0.69.0 → 0.71.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 (103) hide show
  1. package/dist/branch/evaluate.js +7 -0
  2. package/dist/deposit/contain.d.ts +41 -0
  3. package/dist/deposit/contain.js +113 -0
  4. package/dist/deposit/copy-tree.js +8 -2
  5. package/dist/doctor/checks.d.ts +11 -0
  6. package/dist/doctor/checks.js +75 -2
  7. package/dist/doctor/doctor-state.js +2 -2
  8. package/dist/doctor/main.js +54 -1
  9. package/dist/doctor/manifest.d.ts +20 -0
  10. package/dist/doctor/manifest.js +22 -0
  11. package/dist/doctor/types.d.ts +8 -0
  12. package/dist/eval/crud-telemetry.d.ts +59 -0
  13. package/dist/eval/crud-telemetry.js +307 -0
  14. package/dist/eval/health.d.ts +52 -0
  15. package/dist/eval/health.js +240 -0
  16. package/dist/eval/readback.d.ts +38 -0
  17. package/dist/eval/readback.js +209 -0
  18. package/dist/eval/report.d.ts +53 -0
  19. package/dist/eval/report.js +161 -0
  20. package/dist/eval/run.d.ts +79 -0
  21. package/dist/eval/run.js +309 -0
  22. package/dist/events/attribution-constants.d.ts +13 -0
  23. package/dist/events/attribution-constants.js +18 -0
  24. package/dist/events/attribution-ledger.d.ts +43 -0
  25. package/dist/events/attribution-ledger.js +61 -0
  26. package/dist/index.d.ts +5 -0
  27. package/dist/index.js +5 -0
  28. package/dist/init-deposit/gitignore.js +5 -5
  29. package/dist/init-deposit/init-deposit.js +6 -0
  30. package/dist/init-deposit/refresh.js +5 -0
  31. package/dist/intake/issue-ingest.d.ts +11 -0
  32. package/dist/intake/issue-ingest.js +86 -11
  33. package/dist/layout/resolve.d.ts +2 -2
  34. package/dist/layout/resolve.js +2 -2
  35. package/dist/lifecycle/events.js +2 -0
  36. package/dist/policy/index.d.ts +2 -1
  37. package/dist/policy/index.js +15 -2
  38. package/dist/policy/plan-extensions.d.ts +31 -0
  39. package/dist/policy/plan-extensions.js +45 -0
  40. package/dist/policy/value-feedback.d.ts +56 -0
  41. package/dist/policy/value-feedback.js +284 -0
  42. package/dist/policy/wip.d.ts +2 -2
  43. package/dist/policy/wip.js +2 -2
  44. package/dist/preflight-cache/evaluate.js +4 -3
  45. package/dist/scope/audit-log.js +3 -3
  46. package/dist/scope/transition.js +16 -5
  47. package/dist/session/session-start.js +20 -0
  48. package/dist/slice/record.js +3 -3
  49. package/dist/swarm/routing.js +10 -3
  50. package/dist/triage/actions/candidates-log.d.ts +4 -4
  51. package/dist/triage/actions/candidates-log.js +4 -7
  52. package/dist/triage/bootstrap/gitignore.d.ts +7 -3
  53. package/dist/triage/bootstrap/gitignore.js +67 -37
  54. package/dist/triage/bootstrap/index.d.ts +1 -1
  55. package/dist/triage/bootstrap/index.js +4 -3
  56. package/dist/triage/bulk/index.d.ts +11 -2
  57. package/dist/triage/bulk/index.js +43 -6
  58. package/dist/triage/cache-path.d.ts +39 -0
  59. package/dist/triage/cache-path.js +98 -0
  60. package/dist/triage/help/registry-data.d.ts +53 -20
  61. package/dist/triage/help/registry-data.js +104 -20
  62. package/dist/triage/index.d.ts +2 -0
  63. package/dist/triage/index.js +2 -0
  64. package/dist/triage/queue/audit.js +2 -2
  65. package/dist/triage/queue/cache.js +2 -2
  66. package/dist/triage/reconcile/reconcile.js +4 -3
  67. package/dist/triage/scope/cli.d.ts +2 -1
  68. package/dist/triage/scope/cli.js +30 -6
  69. package/dist/triage/scope/mutations-core.js +3 -2
  70. package/dist/triage/scope/mutations.d.ts +10 -0
  71. package/dist/triage/scope/mutations.js +22 -0
  72. package/dist/triage/subscribe/index.d.ts +1 -1
  73. package/dist/triage/subscribe/index.js +4 -3
  74. package/dist/triage/summary/index.d.ts +2 -2
  75. package/dist/triage/summary/index.js +4 -3
  76. package/dist/triage/summary/reconcilable.d.ts +1 -1
  77. package/dist/triage/summary/reconcilable.js +3 -2
  78. package/dist/triage/welcome/constants.d.ts +3 -4
  79. package/dist/triage/welcome/constants.js +3 -4
  80. package/dist/triage/welcome/index.d.ts +1 -0
  81. package/dist/triage/welcome/index.js +1 -0
  82. package/dist/triage/welcome/onboard.d.ts +35 -0
  83. package/dist/triage/welcome/onboard.js +94 -0
  84. package/dist/triage/welcome/prior-state.js +3 -2
  85. package/dist/triage/welcome/summary.js +5 -4
  86. package/dist/umbrella-current-shape/index.d.ts +25 -1
  87. package/dist/umbrella-current-shape/index.js +84 -4
  88. package/dist/value/adoption-emit.d.ts +14 -0
  89. package/dist/value/adoption-emit.js +67 -0
  90. package/dist/value/adoption-registry.d.ts +62 -0
  91. package/dist/value/adoption-registry.js +214 -0
  92. package/dist/value/feedback-file.d.ts +67 -0
  93. package/dist/value/feedback-file.js +353 -0
  94. package/dist/value/friction-emit.d.ts +10 -0
  95. package/dist/value/friction-emit.js +24 -0
  96. package/dist/value/readback.d.ts +86 -0
  97. package/dist/value/readback.js +503 -0
  98. package/dist/vbrief-build/project-definition-io.d.ts +6 -0
  99. package/dist/vbrief-build/project-definition-io.js +7 -2
  100. package/dist/vbrief-validate/decomposition.js +1 -1
  101. package/dist/vbrief-validate/precutover.js +7 -3
  102. package/dist/wip-cap/evaluate.js +3 -0
  103. package/package.json +35 -3
@@ -0,0 +1,503 @@
1
+ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { runningInsideDeftRepo } from "../doctor/paths.js";
4
+ import { ALL_ATTRIBUTION_EVENT_NAMES } from "../events/attribution-constants.js";
5
+ import { DEFAULT_EVENT_LOG, readEvents } from "../lifecycle/events.js";
6
+ import { isValueFeedbackPathAllowed, resolveValueFeedback, } from "../policy/value-feedback.js";
7
+ import { resolveProjectRoot } from "../scope/project-context.js";
8
+ import { MAX_LINE_CHARS } from "../triage/welcome/constants.js";
9
+ import { probeAdoptionAtWorkBoundary } from "./adoption-emit.js";
10
+ import { probeFrictionAtWorkBoundary } from "./friction-emit.js";
11
+ /** Repeat-suppression window for the budgeted session readback (#1709 / #1279 parity). */
12
+ export const VALUE_READBACK_SUPPRESSION_HOURS = 4;
13
+ export const VALUE_READBACK_HISTORY_REL = join(".deft-cache", "value-readback-history.jsonl");
14
+ export const VALUE_READBACK_HISTORY_SCHEMA = "deft.value.readback.v1";
15
+ const ATTRIBUTION_NAME_SET = new Set(ALL_ATTRIBUTION_EVENT_NAMES);
16
+ /** Signal-class priority: value/bypass beat adoption (#1709 RFC nudge ordering). */
17
+ const SIGNAL_CLASS_PRIORITY = {
18
+ value: 0,
19
+ bypass: 1,
20
+ adoption: 2,
21
+ friction: 3,
22
+ };
23
+ function resolveLedgerPath(projectRoot, logPath) {
24
+ if (logPath !== undefined && logPath !== null) {
25
+ return resolve(logPath);
26
+ }
27
+ return resolve(projectRoot, DEFAULT_EVENT_LOG);
28
+ }
29
+ function parseDetectedAt(record) {
30
+ const raw = record.detected_at;
31
+ if (typeof raw !== "string" || raw.trim().length === 0) {
32
+ return null;
33
+ }
34
+ let text = raw.trim();
35
+ if (text.endsWith("Z")) {
36
+ text = `${text.slice(0, -1)}+00:00`;
37
+ }
38
+ const parsed = new Date(text);
39
+ return Number.isNaN(parsed.getTime()) ? null : parsed;
40
+ }
41
+ /** Read attribution ledger entries in emission order (#1709). */
42
+ export function readAttributionEvents(options) {
43
+ try {
44
+ const logPath = resolveLedgerPath(options.projectRoot, options.logPath);
45
+ const since = options.since ?? null;
46
+ return readEvents(logPath).filter((record) => {
47
+ if (typeof record?.event !== "string" || !ATTRIBUTION_NAME_SET.has(record.event)) {
48
+ return false;
49
+ }
50
+ if (!isRecordPayload(record.payload)) {
51
+ return false;
52
+ }
53
+ if (since === null) {
54
+ return true;
55
+ }
56
+ const at = parseDetectedAt(record);
57
+ return at !== null && at.getTime() >= since.getTime();
58
+ });
59
+ }
60
+ catch {
61
+ return [];
62
+ }
63
+ }
64
+ function signalClassOf(record) {
65
+ const payload = record.payload;
66
+ if (isRecordPayload(payload)) {
67
+ const raw = payload.signal_class;
68
+ if (raw === "value" || raw === "bypass" || raw === "adoption" || raw === "friction") {
69
+ return raw;
70
+ }
71
+ }
72
+ const prefix = record.event.split(":")[0];
73
+ if (prefix === "value" || prefix === "bypass" || prefix === "adoption" || prefix === "friction") {
74
+ return prefix;
75
+ }
76
+ return null;
77
+ }
78
+ function priorityOf(record) {
79
+ const cls = signalClassOf(record);
80
+ if (cls === null) {
81
+ return 99;
82
+ }
83
+ return SIGNAL_CLASS_PRIORITY[cls] ?? 99;
84
+ }
85
+ function truncate(text, maxChars) {
86
+ if (text.length <= maxChars) {
87
+ return text;
88
+ }
89
+ if (maxChars <= 3) {
90
+ return text.slice(0, maxChars);
91
+ }
92
+ return `${text.slice(0, maxChars - 3)}...`;
93
+ }
94
+ function payloadString(payload, key) {
95
+ const raw = payload[key];
96
+ if (typeof raw === "string" && raw.trim().length > 0) {
97
+ return raw.trim();
98
+ }
99
+ if (typeof raw === "number" && Number.isFinite(raw)) {
100
+ return String(raw);
101
+ }
102
+ return "";
103
+ }
104
+ /** Format one attributed session line from a concrete ledger event (#1709 attributed-only). */
105
+ export function formatAttributedSessionLine(record) {
106
+ const cls = signalClassOf(record) ?? "value";
107
+ const payload = isRecordPayload(record.payload) ? record.payload : {};
108
+ const source = payloadString(payload, "source");
109
+ const detail = payloadString(payload, "detail");
110
+ const capability = payloadString(payload, "capability");
111
+ switch (record.event) {
112
+ case "value:gate-catch": {
113
+ const tail = detail.length > 0 ? `: ${detail}` : "";
114
+ return `[value] Gate catch (${source || "gate"})${tail}`;
115
+ }
116
+ case "value:wip-cap-protect": {
117
+ const count = payloadString(payload, "count");
118
+ const cap = payloadString(payload, "cap");
119
+ const pair = count && cap ? ` ${count}/${cap}` : "";
120
+ return `[value] WIP cap protected in-flight scope${pair}.`;
121
+ }
122
+ case "bypass:off-flow": {
123
+ const tail = detail.length > 0 ? `: ${detail}` : "";
124
+ return `[boundary] Off-flow signal (${source || "bypass"})${tail}`;
125
+ }
126
+ case "adoption:unused-capability": {
127
+ const tail = detail.length > 0 ? `: ${detail}` : capability;
128
+ return `[adoption] Unused capability${tail ? ` (${tail})` : ""}.`;
129
+ }
130
+ case "friction:directive-gap": {
131
+ const tail = detail.length > 0 ? `: ${detail}` : "";
132
+ return `[friction] Directive gap (${source || "friction"})${tail}`;
133
+ }
134
+ default:
135
+ return `[${cls}] ${record.event}`;
136
+ }
137
+ }
138
+ /** Pick the single highest-priority recent attribution for the session one-liner. */
139
+ export function selectSessionAttribution(events) {
140
+ if (events.length === 0) {
141
+ return null;
142
+ }
143
+ const sorted = [...events].sort((a, b) => {
144
+ const pri = priorityOf(a) - priorityOf(b);
145
+ if (pri !== 0) {
146
+ return pri;
147
+ }
148
+ const aAt = parseDetectedAt(a)?.getTime() ?? 0;
149
+ const bAt = parseDetectedAt(b)?.getTime() ?? 0;
150
+ return bAt - aAt;
151
+ });
152
+ return sorted[0] ?? null;
153
+ }
154
+ function parseJsonObjectOrNull(text) {
155
+ try {
156
+ const parsed = JSON.parse(text);
157
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
158
+ return parsed;
159
+ }
160
+ }
161
+ catch {
162
+ // malformed history line
163
+ }
164
+ return null;
165
+ }
166
+ function isRecordPayload(payload) {
167
+ return payload !== null && typeof payload === "object" && !Array.isArray(payload);
168
+ }
169
+ function historyPath(projectRoot) {
170
+ return resolve(projectRoot, VALUE_READBACK_HISTORY_REL);
171
+ }
172
+ function readLastHistoryRecord(path) {
173
+ if (!existsSync(path)) {
174
+ return null;
175
+ }
176
+ let text;
177
+ try {
178
+ text = readFileSync(path, "utf8");
179
+ }
180
+ catch {
181
+ return null;
182
+ }
183
+ const lines = text.split("\n").filter((line) => line.trim().length > 0);
184
+ if (lines.length === 0) {
185
+ return null;
186
+ }
187
+ const last = lines[lines.length - 1];
188
+ if (last === undefined) {
189
+ return null;
190
+ }
191
+ return parseJsonObjectOrNull(last);
192
+ }
193
+ function parseHistoryEmittedAt(record) {
194
+ const raw = record.emitted_at;
195
+ if (typeof raw !== "string" || raw.trim().length === 0) {
196
+ return null;
197
+ }
198
+ let candidate = raw.trim();
199
+ if (candidate.endsWith("Z")) {
200
+ candidate = `${candidate.slice(0, -1)}+00:00`;
201
+ }
202
+ const parsed = new Date(candidate);
203
+ return Number.isNaN(parsed.getTime()) ? null : parsed;
204
+ }
205
+ /** True when the same attribution event id was read back within the suppression window. */
206
+ export function shouldSuppressSessionReadback(eventId, historyFile, options = {}) {
207
+ const prior = readLastHistoryRecord(historyFile);
208
+ if (prior === null) {
209
+ return false;
210
+ }
211
+ const emittedAt = parseHistoryEmittedAt(prior);
212
+ if (emittedAt === null) {
213
+ return false;
214
+ }
215
+ const now = options.now ?? new Date();
216
+ const ageMs = now.getTime() - emittedAt.getTime();
217
+ if (ageMs < 0 || ageMs >= VALUE_READBACK_SUPPRESSION_HOURS * 3_600_000) {
218
+ return false;
219
+ }
220
+ return prior.event_id === eventId;
221
+ }
222
+ function appendReadbackHistory(projectRoot, eventId, line, options = {}) {
223
+ const path = historyPath(projectRoot);
224
+ const record = {
225
+ schema: VALUE_READBACK_HISTORY_SCHEMA,
226
+ emitted_at: (options.now ?? new Date()).toISOString().replace(/\.\d{3}Z$/, "Z"),
227
+ event_id: eventId,
228
+ line,
229
+ };
230
+ try {
231
+ mkdirSync(join(path, ".."), { recursive: true });
232
+ appendFileSync(path, `${JSON.stringify(record)}\n`, "utf8");
233
+ }
234
+ catch {
235
+ // observability only
236
+ }
237
+ }
238
+ function maintainerReadbackDisabled(projectRoot) {
239
+ if (!runningInsideDeftRepo(projectRoot)) {
240
+ return false;
241
+ }
242
+ return process.env.DEFT_VALUE_SELF_DOGFOOD !== "1";
243
+ }
244
+ /** Budgeted session one-liner — silent when gated, empty, or debounced (#1709). */
245
+ export function renderSessionReadback(projectRoot, options = {}) {
246
+ try {
247
+ const root = resolve(projectRoot);
248
+ if (maintainerReadbackDisabled(root)) {
249
+ return { line: null, suppressed: false, gated: true };
250
+ }
251
+ const policy = options.policyOverride ?? resolveValueFeedback(root);
252
+ if (isValueFeedbackPathAllowed("emitEvents", policy)) {
253
+ probeAdoptionAtWorkBoundary(root, {
254
+ logPath: options.logPath,
255
+ policyOverride: policy,
256
+ });
257
+ probeFrictionAtWorkBoundary(root, {
258
+ logPath: options.logPath,
259
+ policyOverride: policy,
260
+ });
261
+ }
262
+ if (!isValueFeedbackPathAllowed("sessionLine", policy)) {
263
+ return { line: null, suppressed: false, gated: true };
264
+ }
265
+ const events = readAttributionEvents({ projectRoot: root, logPath: options.logPath });
266
+ if (events.length === 0) {
267
+ return { line: null, suppressed: false, gated: false };
268
+ }
269
+ const selected = selectSessionAttribution(events);
270
+ if (selected === null) {
271
+ return { line: null, suppressed: false, gated: false };
272
+ }
273
+ const hist = historyPath(root);
274
+ if (shouldSuppressSessionReadback(selected.id, hist, { now: options.now })) {
275
+ return { line: null, suppressed: true, gated: false };
276
+ }
277
+ const maxChars = options.maxChars ?? MAX_LINE_CHARS;
278
+ const line = truncate(formatAttributedSessionLine(selected), maxChars);
279
+ if (options.writeHistory !== false) {
280
+ appendReadbackHistory(root, selected.id, line, { now: options.now });
281
+ }
282
+ return { line, suppressed: false, gated: false };
283
+ }
284
+ catch {
285
+ return { line: null, suppressed: false, gated: false };
286
+ }
287
+ }
288
+ /** Emit the session readback line to stdout when present. Returns the line or null. */
289
+ export function emitSessionValueReadback(projectRoot, options = {}) {
290
+ const result = renderSessionReadback(projectRoot, options);
291
+ if (result.line === null) {
292
+ return null;
293
+ }
294
+ const out = options.output ?? ((line) => process.stdout.write(`${line}\n`));
295
+ out(result.line);
296
+ return result.line;
297
+ }
298
+ export function parseWindowMs(raw, defaultDays = 7) {
299
+ if (raw === undefined || raw.trim().length === 0) {
300
+ return defaultDays * 86_400_000;
301
+ }
302
+ const match = /^(\d+)([dhm])$/i.exec(raw.trim());
303
+ if (match === null) {
304
+ return defaultDays * 86_400_000;
305
+ }
306
+ const amount = Number.parseInt(match[1] ?? "", 10);
307
+ if (!Number.isFinite(amount) || amount <= 0) {
308
+ return defaultDays * 86_400_000;
309
+ }
310
+ const unit = (match[2] ?? "d").toLowerCase();
311
+ if (unit === "h") {
312
+ return amount * 3_600_000;
313
+ }
314
+ if (unit === "m") {
315
+ return amount * 60_000;
316
+ }
317
+ return amount * 86_400_000;
318
+ }
319
+ function formatWindowLabel(windowMs) {
320
+ const days = Math.round(windowMs / 86_400_000);
321
+ if (days >= 1 && days * 86_400_000 === windowMs) {
322
+ return `${days}d`;
323
+ }
324
+ const hours = Math.round(windowMs / 3_600_000);
325
+ if (hours >= 1 && hours * 3_600_000 === windowMs) {
326
+ return `${hours}h`;
327
+ }
328
+ return `${windowMs}ms`;
329
+ }
330
+ /** Build attributed-value trend counts for pull-based value:show (#1709). */
331
+ export function computeValueShowTrend(projectRoot, options = {}) {
332
+ const windowMs = options.windowMs ?? 7 * 86_400_000;
333
+ const now = options.now ?? new Date();
334
+ const since = new Date(now.getTime() - windowMs);
335
+ const events = readAttributionEvents({
336
+ projectRoot,
337
+ logPath: options.logPath,
338
+ since,
339
+ });
340
+ const byClass = {
341
+ value: 0,
342
+ bypass: 0,
343
+ adoption: 0,
344
+ friction: 0,
345
+ };
346
+ const byEvent = {};
347
+ for (const record of events) {
348
+ const cls = signalClassOf(record);
349
+ if (cls !== null) {
350
+ byClass[cls] += 1;
351
+ }
352
+ byEvent[record.event] = (byEvent[record.event] ?? 0) + 1;
353
+ }
354
+ return {
355
+ windowLabel: formatWindowLabel(windowMs),
356
+ windowMs,
357
+ total: events.length,
358
+ byClass,
359
+ byEvent,
360
+ recent: events.slice(-5),
361
+ };
362
+ }
363
+ export function formatValueShowReport(trend) {
364
+ if (trend.total === 0) {
365
+ return (`[value] No attributed signals in the last ${trend.windowLabel} ` +
366
+ "(ledger empty for this window).\n" +
367
+ "Inspect policy: `task policy:show --field=valueFeedback`.\n");
368
+ }
369
+ const classParts = ["value", "bypass", "adoption", "friction"]
370
+ .filter((key) => trend.byClass[key] > 0)
371
+ .map((key) => `${key}=${trend.byClass[key]}`);
372
+ const eventParts = Object.entries(trend.byEvent)
373
+ .sort(([a], [b]) => a.localeCompare(b))
374
+ .map(([name, count]) => `${name}: ${count}`);
375
+ const lines = [
376
+ `[value] Attributed signals (${trend.windowLabel}): ${trend.total} total`,
377
+ ` classes: ${classParts.join(", ")}`,
378
+ ` events: ${eventParts.join("; ")}`,
379
+ "Pull detail any time with `task value:show -- --window=30d --format=json`.",
380
+ ];
381
+ return `${lines.join("\n")}\n`;
382
+ }
383
+ /** Pull-based value:show CLI core (#1709). */
384
+ export function runValueShow(options) {
385
+ const rootRaw = resolveProjectRoot(options.projectRoot ?? undefined);
386
+ if (rootRaw === null) {
387
+ return {
388
+ exitCode: 2,
389
+ gated: false,
390
+ empty: true,
391
+ text: "Error: could not resolve project root.\n",
392
+ trend: null,
393
+ };
394
+ }
395
+ const root = resolve(rootRaw);
396
+ if (maintainerReadbackDisabled(root)) {
397
+ return {
398
+ exitCode: 0,
399
+ gated: true,
400
+ empty: true,
401
+ text: "[value] Skipped: maintainer framework repo (set DEFT_VALUE_SELF_DOGFOOD=1 to dogfood).\n",
402
+ trend: null,
403
+ };
404
+ }
405
+ const policy = options.policyOverride ?? resolveValueFeedback(root);
406
+ if (!policy.enabled) {
407
+ return {
408
+ exitCode: 1,
409
+ gated: true,
410
+ empty: true,
411
+ text: "[value] Blocked: plan.policy.valueFeedback is OFF. " +
412
+ "Enable with `task policy:enable-value-feedback -- --confirm`.\n",
413
+ trend: null,
414
+ };
415
+ }
416
+ const windowMs = parseWindowMs(options.window);
417
+ const trend = computeValueShowTrend(root, { windowMs, logPath: options.logPath });
418
+ const empty = trend.total === 0;
419
+ if (options.format === "json") {
420
+ return {
421
+ exitCode: 0,
422
+ gated: false,
423
+ empty,
424
+ text: `${JSON.stringify(trend, null, 2)}\n`,
425
+ trend,
426
+ };
427
+ }
428
+ return {
429
+ exitCode: 0,
430
+ gated: false,
431
+ empty,
432
+ text: formatValueShowReport(trend),
433
+ trend,
434
+ };
435
+ }
436
+ export function parseValueShowArgs(argv) {
437
+ const out = {};
438
+ for (let i = 0; i < argv.length; i += 1) {
439
+ const arg = argv[i];
440
+ if (arg === "--format") {
441
+ const raw = argv[++i];
442
+ if (raw === "json" || raw === "text") {
443
+ out.format = raw;
444
+ }
445
+ else {
446
+ return { ...out, error: `--format expects text|json, got ${JSON.stringify(raw)}` };
447
+ }
448
+ }
449
+ else if (arg?.startsWith("--format=")) {
450
+ const raw = arg.slice("--format=".length);
451
+ if (raw === "json" || raw === "text") {
452
+ out.format = raw;
453
+ }
454
+ else {
455
+ return { ...out, error: `--format expects text|json, got ${JSON.stringify(raw)}` };
456
+ }
457
+ }
458
+ else if (arg === "--window") {
459
+ const raw = argv[++i];
460
+ if (raw === undefined) {
461
+ return { ...out, error: "--window requires a value (e.g. --window=7d)" };
462
+ }
463
+ out.window = raw;
464
+ }
465
+ else if (arg?.startsWith("--window=")) {
466
+ out.window = arg.slice("--window=".length);
467
+ }
468
+ else if (arg === "--project-root") {
469
+ const raw = argv[++i];
470
+ if (raw === undefined) {
471
+ return { ...out, error: "--project-root requires a path" };
472
+ }
473
+ out.projectRoot = raw;
474
+ }
475
+ else if (arg?.startsWith("--project-root=")) {
476
+ out.projectRoot = arg.slice("--project-root=".length);
477
+ }
478
+ else if (arg.startsWith("-")) {
479
+ return { ...out, error: `unrecognized argument: ${arg}` };
480
+ }
481
+ }
482
+ return out;
483
+ }
484
+ /** CLI entry for value:show (#1709). */
485
+ export function valueShowMain(argv = process.argv.slice(2)) {
486
+ const args = parseValueShowArgs(argv);
487
+ if (args.error !== undefined) {
488
+ process.stderr.write(`value:show: ${args.error}\n`);
489
+ return 2;
490
+ }
491
+ const result = runValueShow({
492
+ projectRoot: args.projectRoot,
493
+ window: args.window,
494
+ format: args.format ?? "text",
495
+ });
496
+ process.stdout.write(result.text);
497
+ return result.exitCode;
498
+ }
499
+ /** CLI module entrypoint for dispatch (#1709). */
500
+ export function mainEntry(argv = process.argv.slice(2)) {
501
+ return valueShowMain(argv);
502
+ }
503
+ //# sourceMappingURL=readback.js.map
@@ -1,4 +1,10 @@
1
1
  import type { JsonObject } from "./types.js";
2
+ /**
3
+ * Absolute path to the PROJECT-DEFINITION artifact. Layout-aware (#2302):
4
+ * resolves `xbrief/PROJECT-DEFINITION.xbrief.json` on a migrated tree, else the
5
+ * legacy `vbrief/PROJECT-DEFINITION.vbrief.json`, so loader not-found messages
6
+ * name the path that actually applies to the project's layout.
7
+ */
2
8
  export declare function projectDefinitionPath(projectRoot: string): string;
3
9
  export interface MutationLockDeps {
4
10
  readonly sleepMs?: (ms: number) => void;
@@ -2,12 +2,17 @@ import { randomBytes } from "node:crypto";
2
2
  import { closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync, writeSync, } from "node:fs";
3
3
  import { dirname, join, resolve } from "node:path";
4
4
  import { resolveProjectDefinitionPath } from "../layout/resolve.js";
5
- import { PROJECT_DEFINITION_REL_PATH } from "./constants.js";
6
5
  import { pythonJsonPretty } from "./json.js";
7
6
  import { ProjectDefinitionIOError } from "./types.js";
8
7
  const mutationThreadLock = { held: false };
8
+ /**
9
+ * Absolute path to the PROJECT-DEFINITION artifact. Layout-aware (#2302):
10
+ * resolves `xbrief/PROJECT-DEFINITION.xbrief.json` on a migrated tree, else the
11
+ * legacy `vbrief/PROJECT-DEFINITION.vbrief.json`, so loader not-found messages
12
+ * name the path that actually applies to the project's layout.
13
+ */
9
14
  export function projectDefinitionPath(projectRoot) {
10
- return join(projectRoot, PROJECT_DEFINITION_REL_PATH);
15
+ return resolveProjectDefinitionPath(resolve(projectRoot));
11
16
  }
12
17
  function defaultSleep(ms) {
13
18
  const end = Date.now() + ms;
@@ -30,7 +30,7 @@ export function validateNoRootDecompositionDrafts(vbriefDir) {
30
30
  }
31
31
  if (looksLikeDecompositionDraft(data)) {
32
32
  errors.push(`${path}: decomposition draft JSON must not live at workspace root; ` +
33
- "write temporary proposals under vbrief/.eval/decompositions/");
33
+ "write temporary proposals under vbrief/.triage-cache/decompositions/");
34
34
  }
35
35
  }
36
36
  return errors;
@@ -103,9 +103,13 @@ export function detectPreCutover(projectRoot) {
103
103
  /** Last release that ships `scripts/migrate_vbrief.py` on the consumer deposit path (#2068). */
104
104
  export const FROZEN_PRECUTOVER_MIGRATION_TAG = "v0.59.0";
105
105
  export function frozenPreCutoverMigrationGuidance() {
106
- return (`Current npm releases no longer ship in-product \`task migrate:vbrief\`. Pin framework ${FROZEN_PRECUTOVER_MIGRATION_TAG} ` +
107
- `(frozen Go installer or git tag), install Python 3.11+ and uv, run \`task migrate:vbrief\` once from that payload, ` +
108
- `then upgrade to current npm. See UPGRADING.md § Frozen pre-v0.20 document-model migration (#2068).`);
106
+ return (`Current npm releases no longer ship in-product \`task migrate:vbrief\`. Best-effort path (anchored on the ` +
107
+ `${FROZEN_PRECUTOVER_MIGRATION_TAG} git tag, from which GitHub serves a source tarball on demand): pin framework ` +
108
+ `${FROZEN_PRECUTOVER_MIGRATION_TAG}, install Python 3.11+ and uv, run \`task migrate:vbrief\` once from that payload, ` +
109
+ `then upgrade to current npm. This is a two-hop chain: pre-v0.20 flat model -> vBRIEF v0.6 (on ${FROZEN_PRECUTOVER_MIGRATION_TAG}) ` +
110
+ `-> xBRIEF via \`deft migrate:xbrief\` on current npm. If the ${FROZEN_PRECUTOVER_MIGRATION_TAG} payload is unavailable, ` +
111
+ `fall back to a manual fresh start: \`directive init\` a new project on current npm and hand-port your spec content. ` +
112
+ `See UPGRADING.md § Frozen pre-v0.20 document-model migration (#2068).`);
109
113
  }
110
114
  export function renderPrecutoverLine(projectRoot) {
111
115
  const { preCutover, reasons } = detectPreCutover(projectRoot);
@@ -1,4 +1,5 @@
1
1
  import { resolve } from "node:path";
2
+ import { recordBypassSignal, recordWipCapProtect } from "../events/attribution-ledger.js";
2
3
  import { countVbriefWip, resolveWipCap } from "../policy/wip.js";
3
4
  function formatRefusal(count, cap, projectRoot) {
4
5
  return (`❌ verify:wip-cap: ${count}/${cap} in pending/+active/ ` +
@@ -40,6 +41,7 @@ export function evaluate(projectRoot, options = {}) {
40
41
  };
41
42
  }
42
43
  if (allowOverCap) {
44
+ recordBypassSignal(root, "verify:wip-cap", `--allow-over-cap passed (${count}/${cap} in pending/+active/)`);
43
45
  if (quiet) {
44
46
  return { code: 0, message: "", stream: "none" };
45
47
  }
@@ -53,6 +55,7 @@ export function evaluate(projectRoot, options = {}) {
53
55
  stream: "stderr",
54
56
  };
55
57
  }
58
+ recordWipCapProtect(root, count, cap);
56
59
  return {
57
60
  code: 1,
58
61
  message: formatRefusal(count, cap, root),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deftai/directive-core",
3
- "version": "0.69.0",
3
+ "version": "0.71.0",
4
4
  "description": "TypeScript engine core for the Directive framework.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -70,6 +70,38 @@
70
70
  "types": "./dist/doctor/index.d.ts",
71
71
  "default": "./dist/doctor/index.js"
72
72
  },
73
+ "./eval": {
74
+ "types": "./dist/eval/health.d.ts",
75
+ "default": "./dist/eval/health.js"
76
+ },
77
+ "./eval/health": {
78
+ "types": "./dist/eval/health.d.ts",
79
+ "default": "./dist/eval/health.js"
80
+ },
81
+ "./eval/crud-telemetry": {
82
+ "types": "./dist/eval/crud-telemetry.d.ts",
83
+ "default": "./dist/eval/crud-telemetry.js"
84
+ },
85
+ "./value/feedback-file": {
86
+ "types": "./dist/value/feedback-file.d.ts",
87
+ "default": "./dist/value/feedback-file.js"
88
+ },
89
+ "./value/readback": {
90
+ "types": "./dist/value/readback.d.ts",
91
+ "default": "./dist/value/readback.js"
92
+ },
93
+ "./events": {
94
+ "types": "./dist/events/attribution-ledger.d.ts",
95
+ "default": "./dist/events/attribution-ledger.js"
96
+ },
97
+ "./eval/run": {
98
+ "types": "./dist/eval/run.d.ts",
99
+ "default": "./dist/eval/run.js"
100
+ },
101
+ "./eval/report": {
102
+ "types": "./dist/eval/report.d.ts",
103
+ "default": "./dist/eval/report.js"
104
+ },
73
105
  "./triage": {
74
106
  "types": "./dist/triage/index.d.ts",
75
107
  "default": "./dist/triage/index.js"
@@ -249,8 +281,8 @@
249
281
  "provenance": true
250
282
  },
251
283
  "dependencies": {
252
- "@deftai/directive-content": "^0.69.0",
253
- "@deftai/directive-types": "^0.69.0",
284
+ "@deftai/directive-content": "^0.71.0",
285
+ "@deftai/directive-types": "^0.71.0",
254
286
  "archiver": "^8.0.0"
255
287
  },
256
288
  "scripts": {