@deftai/directive-core 0.70.0 → 0.71.1

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 (79) hide show
  1. package/dist/branch/evaluate.js +7 -0
  2. package/dist/doctor/doctor-state.js +2 -2
  3. package/dist/eval/crud-telemetry.d.ts +59 -0
  4. package/dist/eval/crud-telemetry.js +307 -0
  5. package/dist/eval/health.d.ts +52 -0
  6. package/dist/eval/health.js +240 -0
  7. package/dist/eval/readback.d.ts +38 -0
  8. package/dist/eval/readback.js +209 -0
  9. package/dist/eval/report.d.ts +53 -0
  10. package/dist/eval/report.js +161 -0
  11. package/dist/eval/run.d.ts +79 -0
  12. package/dist/eval/run.js +309 -0
  13. package/dist/events/attribution-constants.d.ts +13 -0
  14. package/dist/events/attribution-constants.js +18 -0
  15. package/dist/events/attribution-ledger.d.ts +43 -0
  16. package/dist/events/attribution-ledger.js +61 -0
  17. package/dist/index.d.ts +5 -0
  18. package/dist/index.js +5 -0
  19. package/dist/init-deposit/gitignore.js +15 -5
  20. package/dist/intake/candidates-log.d.ts +0 -1
  21. package/dist/intake/candidates-log.js +5 -2
  22. package/dist/intake/issue-ingest.d.ts +8 -0
  23. package/dist/intake/issue-ingest.js +19 -1
  24. package/dist/intake/reconcile-issues.js +9 -2
  25. package/dist/layout/resolve.d.ts +2 -2
  26. package/dist/layout/resolve.js +2 -2
  27. package/dist/lifecycle/events.js +2 -0
  28. package/dist/orchestration/subagent-monitor.js +2 -2
  29. package/dist/policy/index.d.ts +2 -1
  30. package/dist/policy/index.js +15 -2
  31. package/dist/policy/value-feedback.d.ts +56 -0
  32. package/dist/policy/value-feedback.js +284 -0
  33. package/dist/preflight-cache/evaluate.js +4 -3
  34. package/dist/scope/audit-log.js +3 -3
  35. package/dist/scope/transition.js +16 -5
  36. package/dist/session/session-start.js +20 -0
  37. package/dist/slice/record.js +3 -3
  38. package/dist/task-surface/index.js +4 -1
  39. package/dist/triage/actions/candidates-log.d.ts +4 -4
  40. package/dist/triage/actions/candidates-log.js +4 -7
  41. package/dist/triage/actions/index.js +8 -32
  42. package/dist/triage/bootstrap/gitignore.d.ts +9 -3
  43. package/dist/triage/bootstrap/gitignore.js +77 -40
  44. package/dist/triage/bootstrap/index.d.ts +1 -1
  45. package/dist/triage/bootstrap/index.js +4 -3
  46. package/dist/triage/bulk/index.js +2 -2
  47. package/dist/triage/cache-path.d.ts +41 -0
  48. package/dist/triage/cache-path.js +115 -0
  49. package/dist/triage/help/registry-data.d.ts +48 -15
  50. package/dist/triage/help/registry-data.js +86 -15
  51. package/dist/triage/index.d.ts +2 -0
  52. package/dist/triage/index.js +2 -0
  53. package/dist/triage/queue/audit.js +2 -2
  54. package/dist/triage/queue/cache.js +2 -2
  55. package/dist/triage/reconcile/reconcile.js +4 -3
  56. package/dist/triage/scope/mutations-core.js +3 -2
  57. package/dist/triage/subscribe/index.d.ts +1 -1
  58. package/dist/triage/subscribe/index.js +4 -3
  59. package/dist/triage/summary/index.d.ts +2 -2
  60. package/dist/triage/summary/index.js +4 -3
  61. package/dist/triage/summary/reconcilable.d.ts +1 -1
  62. package/dist/triage/summary/reconcilable.js +3 -2
  63. package/dist/triage/welcome/constants.d.ts +2 -2
  64. package/dist/triage/welcome/constants.js +2 -2
  65. package/dist/triage/welcome/prior-state.js +3 -2
  66. package/dist/triage/welcome/summary.js +5 -4
  67. package/dist/value/adoption-emit.d.ts +14 -0
  68. package/dist/value/adoption-emit.js +67 -0
  69. package/dist/value/adoption-registry.d.ts +62 -0
  70. package/dist/value/adoption-registry.js +214 -0
  71. package/dist/value/feedback-file.d.ts +67 -0
  72. package/dist/value/feedback-file.js +353 -0
  73. package/dist/value/friction-emit.d.ts +10 -0
  74. package/dist/value/friction-emit.js +24 -0
  75. package/dist/value/readback.d.ts +86 -0
  76. package/dist/value/readback.js +503 -0
  77. package/dist/vbrief-validate/decomposition.js +1 -1
  78. package/dist/wip-cap/evaluate.js +3 -0
  79. package/package.json +35 -3
@@ -0,0 +1,86 @@
1
+ import { type BehavioralEventRecord } from "../lifecycle/events.js";
2
+ import { type ValueFeedbackResolved } from "../policy/value-feedback.js";
3
+ /** Repeat-suppression window for the budgeted session readback (#1709 / #1279 parity). */
4
+ export declare const VALUE_READBACK_SUPPRESSION_HOURS = 4;
5
+ export declare const VALUE_READBACK_HISTORY_REL: string;
6
+ export declare const VALUE_READBACK_HISTORY_SCHEMA = "deft.value.readback.v1";
7
+ export type SignalClass = "value" | "bypass" | "adoption" | "friction";
8
+ export interface ReadAttributionOptions {
9
+ readonly projectRoot: string;
10
+ readonly logPath?: string | null;
11
+ readonly since?: Date | null;
12
+ }
13
+ export interface ValueShowTrend {
14
+ readonly windowLabel: string;
15
+ readonly windowMs: number;
16
+ readonly total: number;
17
+ readonly byClass: Readonly<Record<SignalClass, number>>;
18
+ readonly byEvent: Readonly<Record<string, number>>;
19
+ readonly recent: readonly BehavioralEventRecord[];
20
+ }
21
+ export interface ValueShowResult {
22
+ readonly exitCode: 0 | 1 | 2;
23
+ readonly gated: boolean;
24
+ readonly empty: boolean;
25
+ readonly text: string;
26
+ readonly trend: ValueShowTrend | null;
27
+ }
28
+ export interface SessionReadbackResult {
29
+ readonly line: string | null;
30
+ readonly suppressed: boolean;
31
+ readonly gated: boolean;
32
+ }
33
+ /** Read attribution ledger entries in emission order (#1709). */
34
+ export declare function readAttributionEvents(options: ReadAttributionOptions): BehavioralEventRecord[];
35
+ /** Format one attributed session line from a concrete ledger event (#1709 attributed-only). */
36
+ export declare function formatAttributedSessionLine(record: BehavioralEventRecord): string;
37
+ /** Pick the single highest-priority recent attribution for the session one-liner. */
38
+ export declare function selectSessionAttribution(events: readonly BehavioralEventRecord[]): BehavioralEventRecord | null;
39
+ /** True when the same attribution event id was read back within the suppression window. */
40
+ export declare function shouldSuppressSessionReadback(eventId: string, historyFile: string, options?: {
41
+ now?: Date;
42
+ }): boolean;
43
+ /** Budgeted session one-liner — silent when gated, empty, or debounced (#1709). */
44
+ export declare function renderSessionReadback(projectRoot: string, options?: {
45
+ policyOverride?: ValueFeedbackResolved;
46
+ logPath?: string | null;
47
+ now?: Date;
48
+ maxChars?: number;
49
+ writeHistory?: boolean;
50
+ }): SessionReadbackResult;
51
+ /** Emit the session readback line to stdout when present. Returns the line or null. */
52
+ export declare function emitSessionValueReadback(projectRoot: string, options?: {
53
+ output?: (line: string) => void;
54
+ policyOverride?: ValueFeedbackResolved;
55
+ logPath?: string | null;
56
+ now?: Date;
57
+ writeHistory?: boolean;
58
+ }): string | null;
59
+ export declare function parseWindowMs(raw: string | undefined, defaultDays?: number): number;
60
+ /** Build attributed-value trend counts for pull-based value:show (#1709). */
61
+ export declare function computeValueShowTrend(projectRoot: string, options?: {
62
+ windowMs?: number;
63
+ logPath?: string | null;
64
+ now?: Date;
65
+ }): ValueShowTrend;
66
+ export declare function formatValueShowReport(trend: ValueShowTrend): string;
67
+ /** Pull-based value:show CLI core (#1709). */
68
+ export declare function runValueShow(options: {
69
+ projectRoot?: string | null;
70
+ window?: string;
71
+ format?: "text" | "json";
72
+ logPath?: string | null;
73
+ policyOverride?: ValueFeedbackResolved;
74
+ }): ValueShowResult;
75
+ export interface ValueShowCliArgs {
76
+ window?: string;
77
+ format?: "text" | "json";
78
+ projectRoot?: string;
79
+ error?: string;
80
+ }
81
+ export declare function parseValueShowArgs(argv: readonly string[]): ValueShowCliArgs;
82
+ /** CLI entry for value:show (#1709). */
83
+ export declare function valueShowMain(argv?: readonly string[]): number;
84
+ /** CLI module entrypoint for dispatch (#1709). */
85
+ export declare function mainEntry(argv?: string[]): number;
86
+ //# sourceMappingURL=readback.d.ts.map
@@ -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
@@ -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;
@@ -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.70.0",
3
+ "version": "0.71.1",
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.70.0",
253
- "@deftai/directive-types": "^0.70.0",
284
+ "@deftai/directive-content": "^0.71.1",
285
+ "@deftai/directive-types": "^0.71.1",
254
286
  "archiver": "^8.0.0"
255
287
  },
256
288
  "scripts": {