@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.
- package/dist/branch/evaluate.js +7 -0
- package/dist/deposit/contain.d.ts +41 -0
- package/dist/deposit/contain.js +113 -0
- package/dist/deposit/copy-tree.js +8 -2
- package/dist/doctor/checks.d.ts +11 -0
- package/dist/doctor/checks.js +75 -2
- package/dist/doctor/doctor-state.js +2 -2
- package/dist/doctor/main.js +54 -1
- package/dist/doctor/manifest.d.ts +20 -0
- package/dist/doctor/manifest.js +22 -0
- package/dist/doctor/types.d.ts +8 -0
- package/dist/eval/crud-telemetry.d.ts +59 -0
- package/dist/eval/crud-telemetry.js +307 -0
- package/dist/eval/health.d.ts +52 -0
- package/dist/eval/health.js +240 -0
- package/dist/eval/readback.d.ts +38 -0
- package/dist/eval/readback.js +209 -0
- package/dist/eval/report.d.ts +53 -0
- package/dist/eval/report.js +161 -0
- package/dist/eval/run.d.ts +79 -0
- package/dist/eval/run.js +309 -0
- package/dist/events/attribution-constants.d.ts +13 -0
- package/dist/events/attribution-constants.js +18 -0
- package/dist/events/attribution-ledger.d.ts +43 -0
- package/dist/events/attribution-ledger.js +61 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/init-deposit/gitignore.js +5 -5
- package/dist/init-deposit/init-deposit.js +6 -0
- package/dist/init-deposit/refresh.js +5 -0
- package/dist/intake/issue-ingest.d.ts +11 -0
- package/dist/intake/issue-ingest.js +86 -11
- package/dist/layout/resolve.d.ts +2 -2
- package/dist/layout/resolve.js +2 -2
- package/dist/lifecycle/events.js +2 -0
- package/dist/policy/index.d.ts +2 -1
- package/dist/policy/index.js +15 -2
- package/dist/policy/plan-extensions.d.ts +31 -0
- package/dist/policy/plan-extensions.js +45 -0
- package/dist/policy/value-feedback.d.ts +56 -0
- package/dist/policy/value-feedback.js +284 -0
- package/dist/policy/wip.d.ts +2 -2
- package/dist/policy/wip.js +2 -2
- package/dist/preflight-cache/evaluate.js +4 -3
- package/dist/scope/audit-log.js +3 -3
- package/dist/scope/transition.js +16 -5
- package/dist/session/session-start.js +20 -0
- package/dist/slice/record.js +3 -3
- package/dist/swarm/routing.js +10 -3
- package/dist/triage/actions/candidates-log.d.ts +4 -4
- package/dist/triage/actions/candidates-log.js +4 -7
- package/dist/triage/bootstrap/gitignore.d.ts +7 -3
- package/dist/triage/bootstrap/gitignore.js +67 -37
- package/dist/triage/bootstrap/index.d.ts +1 -1
- package/dist/triage/bootstrap/index.js +4 -3
- package/dist/triage/bulk/index.d.ts +11 -2
- package/dist/triage/bulk/index.js +43 -6
- package/dist/triage/cache-path.d.ts +39 -0
- package/dist/triage/cache-path.js +98 -0
- package/dist/triage/help/registry-data.d.ts +53 -20
- package/dist/triage/help/registry-data.js +104 -20
- package/dist/triage/index.d.ts +2 -0
- package/dist/triage/index.js +2 -0
- package/dist/triage/queue/audit.js +2 -2
- package/dist/triage/queue/cache.js +2 -2
- package/dist/triage/reconcile/reconcile.js +4 -3
- package/dist/triage/scope/cli.d.ts +2 -1
- package/dist/triage/scope/cli.js +30 -6
- package/dist/triage/scope/mutations-core.js +3 -2
- package/dist/triage/scope/mutations.d.ts +10 -0
- package/dist/triage/scope/mutations.js +22 -0
- package/dist/triage/subscribe/index.d.ts +1 -1
- package/dist/triage/subscribe/index.js +4 -3
- package/dist/triage/summary/index.d.ts +2 -2
- package/dist/triage/summary/index.js +4 -3
- package/dist/triage/summary/reconcilable.d.ts +1 -1
- package/dist/triage/summary/reconcilable.js +3 -2
- package/dist/triage/welcome/constants.d.ts +3 -4
- package/dist/triage/welcome/constants.js +3 -4
- package/dist/triage/welcome/index.d.ts +1 -0
- package/dist/triage/welcome/index.js +1 -0
- package/dist/triage/welcome/onboard.d.ts +35 -0
- package/dist/triage/welcome/onboard.js +94 -0
- package/dist/triage/welcome/prior-state.js +3 -2
- package/dist/triage/welcome/summary.js +5 -4
- package/dist/umbrella-current-shape/index.d.ts +25 -1
- package/dist/umbrella-current-shape/index.js +84 -4
- package/dist/value/adoption-emit.d.ts +14 -0
- package/dist/value/adoption-emit.js +67 -0
- package/dist/value/adoption-registry.d.ts +62 -0
- package/dist/value/adoption-registry.js +214 -0
- package/dist/value/feedback-file.d.ts +67 -0
- package/dist/value/feedback-file.js +353 -0
- package/dist/value/friction-emit.d.ts +10 -0
- package/dist/value/friction-emit.js +24 -0
- package/dist/value/readback.d.ts +86 -0
- package/dist/value/readback.js +503 -0
- package/dist/vbrief-build/project-definition-io.d.ts +6 -0
- package/dist/vbrief-build/project-definition-io.js +7 -2
- package/dist/vbrief-validate/decomposition.js +1 -1
- package/dist/vbrief-validate/precutover.js +7 -3
- package/dist/wip-cap/evaluate.js +3 -0
- package/package.json +35 -3
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { isFrameworkRepoRoot } from "../check/orchestrator.js";
|
|
4
|
+
import { isValueFeedbackPathAllowed, resolveValueFeedback } from "../policy/value-feedback.js";
|
|
5
|
+
import { GhRestError, restCreateIssue, restIssueListPaginated, } from "../scm/gh-rest.js";
|
|
6
|
+
import { resolveProjectRoot } from "../scope/project-context.js";
|
|
7
|
+
export const DEFAULT_UPSTREAM_REPO = "deftai/directive";
|
|
8
|
+
export const FRAMEWORK_GAP_TITLE_PREFIX = "[framework-gap]";
|
|
9
|
+
function sanitizeOneLine(value) {
|
|
10
|
+
return value.replace(/\r?\n/g, " ").trim();
|
|
11
|
+
}
|
|
12
|
+
function sectionOrPlaceholder(value, placeholder) {
|
|
13
|
+
const trimmed = value?.trim() ?? "";
|
|
14
|
+
return trimmed.length > 0 ? trimmed : placeholder;
|
|
15
|
+
}
|
|
16
|
+
/** True when the resolved project root is the directive maintainer source checkout. */
|
|
17
|
+
export function isMaintainerFrameworkRepo(projectRoot) {
|
|
18
|
+
return isFrameworkRepoRoot(resolve(projectRoot));
|
|
19
|
+
}
|
|
20
|
+
/** Normalize a title for duplicate comparison (#1709). */
|
|
21
|
+
export function normalizeForDedup(title) {
|
|
22
|
+
return title
|
|
23
|
+
.trim()
|
|
24
|
+
.replace(/^\[framework-gap\]\s*/i, "")
|
|
25
|
+
.toLowerCase()
|
|
26
|
+
.replace(/[^\p{L}\p{N}]+/gu, " ")
|
|
27
|
+
.trim();
|
|
28
|
+
}
|
|
29
|
+
/** Build the upstream issue title for a framework-gap report. */
|
|
30
|
+
export function buildFrameworkGapTitle(summary) {
|
|
31
|
+
const clean = sanitizeOneLine(summary);
|
|
32
|
+
if (clean.toLowerCase().startsWith(FRAMEWORK_GAP_TITLE_PREFIX.toLowerCase())) {
|
|
33
|
+
return clean;
|
|
34
|
+
}
|
|
35
|
+
return `${FRAMEWORK_GAP_TITLE_PREFIX} ${clean}`;
|
|
36
|
+
}
|
|
37
|
+
/** Build the structured framework-gap issue body. */
|
|
38
|
+
export function buildFrameworkGapBody(input) {
|
|
39
|
+
const summary = sanitizeOneLine(input.summary);
|
|
40
|
+
const lines = [
|
|
41
|
+
"## Summary",
|
|
42
|
+
"",
|
|
43
|
+
summary,
|
|
44
|
+
"",
|
|
45
|
+
"## Context",
|
|
46
|
+
"",
|
|
47
|
+
sectionOrPlaceholder(input.context, "_(not provided)_"),
|
|
48
|
+
"",
|
|
49
|
+
"## Expected behavior",
|
|
50
|
+
"",
|
|
51
|
+
sectionOrPlaceholder(input.expected, "_(not provided)_"),
|
|
52
|
+
"",
|
|
53
|
+
"## Actual behavior / gap",
|
|
54
|
+
"",
|
|
55
|
+
sectionOrPlaceholder(input.actual, summary),
|
|
56
|
+
"",
|
|
57
|
+
"## Session notes",
|
|
58
|
+
"",
|
|
59
|
+
sectionOrPlaceholder(input.sessionNotes, "_(not provided)_"),
|
|
60
|
+
"",
|
|
61
|
+
"---",
|
|
62
|
+
"_Filed via `task feedback:file` from a directive consumer project (Refs #1709 value-feedback gap escalation)._",
|
|
63
|
+
"",
|
|
64
|
+
];
|
|
65
|
+
return lines.join("\n");
|
|
66
|
+
}
|
|
67
|
+
/** Search open upstream issues for a normalized title match. */
|
|
68
|
+
export function findDuplicateIssue(repo, title, seams = {}) {
|
|
69
|
+
const needle = normalizeForDedup(title);
|
|
70
|
+
if (needle.length === 0) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const issues = restIssueListPaginated(repo, { state: "open", limit: 100, excludePulls: true }, seams);
|
|
74
|
+
for (const issue of issues) {
|
|
75
|
+
const issueTitle = typeof issue.title === "string" ? issue.title : "";
|
|
76
|
+
if (normalizeForDedup(issueTitle) === needle) {
|
|
77
|
+
const url = typeof issue.html_url === "string" ? issue.html_url : "";
|
|
78
|
+
if (url.length > 0) {
|
|
79
|
+
return { url, title: issueTitle };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
function resolveRepo(options) {
|
|
86
|
+
const explicit = options.repo?.trim();
|
|
87
|
+
return explicit && explicit.length > 0 ? explicit : DEFAULT_UPSTREAM_REPO;
|
|
88
|
+
}
|
|
89
|
+
function isOffline() {
|
|
90
|
+
return process.env.DEFT_NO_NETWORK === "1";
|
|
91
|
+
}
|
|
92
|
+
function networkErrorMessage(prefix, err) {
|
|
93
|
+
const detail = err instanceof GhRestError ? err.message : String(err);
|
|
94
|
+
return `[deft feedback] ${prefix}: ${detail}\n`;
|
|
95
|
+
}
|
|
96
|
+
function buildConfirmationPrompt(title, body) {
|
|
97
|
+
return ("Draft framework-gap issue (not filed yet):\n\n" +
|
|
98
|
+
`Title: ${title}\n\n` +
|
|
99
|
+
`${body}\n` +
|
|
100
|
+
"Re-run with --confirm to file this issue upstream after reviewing the draft.\n");
|
|
101
|
+
}
|
|
102
|
+
/** Core feedback:file workflow (#1709 gap escalation). */
|
|
103
|
+
export function runFeedbackFile(options) {
|
|
104
|
+
const summary = sanitizeOneLine(options.summary);
|
|
105
|
+
if (summary.length === 0) {
|
|
106
|
+
return {
|
|
107
|
+
outcome: "error-bad-args",
|
|
108
|
+
exitCode: 2,
|
|
109
|
+
title: "",
|
|
110
|
+
body: "",
|
|
111
|
+
repo: resolveRepo(options),
|
|
112
|
+
message: "Error: summary is required (pass --summary or a positional argument).\n",
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const projectRootRaw = resolveProjectRoot(options.projectRoot ?? undefined);
|
|
116
|
+
if (projectRootRaw === null) {
|
|
117
|
+
return {
|
|
118
|
+
outcome: "error-config",
|
|
119
|
+
exitCode: 2,
|
|
120
|
+
title: buildFrameworkGapTitle(summary),
|
|
121
|
+
body: buildFrameworkGapBody(options),
|
|
122
|
+
repo: resolveRepo(options),
|
|
123
|
+
message: "Error: could not resolve project root. Pass --project-root or run from a directive consumer repo.\n",
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const projectRoot = resolve(projectRootRaw);
|
|
127
|
+
const repo = resolveRepo(options);
|
|
128
|
+
const title = buildFrameworkGapTitle(summary);
|
|
129
|
+
const body = buildFrameworkGapBody(options);
|
|
130
|
+
if (isMaintainerFrameworkRepo(projectRoot)) {
|
|
131
|
+
return {
|
|
132
|
+
outcome: "skipped-maintainer",
|
|
133
|
+
exitCode: 0,
|
|
134
|
+
title,
|
|
135
|
+
body,
|
|
136
|
+
repo,
|
|
137
|
+
message: "[deft feedback] Skipped: maintainer framework repo detected; gap escalation targets consumer projects only.\n",
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const policy = resolveValueFeedback(projectRoot);
|
|
141
|
+
if (!isValueFeedbackPathAllowed("upstreamPrompt", policy)) {
|
|
142
|
+
return {
|
|
143
|
+
outcome: "blocked-policy",
|
|
144
|
+
exitCode: 1,
|
|
145
|
+
title,
|
|
146
|
+
body,
|
|
147
|
+
repo,
|
|
148
|
+
message: "[deft feedback] Blocked: plan.policy.valueFeedback upstreamPrompt is OFF. " +
|
|
149
|
+
"Enable with `task policy:enable-value-feedback -- --confirm` and set upstreamPrompt, " +
|
|
150
|
+
"or inspect via `task policy:show --field=valueFeedback`.\n",
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const offline = isOffline();
|
|
154
|
+
const dryRun = options.dryRun ?? false;
|
|
155
|
+
const seams = options.seams ?? {};
|
|
156
|
+
let dedupSkippedNote = "";
|
|
157
|
+
if (!offline) {
|
|
158
|
+
try {
|
|
159
|
+
const duplicate = findDuplicateIssue(repo, title, seams);
|
|
160
|
+
if (duplicate !== null) {
|
|
161
|
+
return {
|
|
162
|
+
outcome: "blocked-duplicate",
|
|
163
|
+
exitCode: 1,
|
|
164
|
+
title,
|
|
165
|
+
body,
|
|
166
|
+
repo,
|
|
167
|
+
duplicateUrl: duplicate.url,
|
|
168
|
+
message: `[deft feedback] Duplicate detected: open issue ${duplicate.url} ` +
|
|
169
|
+
`("${duplicate.title}") matches this report.\n`,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
return {
|
|
175
|
+
outcome: "error-network",
|
|
176
|
+
exitCode: 2,
|
|
177
|
+
title,
|
|
178
|
+
body,
|
|
179
|
+
repo,
|
|
180
|
+
message: networkErrorMessage("Duplicate search failed", err),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
dedupSkippedNote = "[deft feedback] Note: duplicate detection skipped (DEFT_NO_NETWORK=1).\n\n";
|
|
186
|
+
}
|
|
187
|
+
if (!options.confirm) {
|
|
188
|
+
return {
|
|
189
|
+
outcome: "draft",
|
|
190
|
+
exitCode: 1,
|
|
191
|
+
title,
|
|
192
|
+
body,
|
|
193
|
+
repo,
|
|
194
|
+
message: `${dedupSkippedNote}${buildConfirmationPrompt(title, body)}`,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
if (dryRun || offline) {
|
|
198
|
+
return {
|
|
199
|
+
outcome: "draft",
|
|
200
|
+
exitCode: 0,
|
|
201
|
+
title,
|
|
202
|
+
body,
|
|
203
|
+
repo,
|
|
204
|
+
message: `${dedupSkippedNote}[deft feedback] Dry run: would file upstream issue in ${repo}.\n` +
|
|
205
|
+
`${buildConfirmationPrompt(title, body)}`,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
const created = restCreateIssue(repo, title, body, [], seams);
|
|
210
|
+
const issueUrl = typeof created.html_url === "string" && created.html_url.length > 0 ? created.html_url : null;
|
|
211
|
+
const number = created.number;
|
|
212
|
+
const urlLine = issueUrl ??
|
|
213
|
+
(typeof number === "number" ? `https://github.com/${repo}/issues/${number}` : null);
|
|
214
|
+
return {
|
|
215
|
+
outcome: "filed",
|
|
216
|
+
exitCode: 0,
|
|
217
|
+
title,
|
|
218
|
+
body,
|
|
219
|
+
repo,
|
|
220
|
+
issueUrl: urlLine,
|
|
221
|
+
message: urlLine !== null
|
|
222
|
+
? `[deft feedback] Filed upstream issue: ${urlLine}\n`
|
|
223
|
+
: "[deft feedback] Issue filed upstream (URL unavailable in API response).\n",
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
227
|
+
return {
|
|
228
|
+
outcome: "error-network",
|
|
229
|
+
exitCode: 2,
|
|
230
|
+
title,
|
|
231
|
+
body,
|
|
232
|
+
repo,
|
|
233
|
+
message: networkErrorMessage("Upstream filing failed", err),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/** Parse argv for feedback:file. */
|
|
238
|
+
export function parseFeedbackFileArgs(argv) {
|
|
239
|
+
const out = {};
|
|
240
|
+
const positionals = [];
|
|
241
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
242
|
+
const arg = argv[i];
|
|
243
|
+
if (arg === "--confirm")
|
|
244
|
+
out.confirm = true;
|
|
245
|
+
else if (arg === "--dry-run")
|
|
246
|
+
out.dryRun = true;
|
|
247
|
+
else if (arg === "--json")
|
|
248
|
+
out.json = true;
|
|
249
|
+
else if (arg === "--summary") {
|
|
250
|
+
out.summary = argv[++i];
|
|
251
|
+
}
|
|
252
|
+
else if (arg?.startsWith("--summary=")) {
|
|
253
|
+
out.summary = arg.slice("--summary=".length);
|
|
254
|
+
}
|
|
255
|
+
else if (arg === "--context") {
|
|
256
|
+
out.context = argv[++i];
|
|
257
|
+
}
|
|
258
|
+
else if (arg?.startsWith("--context=")) {
|
|
259
|
+
out.context = arg.slice("--context=".length);
|
|
260
|
+
}
|
|
261
|
+
else if (arg === "--expected") {
|
|
262
|
+
out.expected = argv[++i];
|
|
263
|
+
}
|
|
264
|
+
else if (arg?.startsWith("--expected=")) {
|
|
265
|
+
out.expected = arg.slice("--expected=".length);
|
|
266
|
+
}
|
|
267
|
+
else if (arg === "--actual") {
|
|
268
|
+
out.actual = argv[++i];
|
|
269
|
+
}
|
|
270
|
+
else if (arg?.startsWith("--actual=")) {
|
|
271
|
+
out.actual = arg.slice("--actual=".length);
|
|
272
|
+
}
|
|
273
|
+
else if (arg === "--notes" || arg === "--session-notes") {
|
|
274
|
+
out.sessionNotes = argv[++i];
|
|
275
|
+
}
|
|
276
|
+
else if (arg?.startsWith("--notes=")) {
|
|
277
|
+
out.sessionNotes = arg.slice("--notes=".length);
|
|
278
|
+
}
|
|
279
|
+
else if (arg?.startsWith("--session-notes=")) {
|
|
280
|
+
out.sessionNotes = arg.slice("--session-notes=".length);
|
|
281
|
+
}
|
|
282
|
+
else if (arg === "--repo") {
|
|
283
|
+
out.repo = argv[++i];
|
|
284
|
+
}
|
|
285
|
+
else if (arg?.startsWith("--repo=")) {
|
|
286
|
+
out.repo = arg.slice("--repo=".length);
|
|
287
|
+
}
|
|
288
|
+
else if (arg === "--project-root") {
|
|
289
|
+
out.projectRoot = argv[++i];
|
|
290
|
+
}
|
|
291
|
+
else if (arg?.startsWith("--project-root=")) {
|
|
292
|
+
out.projectRoot = arg.slice("--project-root=".length);
|
|
293
|
+
}
|
|
294
|
+
else if (arg.startsWith("-")) {
|
|
295
|
+
return { ...out, error: `unrecognized argument: ${arg}` };
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
positionals.push(arg);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if ((out.summary === undefined || out.summary.trim().length === 0) && positionals.length > 0) {
|
|
302
|
+
out.summary = positionals.join(" ");
|
|
303
|
+
}
|
|
304
|
+
return out;
|
|
305
|
+
}
|
|
306
|
+
/** CLI entry for feedback:file. */
|
|
307
|
+
export function feedbackFileMain(argv) {
|
|
308
|
+
const args = parseFeedbackFileArgs(argv);
|
|
309
|
+
if (args.error !== undefined) {
|
|
310
|
+
process.stderr.write(`feedback:file: ${args.error}\n`);
|
|
311
|
+
return 2;
|
|
312
|
+
}
|
|
313
|
+
const result = runFeedbackFile({
|
|
314
|
+
summary: args.summary ?? "",
|
|
315
|
+
context: args.context,
|
|
316
|
+
expected: args.expected,
|
|
317
|
+
actual: args.actual,
|
|
318
|
+
sessionNotes: args.sessionNotes,
|
|
319
|
+
confirm: args.confirm,
|
|
320
|
+
dryRun: args.dryRun,
|
|
321
|
+
json: args.json,
|
|
322
|
+
repo: args.repo,
|
|
323
|
+
projectRoot: args.projectRoot,
|
|
324
|
+
});
|
|
325
|
+
if (args.json) {
|
|
326
|
+
process.stdout.write(`${JSON.stringify({
|
|
327
|
+
outcome: result.outcome,
|
|
328
|
+
exit_code: result.exitCode,
|
|
329
|
+
title: result.title,
|
|
330
|
+
body: result.body,
|
|
331
|
+
repo: result.repo,
|
|
332
|
+
issue_url: result.issueUrl ?? null,
|
|
333
|
+
duplicate_url: result.duplicateUrl ?? null,
|
|
334
|
+
message: result.message.trim(),
|
|
335
|
+
}, null, 2)}\n`);
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
process.stdout.write(result.message);
|
|
339
|
+
}
|
|
340
|
+
return result.exitCode;
|
|
341
|
+
}
|
|
342
|
+
/** CLI module entrypoint for dispatch (#1709). */
|
|
343
|
+
export function mainEntry(argv = process.argv.slice(2)) {
|
|
344
|
+
return feedbackFileMain(argv);
|
|
345
|
+
}
|
|
346
|
+
/** Convenience probe used in tests for project-root sentinel presence. */
|
|
347
|
+
export function projectRootLooksLikeConsumer(projectRoot) {
|
|
348
|
+
const root = resolve(projectRoot);
|
|
349
|
+
return (existsSync(resolve(root, "xbrief")) ||
|
|
350
|
+
existsSync(resolve(root, "vbrief")) ||
|
|
351
|
+
existsSync(resolve(root, ".deft")));
|
|
352
|
+
}
|
|
353
|
+
//# sourceMappingURL=feedback-file.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ValueFeedbackResolved } from "../policy/value-feedback.js";
|
|
2
|
+
export interface FrictionEmitOptions {
|
|
3
|
+
readonly logPath?: string | null;
|
|
4
|
+
readonly policyOverride?: ValueFeedbackResolved;
|
|
5
|
+
}
|
|
6
|
+
/** Record friction signals for each contradictory gate detected by eval:health (#2339). */
|
|
7
|
+
export declare function recordFrictionFromContradictoryGates(projectRoot: string, options?: FrictionEmitOptions): number;
|
|
8
|
+
/** Session/work-boundary friction probe alias (#2339). */
|
|
9
|
+
export declare function probeFrictionAtWorkBoundary(projectRoot: string, options?: FrictionEmitOptions): number;
|
|
10
|
+
//# sourceMappingURL=friction-emit.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { detectContradictoryGates } from "../eval/health.js";
|
|
2
|
+
import { recordFrictionSignal } from "../events/attribution-ledger.js";
|
|
3
|
+
import { isValueFeedbackPathAllowed, resolveValueFeedback, } from "../policy/value-feedback.js";
|
|
4
|
+
/** Record friction signals for each contradictory gate detected by eval:health (#2339). */
|
|
5
|
+
export function recordFrictionFromContradictoryGates(projectRoot, options = {}) {
|
|
6
|
+
const policy = options.policyOverride ?? resolveValueFeedback(projectRoot);
|
|
7
|
+
if (!isValueFeedbackPathAllowed("emitEvents", policy)) {
|
|
8
|
+
return 0;
|
|
9
|
+
}
|
|
10
|
+
const contradictions = detectContradictoryGates(projectRoot);
|
|
11
|
+
let recorded = 0;
|
|
12
|
+
for (const contradiction of contradictions) {
|
|
13
|
+
const record = recordFrictionSignal(projectRoot, "eval:health", `${contradiction.id}: ${contradiction.summary}`, { logPath: options.logPath, policyOverride: policy });
|
|
14
|
+
if (record !== null) {
|
|
15
|
+
recorded += 1;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return recorded;
|
|
19
|
+
}
|
|
20
|
+
/** Session/work-boundary friction probe alias (#2339). */
|
|
21
|
+
export function probeFrictionAtWorkBoundary(projectRoot, options = {}) {
|
|
22
|
+
return recordFrictionFromContradictoryGates(projectRoot, options);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=friction-emit.js.map
|
|
@@ -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
|