@deftai/directive-core 0.100.0 → 0.102.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 (120) hide show
  1. package/dist/authz/classify.js +193 -27
  2. package/dist/authz/decompose-apply.d.ts +14 -0
  3. package/dist/authz/decompose-apply.js +93 -18
  4. package/dist/check/cached-orchestrator.d.ts +30 -5
  5. package/dist/check/cached-orchestrator.js +297 -10
  6. package/dist/check/cli-native-gates.d.ts +43 -0
  7. package/dist/check/cli-native-gates.js +84 -0
  8. package/dist/check/gate-lists.d.ts +10 -4
  9. package/dist/check/gate-lists.js +19 -5
  10. package/dist/check/index.d.ts +4 -2
  11. package/dist/check/index.js +3 -1
  12. package/dist/check/named-cause.d.ts +45 -0
  13. package/dist/check/named-cause.js +131 -0
  14. package/dist/check/orchestrator.js +6 -1
  15. package/dist/doctor/checks.d.ts +5 -6
  16. package/dist/doctor/checks.js +13 -56
  17. package/dist/doctor/help.d.ts +5 -0
  18. package/dist/doctor/help.js +31 -0
  19. package/dist/doctor/index.d.ts +2 -0
  20. package/dist/doctor/index.js +2 -0
  21. package/dist/doctor/main.js +58 -0
  22. package/dist/doctor/session-coda.d.ts +82 -0
  23. package/dist/doctor/session-coda.js +151 -0
  24. package/dist/doctor/types.d.ts +16 -0
  25. package/dist/eval/later-graduation.d.ts +39 -0
  26. package/dist/eval/later-graduation.js +91 -0
  27. package/dist/eval/report.d.ts +6 -0
  28. package/dist/eval/report.js +27 -8
  29. package/dist/index.d.ts +3 -0
  30. package/dist/index.js +3 -0
  31. package/dist/init-deposit/gitignore.js +2 -0
  32. package/dist/intake/issue-ingest.js +49 -0
  33. package/dist/policy/ac-pass-banking.d.ts +58 -0
  34. package/dist/policy/ac-pass-banking.js +150 -0
  35. package/dist/policy/ceremony-dial-escalation.d.ts +52 -0
  36. package/dist/policy/ceremony-dial-escalation.js +92 -0
  37. package/dist/policy/ceremony-dial.d.ts +32 -1
  38. package/dist/policy/ceremony-dial.js +128 -1
  39. package/dist/policy/check-resume.d.ts +9 -38
  40. package/dist/policy/check-resume.js +18 -156
  41. package/dist/policy/coverage-debt.d.ts +18 -37
  42. package/dist/policy/coverage-debt.js +38 -147
  43. package/dist/policy/index.d.ts +2 -1
  44. package/dist/policy/index.js +16 -2
  45. package/dist/preflight-cache/evaluate.d.ts +3 -0
  46. package/dist/preflight-cache/evaluate.js +3 -0
  47. package/dist/product-first-done-gate/acceptance.d.ts +25 -0
  48. package/dist/product-first-done-gate/acceptance.js +269 -0
  49. package/dist/product-first-done-gate/check-mode.d.ts +45 -0
  50. package/dist/product-first-done-gate/check-mode.js +148 -0
  51. package/dist/product-first-done-gate/empty-resolution.d.ts +26 -0
  52. package/dist/product-first-done-gate/empty-resolution.js +38 -0
  53. package/dist/product-first-done-gate/evaluate.d.ts +76 -0
  54. package/dist/product-first-done-gate/evaluate.js +411 -0
  55. package/dist/product-first-done-gate/index.d.ts +12 -0
  56. package/dist/product-first-done-gate/index.js +12 -0
  57. package/dist/product-first-done-gate/types.d.ts +67 -0
  58. package/dist/product-first-done-gate/types.js +54 -0
  59. package/dist/run-summary/emit.d.ts +66 -0
  60. package/dist/run-summary/emit.js +217 -0
  61. package/dist/run-summary/index.d.ts +5 -0
  62. package/dist/run-summary/index.js +5 -0
  63. package/dist/run-summary/path.d.ts +25 -0
  64. package/dist/run-summary/path.js +78 -0
  65. package/dist/run-summary/share.d.ts +25 -0
  66. package/dist/run-summary/share.js +106 -0
  67. package/dist/run-summary/types.d.ts +134 -0
  68. package/dist/run-summary/types.js +28 -0
  69. package/dist/scope/acceptance-activate-gate.d.ts +23 -0
  70. package/dist/scope/acceptance-activate-gate.js +70 -0
  71. package/dist/scope/acceptance-evidence.d.ts +34 -0
  72. package/dist/scope/acceptance-evidence.js +84 -10
  73. package/dist/scope/decompose.d.ts +5 -0
  74. package/dist/scope/decompose.js +11 -2
  75. package/dist/scope/index.d.ts +1 -0
  76. package/dist/scope/index.js +1 -0
  77. package/dist/scope/transition.js +5 -0
  78. package/dist/session/ac-pass-banking.d.ts +221 -0
  79. package/dist/session/ac-pass-banking.js +761 -0
  80. package/dist/session/deposit-sha.d.ts +49 -0
  81. package/dist/session/deposit-sha.js +121 -0
  82. package/dist/session/effort-budget.d.ts +6 -0
  83. package/dist/session/effort-budget.js +21 -3
  84. package/dist/session/index.d.ts +5 -1
  85. package/dist/session/index.js +5 -1
  86. package/dist/session/orientation-compression.d.ts +127 -0
  87. package/dist/session/orientation-compression.js +435 -0
  88. package/dist/session/orientation-state.d.ts +29 -0
  89. package/dist/session/orientation-state.js +85 -0
  90. package/dist/session/session-start.d.ts +31 -0
  91. package/dist/session/session-start.js +219 -22
  92. package/dist/session/toolchain-preflight.d.ts +79 -0
  93. package/dist/session/toolchain-preflight.js +211 -0
  94. package/dist/triage/bootstrap/gitignore.d.ts +1 -1
  95. package/dist/triage/bootstrap/gitignore.js +8 -2
  96. package/dist/triage/bootstrap/index.js +15 -3
  97. package/dist/triage/classify/index.d.ts +1 -0
  98. package/dist/triage/classify/index.js +2 -0
  99. package/dist/triage/classify/label-mirror.js +16 -0
  100. package/dist/triage/classify/mirror-discovery-tip.d.ts +95 -0
  101. package/dist/triage/classify/mirror-discovery-tip.js +234 -0
  102. package/dist/triage/reconcile/reconcile.js +28 -6
  103. package/dist/triage/summary/index.js +12 -4
  104. package/dist/triage/welcome/default-mode.js +16 -0
  105. package/dist/ts-check-lane/run-lane.js +21 -4
  106. package/dist/vbrief-validate/conformance.js +7 -0
  107. package/dist/verify-ac/clauses.d.ts +51 -0
  108. package/dist/verify-ac/clauses.js +490 -0
  109. package/dist/verify-ac/evaluate.d.ts +55 -0
  110. package/dist/verify-ac/evaluate.js +145 -0
  111. package/dist/verify-ac/flag.d.ts +35 -0
  112. package/dist/verify-ac/flag.js +104 -0
  113. package/dist/verify-ac/index.d.ts +10 -0
  114. package/dist/verify-ac/index.js +10 -0
  115. package/dist/vitest-runner/coverage-debt-teardown.js +16 -4
  116. package/package.json +15 -3
  117. package/dist/policy/coverage-check-resume-presets.d.ts +0 -46
  118. package/dist/policy/coverage-check-resume-presets.js +0 -228
  119. package/dist/session/coverage-check-resume-nudge.d.ts +0 -34
  120. package/dist/session/coverage-check-resume-nudge.js +0 -66
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Rung-2 derived AC: decompose the task statement into testable clauses
3
+ * at intake; walk every clause against the shipped artifact at done (#3323).
4
+ */
5
+ export type ClauseOutcome = "verified" | "unverifiable" | "failed";
6
+ export interface AcceptanceClauseReading {
7
+ readonly text: string;
8
+ readonly artifact_path: string | null;
9
+ }
10
+ export interface AcceptanceClause {
11
+ readonly id: number;
12
+ readonly text: string;
13
+ readonly artifact_path: string | null;
14
+ readonly ambiguous: boolean;
15
+ readonly readings?: readonly AcceptanceClauseReading[];
16
+ readonly chosen_reading?: number;
17
+ }
18
+ export interface ClauseWalkResult {
19
+ readonly id: number;
20
+ readonly text: string;
21
+ readonly artifact_path: string | null;
22
+ readonly outcome: ClauseOutcome;
23
+ readonly detail: string;
24
+ }
25
+ export interface ClauseWalkReport {
26
+ readonly clauses: readonly ClauseWalkResult[];
27
+ readonly failed: readonly ClauseWalkResult[];
28
+ readonly unverifiable: readonly ClauseWalkResult[];
29
+ readonly verified: readonly ClauseWalkResult[];
30
+ readonly ok: boolean;
31
+ readonly message: string;
32
+ }
33
+ /** Numbered independently testable clauses from the task statement (#3323). */
34
+ export declare function deriveAcceptanceClauses(taskStatement: string): AcceptanceClause[];
35
+ export declare function readAcceptanceClauses(acceptance: unknown): AcceptanceClause[];
36
+ export declare function serializeAcceptanceClauses(clauses: readonly AcceptanceClause[]): Record<string, unknown>[];
37
+ /**
38
+ * When no commands are stated, stamp derived clauses onto plan.acceptance
39
+ * and promote source_rung to derived. Leaves stated-command acceptance alone.
40
+ */
41
+ export declare function stampDerivedClausesOnAcceptance(plan: Record<string, unknown>, taskStatement: string): {
42
+ readonly plan: Record<string, unknown>;
43
+ readonly clauses: readonly AcceptanceClause[];
44
+ };
45
+ export declare function isScratchArtifactPath(artifactPath: string): boolean;
46
+ export declare function walkAcceptanceClauses(clauses: readonly AcceptanceClause[], projectRoot: string): ClauseWalkReport;
47
+ /** Done reports lead with failed/unverifiable; unverifiable is never dropped. */
48
+ export declare function formatClauseWalkMessage(report: Omit<ClauseWalkReport, "message"> & {
49
+ readonly message?: string;
50
+ }, prior?: string): string;
51
+ //# sourceMappingURL=clauses.d.ts.map
@@ -0,0 +1,490 @@
1
+ /**
2
+ * Rung-2 derived AC: decompose the task statement into testable clauses
3
+ * at intake; walk every clause against the shipped artifact at done (#3323).
4
+ */
5
+ import { existsSync, readFileSync, statSync } from "node:fs";
6
+ import { isAbsolute, relative, resolve } from "node:path";
7
+ import { findAcHeading, parseListItems, sliceAcSection } from "../intake/markdown-scanners.js";
8
+ const SECTION_HEADING = /^(#{1,6})\s+(acceptance(?:\s+criteria|\s+sketch)?|fix)\s*$/i;
9
+ const LABELED_AC_PREFIXES = ["test:", "acceptance:", "acceptancecriteria:"];
10
+ const META_CLAUSE = /^(relates?\s+#|refs?\s+#)/i;
11
+ const FILE_EXT = /\.(?:ts|tsx|js|mjs|cjs|json|md|go|py|yml|yaml|txt)$/i;
12
+ const SCRATCH_SEGMENTS = new Set([
13
+ "tmp",
14
+ "temp",
15
+ ".deft-scratch",
16
+ "node_modules",
17
+ "scratch",
18
+ "buffer",
19
+ ]);
20
+ const EXISTENCE_CLAIM = /\b(?:exists?|stored on|written to|emitted? (?:at|to)|at its stated path|artifact path)\b/i;
21
+ const NEGATED_EXISTENCE = /\b(?:does not exist|doesn't exist|must not exist|never exists?|not exist)\b/i;
22
+ function asRecord(value) {
23
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
24
+ return value;
25
+ }
26
+ return null;
27
+ }
28
+ function isNonEmptyString(value) {
29
+ return typeof value === "string" && value.trim().length > 0;
30
+ }
31
+ function normalizeClauseText(text) {
32
+ return text.replace(/\s+/g, " ").trim();
33
+ }
34
+ function looksLikeFilePath(token) {
35
+ const t = token.trim().replace(/^['"`]|['"`]$/g, "");
36
+ if (t.length < 3 || t.includes("://") || t.includes(" ")) {
37
+ return false;
38
+ }
39
+ if (t.startsWith("plan.") || (t.includes(":") && !t.includes("/") && !t.includes("\\"))) {
40
+ return false;
41
+ }
42
+ const unified = t.replace(/\\/g, "/");
43
+ if (FILE_EXT.test(unified)) {
44
+ return true;
45
+ }
46
+ return unified.includes("/") && !unified.startsWith("#");
47
+ }
48
+ function extractPathTokens(text) {
49
+ const found = [];
50
+ const seen = new Set();
51
+ const push = (raw) => {
52
+ const token = raw.trim().replace(/^['"`]|['"`]$/g, "");
53
+ if (!looksLikeFilePath(token) || seen.has(token)) {
54
+ return;
55
+ }
56
+ seen.add(token);
57
+ found.push(token);
58
+ };
59
+ const backtick = /`([^`\n]{2,200})`/g;
60
+ let match = backtick.exec(text);
61
+ while (match !== null) {
62
+ push(match[1] ?? "");
63
+ match = backtick.exec(text);
64
+ }
65
+ const bare = /(?<![A-Za-z0-9_])((?:[A-Za-z0-9_.-]+\/)*[A-Za-z0-9_.-]+\.[A-Za-z][A-Za-z0-9]+)(?![A-Za-z0-9_])/g;
66
+ match = bare.exec(text);
67
+ while (match !== null) {
68
+ push(match[1] ?? "");
69
+ match = bare.exec(text);
70
+ }
71
+ return found;
72
+ }
73
+ function isMetaClause(text) {
74
+ return META_CLAUSE.test(text.trim());
75
+ }
76
+ function collectSectionItems(text, headingRe) {
77
+ const items = [];
78
+ let offset = 0;
79
+ for (const line of text.split("\n")) {
80
+ const match = headingRe.exec(line.trim());
81
+ if (match !== null) {
82
+ const hashes = (match[1] ?? "#").length;
83
+ const section = sliceAcSection(text, {
84
+ level: hashes,
85
+ sectionStart: offset + line.length + 1,
86
+ });
87
+ for (const item of parseListItems(section)) {
88
+ const title = normalizeClauseText(item.title.replace(/\*\*/g, ""));
89
+ if (title.length > 0 && !isMetaClause(title)) {
90
+ items.push(title);
91
+ }
92
+ }
93
+ }
94
+ offset += line.length + 1;
95
+ }
96
+ return items;
97
+ }
98
+ function matchLabeledAcLine(line) {
99
+ const trimmed = line.trim();
100
+ if (trimmed.length === 0) {
101
+ return null;
102
+ }
103
+ const lower = trimmed.toLowerCase();
104
+ for (const prefix of LABELED_AC_PREFIXES) {
105
+ if (!lower.startsWith(prefix)) {
106
+ continue;
107
+ }
108
+ const body = trimmed.slice(prefix.length).trim();
109
+ return body.length > 0 ? body : null;
110
+ }
111
+ return null;
112
+ }
113
+ function collectLabeledLines(text) {
114
+ const items = [];
115
+ for (const line of text.split("\n")) {
116
+ const body = matchLabeledAcLine(line);
117
+ if (body !== null && !isMetaClause(body)) {
118
+ items.push(normalizeClauseText(body));
119
+ }
120
+ }
121
+ return items;
122
+ }
123
+ function collectPathBearingLines(text) {
124
+ const items = [];
125
+ for (const item of parseListItems(text)) {
126
+ const title = normalizeClauseText(item.title.replace(/\*\*/g, ""));
127
+ if (title.length === 0 || isMetaClause(title)) {
128
+ continue;
129
+ }
130
+ if (extractPathTokens(title).length > 0) {
131
+ items.push(title);
132
+ }
133
+ }
134
+ return items;
135
+ }
136
+ /** Numbered independently testable clauses from the task statement (#3323). */
137
+ export function deriveAcceptanceClauses(taskStatement) {
138
+ const text = taskStatement.trim();
139
+ if (text.length === 0) {
140
+ return [];
141
+ }
142
+ const acHeading = findAcHeading(text);
143
+ const raw = [];
144
+ if (acHeading !== null) {
145
+ raw.push(...parseListItems(sliceAcSection(text, acHeading))
146
+ .map((item) => normalizeClauseText(item.title.replace(/\*\*/g, "")))
147
+ .filter((title) => title.length > 0 && !isMetaClause(title)));
148
+ }
149
+ raw.push(...collectSectionItems(text, SECTION_HEADING));
150
+ raw.push(...collectLabeledLines(text));
151
+ if (raw.length === 0) {
152
+ raw.push(...collectPathBearingLines(text));
153
+ }
154
+ const seen = new Set();
155
+ const clauses = [];
156
+ for (const line of raw) {
157
+ const key = line.toLowerCase();
158
+ if (seen.has(key)) {
159
+ continue;
160
+ }
161
+ seen.add(key);
162
+ clauses.push(buildClause(clauses.length + 1, line));
163
+ }
164
+ return clauses;
165
+ }
166
+ function buildClause(id, text) {
167
+ const paths = extractPathTokens(text);
168
+ if (paths.length <= 1) {
169
+ return {
170
+ id,
171
+ text,
172
+ artifact_path: paths[0] ?? null,
173
+ ambiguous: false,
174
+ };
175
+ }
176
+ const readings = paths.map((artifact_path) => ({
177
+ text: `${text} [reading: ${artifact_path}]`,
178
+ artifact_path,
179
+ }));
180
+ return {
181
+ id,
182
+ text,
183
+ artifact_path: paths[0] ?? null,
184
+ ambiguous: true,
185
+ readings,
186
+ chosen_reading: 0,
187
+ };
188
+ }
189
+ export function readAcceptanceClauses(acceptance) {
190
+ const rec = asRecord(acceptance);
191
+ if (rec === null || !Array.isArray(rec.clauses)) {
192
+ return [];
193
+ }
194
+ const out = [];
195
+ for (const [index, entry] of rec.clauses.entries()) {
196
+ const row = asRecord(entry);
197
+ if (row === null) {
198
+ continue;
199
+ }
200
+ const text = isNonEmptyString(row.text) ? normalizeClauseText(row.text) : "";
201
+ if (text.length === 0) {
202
+ continue;
203
+ }
204
+ const artifact = isNonEmptyString(row.artifact_path)
205
+ ? row.artifact_path.trim()
206
+ : isNonEmptyString(row.artifactPath)
207
+ ? row.artifactPath.trim()
208
+ : null;
209
+ const readings = Array.isArray(row.readings)
210
+ ? row.readings
211
+ .map((reading) => {
212
+ const rr = asRecord(reading);
213
+ if (rr === null || !isNonEmptyString(rr.text)) {
214
+ return null;
215
+ }
216
+ const path = isNonEmptyString(rr.artifact_path)
217
+ ? rr.artifact_path.trim()
218
+ : isNonEmptyString(rr.artifactPath)
219
+ ? rr.artifactPath.trim()
220
+ : null;
221
+ return { text: rr.text.trim(), artifact_path: path };
222
+ })
223
+ .filter((r) => r !== null)
224
+ : [];
225
+ const ambiguous = row.ambiguous === true || readings.length > 1;
226
+ const chosen = typeof row.chosen_reading === "number"
227
+ ? row.chosen_reading
228
+ : typeof row.chosenReading === "number"
229
+ ? row.chosenReading
230
+ : 0;
231
+ const chosenPath = ambiguous && readings[chosen] !== undefined ? readings[chosen].artifact_path : artifact;
232
+ out.push({
233
+ id: typeof row.id === "number" && row.id > 0 ? row.id : index + 1,
234
+ text,
235
+ artifact_path: chosenPath,
236
+ ambiguous,
237
+ ...(readings.length > 0 ? { readings, chosen_reading: chosen } : {}),
238
+ });
239
+ }
240
+ return out;
241
+ }
242
+ export function serializeAcceptanceClauses(clauses) {
243
+ return clauses.map((clause) => {
244
+ const row = {
245
+ id: clause.id,
246
+ text: clause.text,
247
+ artifact_path: clause.artifact_path,
248
+ ambiguous: clause.ambiguous,
249
+ };
250
+ if (clause.ambiguous && clause.readings !== undefined) {
251
+ row.readings = clause.readings.map((reading) => ({
252
+ text: reading.text,
253
+ artifact_path: reading.artifact_path,
254
+ }));
255
+ row.chosen_reading = clause.chosen_reading ?? 0;
256
+ }
257
+ return row;
258
+ });
259
+ }
260
+ /**
261
+ * When no commands are stated, stamp derived clauses onto plan.acceptance
262
+ * and promote source_rung to derived. Leaves stated-command acceptance alone.
263
+ */
264
+ export function stampDerivedClausesOnAcceptance(plan, taskStatement) {
265
+ const rec = asRecord(plan.acceptance);
266
+ if (rec === null) {
267
+ return { plan, clauses: [] };
268
+ }
269
+ if (rec.none_stated !== true) {
270
+ return { plan, clauses: [] };
271
+ }
272
+ const commands = rec.commands;
273
+ if (Array.isArray(commands) && commands.length > 0) {
274
+ return { plan, clauses: [] };
275
+ }
276
+ const clauses = deriveAcceptanceClauses(taskStatement);
277
+ if (clauses.length === 0) {
278
+ return { plan, clauses: [] };
279
+ }
280
+ return {
281
+ plan: {
282
+ ...plan,
283
+ acceptance: {
284
+ ...rec,
285
+ none_stated: true,
286
+ source_rung: "derived",
287
+ derived_reason: `derived ${clauses.length} independently testable clauses from the task statement before product edit (#3323)`,
288
+ clauses: serializeAcceptanceClauses(clauses),
289
+ },
290
+ },
291
+ clauses,
292
+ };
293
+ }
294
+ export function isScratchArtifactPath(artifactPath) {
295
+ const unified = artifactPath.replace(/\\/g, "/").toLowerCase();
296
+ return unified.split("/").some((seg) => SCRATCH_SEGMENTS.has(seg));
297
+ }
298
+ function isContained(root, child) {
299
+ const rel = relative(resolve(root), resolve(child));
300
+ return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
301
+ }
302
+ function extractExpectedTokens(clause) {
303
+ const tokens = [];
304
+ const seen = new Set();
305
+ const quoted = /["']([^"'\n]{3,80})["']/g;
306
+ let match = quoted.exec(clause.text);
307
+ while (match !== null) {
308
+ const token = (match[1] ?? "").trim();
309
+ const skip = token.length === 0 ||
310
+ token === clause.artifact_path ||
311
+ looksLikeFilePath(token) ||
312
+ seen.has(token);
313
+ if (!skip) {
314
+ seen.add(token);
315
+ tokens.push(token);
316
+ }
317
+ match = quoted.exec(clause.text);
318
+ }
319
+ return tokens;
320
+ }
321
+ function walkOne(clause, projectRoot) {
322
+ const artifactPath = clause.artifact_path;
323
+ if (artifactPath === null || artifactPath.trim().length === 0) {
324
+ return {
325
+ id: clause.id,
326
+ text: clause.text,
327
+ artifact_path: artifactPath,
328
+ outcome: "unverifiable",
329
+ detail: "no artifact path bound",
330
+ };
331
+ }
332
+ if (isScratchArtifactPath(artifactPath)) {
333
+ return {
334
+ id: clause.id,
335
+ text: clause.text,
336
+ artifact_path: artifactPath,
337
+ outcome: "failed",
338
+ detail: "artifact path is a buffer/scratch copy, not the shipped path",
339
+ };
340
+ }
341
+ const abs = resolve(projectRoot, artifactPath);
342
+ if (!isContained(projectRoot, abs)) {
343
+ return {
344
+ id: clause.id,
345
+ text: clause.text,
346
+ artifact_path: artifactPath,
347
+ outcome: "failed",
348
+ detail: "artifact path escaped the project root",
349
+ };
350
+ }
351
+ if (!existsSync(abs)) {
352
+ if (NEGATED_EXISTENCE.test(clause.text)) {
353
+ return {
354
+ id: clause.id,
355
+ text: clause.text,
356
+ artifact_path: artifactPath,
357
+ outcome: "verified",
358
+ detail: `artifact correctly absent at ${artifactPath}`,
359
+ };
360
+ }
361
+ return {
362
+ id: clause.id,
363
+ text: clause.text,
364
+ artifact_path: artifactPath,
365
+ outcome: "failed",
366
+ detail: `artifact missing at stated path ${artifactPath}`,
367
+ };
368
+ }
369
+ try {
370
+ if (!statSync(abs).isFile()) {
371
+ return {
372
+ id: clause.id,
373
+ text: clause.text,
374
+ artifact_path: artifactPath,
375
+ outcome: "failed",
376
+ detail: `stated path is not a shipped file: ${artifactPath}`,
377
+ };
378
+ }
379
+ }
380
+ catch {
381
+ return {
382
+ id: clause.id,
383
+ text: clause.text,
384
+ artifact_path: artifactPath,
385
+ outcome: "failed",
386
+ detail: `artifact unreadable at stated path ${artifactPath}`,
387
+ };
388
+ }
389
+ if (NEGATED_EXISTENCE.test(clause.text)) {
390
+ return {
391
+ id: clause.id,
392
+ text: clause.text,
393
+ artifact_path: artifactPath,
394
+ outcome: "failed",
395
+ detail: `artifact exists at ${artifactPath} but the clause requires absence`,
396
+ };
397
+ }
398
+ const expected = extractExpectedTokens(clause);
399
+ if (expected.length > 0) {
400
+ let body = "";
401
+ try {
402
+ body = readFileSync(abs, "utf8");
403
+ }
404
+ catch {
405
+ return {
406
+ id: clause.id,
407
+ text: clause.text,
408
+ artifact_path: artifactPath,
409
+ outcome: "failed",
410
+ detail: `artifact unreadable at stated path ${artifactPath}`,
411
+ };
412
+ }
413
+ const missing = expected.filter((token) => !body.includes(token));
414
+ if (missing.length > 0) {
415
+ return {
416
+ id: clause.id,
417
+ text: clause.text,
418
+ artifact_path: artifactPath,
419
+ outcome: "failed",
420
+ detail: `expected token(s) missing from ${artifactPath}: ${missing.join(", ")}`,
421
+ };
422
+ }
423
+ return {
424
+ id: clause.id,
425
+ text: clause.text,
426
+ artifact_path: artifactPath,
427
+ outcome: "verified",
428
+ detail: `tokens present in shipped artifact ${artifactPath}`,
429
+ };
430
+ }
431
+ if (EXISTENCE_CLAIM.test(clause.text)) {
432
+ return {
433
+ id: clause.id,
434
+ text: clause.text,
435
+ artifact_path: artifactPath,
436
+ outcome: "verified",
437
+ detail: `shipped artifact exists at ${artifactPath}`,
438
+ };
439
+ }
440
+ return {
441
+ id: clause.id,
442
+ text: clause.text,
443
+ artifact_path: artifactPath,
444
+ outcome: "unverifiable",
445
+ detail: `cannot evaluate behavioral claim against shipped artifact ${artifactPath}`,
446
+ };
447
+ }
448
+ export function walkAcceptanceClauses(clauses, projectRoot) {
449
+ const walked = clauses.map((clause) => walkOne(clause, projectRoot));
450
+ const failed = walked.filter((row) => row.outcome === "failed");
451
+ const unverifiable = walked.filter((row) => row.outcome === "unverifiable");
452
+ const verified = walked.filter((row) => row.outcome === "verified");
453
+ const ok = failed.length === 0 && (verified.length > 0 || walked.length === 0);
454
+ return {
455
+ clauses: walked,
456
+ failed,
457
+ unverifiable,
458
+ verified,
459
+ ok,
460
+ message: formatClauseWalkMessage({
461
+ clauses: walked,
462
+ failed,
463
+ unverifiable,
464
+ verified,
465
+ ok,
466
+ message: "",
467
+ }),
468
+ };
469
+ }
470
+ function formatOne(row) {
471
+ const path = row.artifact_path ?? "(no path)";
472
+ return ` [${row.outcome}] clause ${row.id} @ ${path}: ${row.text} — ${row.detail}`;
473
+ }
474
+ /** Done reports lead with failed/unverifiable; unverifiable is never dropped. */
475
+ export function formatClauseWalkMessage(report, prior) {
476
+ const lead = [...report.failed, ...report.unverifiable];
477
+ const rest = report.verified;
478
+ const lines = [
479
+ `verify:ac clause walk (#3323): ${report.verified.length} verified, ` +
480
+ `${report.unverifiable.length} unverifiable, ${report.failed.length} failed`,
481
+ ...lead.map(formatOne),
482
+ ...rest.map(formatOne),
483
+ ];
484
+ const body = lines.join("\n");
485
+ if (prior !== undefined && prior.trim().length > 0) {
486
+ return `${body}\n${prior}`;
487
+ }
488
+ return body;
489
+ }
490
+ //# sourceMappingURL=clauses.js.map
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Apply product-oracle integrity to verify:ac (#3322).
3
+ *
4
+ * Missing run-summary is a no-op (no evidence). A flagged
5
+ * fail → method-change → pass without independent_rederivation fails closed.
6
+ * Stdout dest (`DEFT_RUN_SUMMARY_PATH=-`) evaluates the same-process
7
+ * attempts just emitted by this verify:ac — it does not treat non-file
8
+ * dest as no evidence.
9
+ */
10
+ import type { LiteralAcceptanceRunResult } from "../literal-acceptance/types.js";
11
+ import { type FlaggedMethodChangePass } from "./flag.js";
12
+ export interface EvaluateProductOracleIntegrityOptions {
13
+ readonly projectRoot: string;
14
+ /** Injected JSONL (tests). Undefined → read dest; null → empty. */
15
+ readonly runSummaryText?: string | null;
16
+ readonly env?: NodeJS.ProcessEnv;
17
+ }
18
+ export interface ProductOracleIntegrityVerdict {
19
+ readonly ok: boolean;
20
+ readonly code: 0 | 1;
21
+ readonly flagged: readonly FlaggedMethodChangePass[];
22
+ readonly unresolved: readonly FlaggedMethodChangePass[];
23
+ readonly message: string;
24
+ }
25
+ export interface OracleIntegrityResultFields {
26
+ readonly ok: boolean;
27
+ readonly code: number;
28
+ readonly message: string;
29
+ }
30
+ /** Test seam: isolate stdout-dest buffer across cases. */
31
+ export declare function resetInProcessVerificationBuffer(): void;
32
+ /**
33
+ * Record each executed acceptance command as a verification event (#3322).
34
+ * Fail-open: missing dest / write errors never change the AC result.
35
+ * Stdout dest also appends to the same-process buffer so evaluate can
36
+ * inspect this invocation without re-reading a file.
37
+ */
38
+ export declare function emitVerifyAcAttempts(options: {
39
+ readonly projectRoot: string;
40
+ readonly runs: readonly LiteralAcceptanceRunResult[];
41
+ readonly env?: NodeJS.ProcessEnv;
42
+ readonly sessionId?: string;
43
+ /** stdout seam (tests). */
44
+ readonly writeStdout?: (line: string) => void;
45
+ }): void;
46
+ /**
47
+ * Evaluate run-summary verification events for pass-after-fail-with-method-change.
48
+ */
49
+ export declare function evaluateProductOracleIntegrity(options: EvaluateProductOracleIntegrityOptions): ProductOracleIntegrityVerdict;
50
+ /**
51
+ * Lead the verify:ac message with any unresolved discrepancy.
52
+ * Config errors (code 2) stay 2; unresolved oracle is exit 1.
53
+ */
54
+ export declare function mergeOracleVerdict<T extends OracleIntegrityResultFields>(result: T, verdict: ProductOracleIntegrityVerdict): T;
55
+ //# sourceMappingURL=evaluate.d.ts.map