@deftai/directive-core 0.101.0 → 0.103.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 (75) hide show
  1. package/dist/authz/classify.js +97 -8
  2. package/dist/check/cached-orchestrator.d.ts +4 -0
  3. package/dist/check/cached-orchestrator.js +70 -5
  4. package/dist/check/cli-native-gates.d.ts +43 -0
  5. package/dist/check/cli-native-gates.js +84 -0
  6. package/dist/check/index.d.ts +1 -0
  7. package/dist/check/index.js +1 -0
  8. package/dist/check/named-cause.js +10 -0
  9. package/dist/doctor/checks.d.ts +5 -6
  10. package/dist/doctor/checks.js +13 -56
  11. package/dist/eval/later-graduation.d.ts +39 -0
  12. package/dist/eval/later-graduation.js +91 -0
  13. package/dist/eval/report.d.ts +6 -0
  14. package/dist/eval/report.js +27 -8
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.js +1 -0
  17. package/dist/init-deposit/hygiene.d.ts +7 -1
  18. package/dist/init-deposit/hygiene.js +11 -3
  19. package/dist/init-deposit/scaffold.d.ts +12 -0
  20. package/dist/init-deposit/scaffold.js +81 -8
  21. package/dist/intake/issue-ingest.js +42 -0
  22. package/dist/policy/ceremony-dial-escalation.d.ts +52 -0
  23. package/dist/policy/ceremony-dial-escalation.js +92 -0
  24. package/dist/policy/ceremony-dial.d.ts +3 -1
  25. package/dist/policy/ceremony-dial.js +52 -1
  26. package/dist/policy/check-resume.d.ts +9 -38
  27. package/dist/policy/check-resume.js +18 -156
  28. package/dist/policy/coverage-debt.d.ts +18 -37
  29. package/dist/policy/coverage-debt.js +38 -147
  30. package/dist/policy/index.d.ts +1 -1
  31. package/dist/policy/index.js +1 -1
  32. package/dist/product-first-done-gate/acceptance.js +22 -0
  33. package/dist/product-first-done-gate/empty-resolution.d.ts +26 -0
  34. package/dist/product-first-done-gate/empty-resolution.js +38 -0
  35. package/dist/product-first-done-gate/evaluate.d.ts +40 -2
  36. package/dist/product-first-done-gate/evaluate.js +253 -61
  37. package/dist/product-first-done-gate/index.d.ts +2 -1
  38. package/dist/product-first-done-gate/index.js +2 -1
  39. package/dist/product-first-done-gate/types.d.ts +3 -0
  40. package/dist/product-first-done-gate/types.js +0 -7
  41. package/dist/run-summary/emit.d.ts +11 -1
  42. package/dist/run-summary/emit.js +48 -2
  43. package/dist/run-summary/index.d.ts +2 -1
  44. package/dist/run-summary/index.js +2 -1
  45. package/dist/run-summary/path.d.ts +1 -1
  46. package/dist/run-summary/path.js +1 -1
  47. package/dist/run-summary/share.d.ts +25 -0
  48. package/dist/run-summary/share.js +106 -0
  49. package/dist/run-summary/types.d.ts +60 -2
  50. package/dist/run-summary/types.js +7 -0
  51. package/dist/scope/acceptance-activate-gate.d.ts +23 -0
  52. package/dist/scope/acceptance-activate-gate.js +70 -0
  53. package/dist/scope/index.d.ts +1 -0
  54. package/dist/scope/index.js +1 -0
  55. package/dist/scope/transition.js +5 -0
  56. package/dist/session/index.d.ts +0 -1
  57. package/dist/session/index.js +0 -1
  58. package/dist/session/orientation-compression.js +19 -9
  59. package/dist/session/session-start.d.ts +6 -0
  60. package/dist/session/session-start.js +44 -22
  61. package/dist/session/toolchain-preflight.d.ts +15 -0
  62. package/dist/session/toolchain-preflight.js +65 -5
  63. package/dist/verify-ac/clauses.d.ts +51 -0
  64. package/dist/verify-ac/clauses.js +490 -0
  65. package/dist/verify-ac/evaluate.d.ts +73 -0
  66. package/dist/verify-ac/evaluate.js +167 -0
  67. package/dist/verify-ac/flag.d.ts +35 -0
  68. package/dist/verify-ac/flag.js +104 -0
  69. package/dist/verify-ac/index.d.ts +10 -0
  70. package/dist/verify-ac/index.js +10 -0
  71. package/package.json +7 -3
  72. package/dist/policy/coverage-check-resume-presets.d.ts +0 -46
  73. package/dist/policy/coverage-check-resume-presets.js +0 -228
  74. package/dist/session/coverage-check-resume-nudge.d.ts +0 -34
  75. package/dist/session/coverage-check-resume-nudge.js +0 -66
@@ -1,34 +1,26 @@
1
1
  /**
2
- * Coverage-debt hatch policy (#3189).
2
+ * Coverage-debt hatch policy (#3189 / #3314).
3
3
  *
4
- * Consent / SoT surface for consumer expansion of release-originated hatch
5
- * behavior (#3187). Unset is fail-closed for behavior (mode off) but still
6
- * nags on interactive session-start until decided or dismissed with reason.
7
- *
8
- * Non-goal: a consumer tree never auto-files coverage-debt issues on
9
- * deftai/directive -- ledger is always THIS repo.
4
+ * Plain optional setting. Default off. Fail-closed when absent or invalid.
5
+ * Hatch / auto-file are reserved not a ship gate. Live hatch is
6
+ * `--allow-coverage-debt=#N` (#2866).
10
7
  */
11
- import { readFileSync } from "node:fs";
12
- import { atomicWriteProjectDefinition, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
13
- import { migrateLegacyPolicyKey, PLAN_POLICY_KEY, readPlanPolicy } from "./plan-extensions.js";
14
- import { policyColonInvocation } from "./policy-invocation.js";
15
- import { appendAuditLog, loadProjectDefinition, projectDefinitionPath } from "./resolve.js";
8
+ import { resolveCheckResume } from "./check-resume.js";
9
+ import { readPlanPolicy } from "./plan-extensions.js";
10
+ import { loadProjectDefinition } from "./resolve.js";
16
11
  /** Canonical registered policy field name. */
17
12
  export const FIELD_COVERAGE_DEBT = "plan.policy.coverageDebt";
18
13
  /** Short alias for `policy:show --field=coverageDebt`. */
19
14
  export const FIELD_COVERAGE_DEBT_CLI_ALIAS = "coverageDebt";
20
- export const COVERAGE_DEBT_STATUSES = ["unset", "decided"];
21
15
  export const COVERAGE_DEBT_MODES = ["off", "warn", "hatch"];
22
- /** Fail-closed effective mode when unset / missing / invalid. */
16
+ /** Fail-closed effective mode when absent / missing / invalid. */
23
17
  export const DEFAULT_COVERAGE_DEBT_MODE = "off";
24
18
  /** Consumers default autoFile false even under hatch mode. */
25
19
  export const DEFAULT_COVERAGE_DEBT_AUTO_FILE = false;
26
20
  function defaultResolved(source, error = null) {
27
21
  return {
28
- status: "unset",
29
22
  mode: DEFAULT_COVERAGE_DEBT_MODE,
30
23
  autoFile: DEFAULT_COVERAGE_DEBT_AUTO_FILE,
31
- dismissReason: null,
32
24
  source,
33
25
  error,
34
26
  };
@@ -36,10 +28,7 @@ function defaultResolved(source, error = null) {
36
28
  function isMode(value) {
37
29
  return typeof value === "string" && COVERAGE_DEBT_MODES.includes(value);
38
30
  }
39
- function isStatus(value) {
40
- return typeof value === "string" && COVERAGE_DEBT_STATUSES.includes(value);
41
- }
42
- /** Validate a `plan.policy.coverageDebt` payload. */
31
+ /** Validate a `plan.policy.coverageDebt` payload. Extra keys are ignored. */
43
32
  export function validateCoverageDebt(value) {
44
33
  if (value === null || value === undefined) {
45
34
  return [];
@@ -49,25 +38,17 @@ export function validateCoverageDebt(value) {
49
38
  }
50
39
  const rec = value;
51
40
  const errors = [];
52
- if ("status" in rec && !isStatus(rec.status)) {
53
- errors.push(`${FIELD_COVERAGE_DEBT}.status must be one of ${COVERAGE_DEBT_STATUSES.join("|")}`);
54
- }
55
41
  if ("mode" in rec && !isMode(rec.mode)) {
56
42
  errors.push(`${FIELD_COVERAGE_DEBT}.mode must be one of ${COVERAGE_DEBT_MODES.join("|")}`);
57
43
  }
58
44
  if ("autoFile" in rec && typeof rec.autoFile !== "boolean") {
59
45
  errors.push(`${FIELD_COVERAGE_DEBT}.autoFile must be a boolean`);
60
46
  }
61
- if ("dismissReason" in rec &&
62
- rec.dismissReason !== null &&
63
- typeof rec.dismissReason !== "string") {
64
- errors.push(`${FIELD_COVERAGE_DEBT}.dismissReason must be a string or null`);
65
- }
66
47
  return errors;
67
48
  }
68
49
  /**
69
- * Resolve a typed block. Missing/invalid → fail-closed mode off, status unset.
70
- * Unset is not the same as decided-off for nag purposes.
50
+ * Resolve a typed block. Missing/invalid → fail-closed mode off.
51
+ * `mode` is the setting; leftover ritual fields (status/dismissReason) are ignored.
71
52
  */
72
53
  export function resolveCoverageDebtFromTypedBlock(raw) {
73
54
  const errors = validateCoverageDebt(raw);
@@ -78,26 +59,17 @@ export function resolveCoverageDebtFromTypedBlock(raw) {
78
59
  return defaultResolved("default");
79
60
  }
80
61
  const block = raw;
81
- const status = isStatus(block.status) ? block.status : "unset";
82
62
  const mode = isMode(block.mode) ? block.mode : DEFAULT_COVERAGE_DEBT_MODE;
83
- // Fail-closed: hatch modes only apply when decided.
84
- const effectiveMode = status === "decided" ? mode : DEFAULT_COVERAGE_DEBT_MODE;
85
63
  const autoFileRaw = typeof block.autoFile === "boolean" ? block.autoFile : DEFAULT_COVERAGE_DEBT_AUTO_FILE;
86
- // autoFile only meaningful under hatch; never on when not hatch.
87
- const autoFile = effectiveMode === "hatch" ? autoFileRaw : false;
88
- const dismissReason = typeof block.dismissReason === "string" && block.dismissReason.trim().length > 0
89
- ? block.dismissReason.trim()
90
- : null;
64
+ const autoFile = mode === "hatch" ? autoFileRaw : false;
91
65
  return {
92
- status,
93
- mode: status === "decided" ? mode : DEFAULT_COVERAGE_DEBT_MODE,
66
+ mode,
94
67
  autoFile,
95
- dismissReason: status === "decided" ? dismissReason : null,
96
68
  source: "typed",
97
69
  error: null,
98
70
  };
99
71
  }
100
- /** Resolve `plan.policy.coverageDebt` from PROJECT-DEFINITION (#3189). */
72
+ /** Resolve `plan.policy.coverageDebt` from PROJECT-DEFINITION (#3314). */
101
73
  export function resolveCoverageDebt(projectRoot) {
102
74
  const [data, err] = loadProjectDefinition(projectRoot);
103
75
  if (data === null) {
@@ -112,40 +84,28 @@ export function resolveCoverageDebt(projectRoot) {
112
84
  }
113
85
  return resolveCoverageDebtFromTypedBlock(policyBlock.coverageDebt);
114
86
  }
115
- /** Effective hatch soft-pass allowed only when decided mode=hatch. */
87
+ /** Effective hatch soft-pass allowed only when mode=hatch. Reserved — unused by check/release. */
116
88
  export function isCoverageDebtHatchAllowed(policy) {
117
- return policy.status === "decided" && policy.mode === "hatch";
89
+ return policy.mode === "hatch";
118
90
  }
119
91
  /** Auto-file debt issues only when hatch is allowed AND autoFile true. */
120
92
  export function isCoverageDebtAutoFileAllowed(policy) {
121
93
  return isCoverageDebtHatchAllowed(policy) && policy.autoFile === true;
122
94
  }
123
- /**
124
- * Non-goal guard (#3189): consumer trees never auto-file coverage-debt on
125
- * deftai/directive. Ledger is always the project under check.
126
- */
127
- export function coverageDebtLedgerRepoIsSelfOnly() {
128
- return true;
129
- }
130
95
  export function formatCoverageDebtStatusLine(policy) {
131
- const dismiss = policy.dismissReason !== null ? ` dismissReason=${JSON.stringify(policy.dismissReason)}` : "";
132
- return (`[deft policy] coverageDebt status=${policy.status} mode=${policy.mode} ` +
133
- `autoFile=${String(policy.autoFile)}${dismiss} (source=${policy.source}).`);
96
+ return (`[deft policy] coverageDebt mode=${policy.mode} ` +
97
+ `autoFile=${String(policy.autoFile)} (source=${policy.source}).`);
134
98
  }
135
99
  function fieldFromResolved(resolved) {
136
100
  return {
137
101
  name: FIELD_COVERAGE_DEBT,
138
102
  current: {
139
- status: resolved.status,
140
103
  mode: resolved.mode,
141
104
  autoFile: resolved.autoFile,
142
- dismissReason: resolved.dismissReason,
143
105
  },
144
106
  default: {
145
- status: "unset",
146
107
  mode: DEFAULT_COVERAGE_DEBT_MODE,
147
108
  autoFile: DEFAULT_COVERAGE_DEBT_AUTO_FILE,
148
- dismissReason: null,
149
109
  },
150
110
  source: resolved.source,
151
111
  };
@@ -167,96 +127,27 @@ export function inspectCoverageDebt(data, projectRoot) {
167
127
  }
168
128
  return fieldFromResolved(resolveCoverageDebtFromTypedBlock(policyBlock.coverageDebt));
169
129
  }
170
- /** Persist decided coverageDebt (preset or dismiss-with-reason). */
171
- export function writeCoverageDebt(projectRoot, options) {
172
- const path = projectDefinitionPath(projectRoot);
173
- try {
174
- const { changed } = projectDefinitionMutationLock(projectRoot, () => {
175
- const parsed = JSON.parse(readFileSync(path, { encoding: "utf8" }));
176
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
177
- throw new Error(`PROJECT-DEFINITION at ${path} top-level value is not a JSON object`);
178
- }
179
- const data = parsed;
180
- if (typeof data.plan !== "object" || data.plan === null || Array.isArray(data.plan)) {
181
- if (data.plan === undefined) {
182
- data.plan = {};
183
- }
184
- else {
185
- throw new Error("PROJECT-DEFINITION 'plan' is not an object");
186
- }
187
- }
188
- const plan = data.plan;
189
- migrateLegacyPolicyKey(plan);
190
- const existingPolicy = plan[PLAN_POLICY_KEY];
191
- if (typeof existingPolicy !== "object" ||
192
- existingPolicy === null ||
193
- Array.isArray(existingPolicy)) {
194
- if (existingPolicy === undefined) {
195
- plan[PLAN_POLICY_KEY] = {};
196
- }
197
- else {
198
- throw new Error("plan.policy is not an object");
199
- }
200
- }
201
- const policyBlock = plan[PLAN_POLICY_KEY];
202
- const previous = policyBlock.coverageDebt;
203
- const autoFile = options.mode === "hatch" ? (options.autoFile ?? DEFAULT_COVERAGE_DEBT_AUTO_FILE) : false;
204
- const dismissReason = typeof options.dismissReason === "string" && options.dismissReason.trim().length > 0
205
- ? options.dismissReason.trim()
206
- : null;
207
- const nextBlock = {
208
- status: "decided",
209
- mode: options.mode,
210
- autoFile,
211
- dismissReason,
212
- };
213
- const previousNormalized = resolveCoverageDebtFromTypedBlock(previous);
214
- const changedFlag = previousNormalized.status !== nextBlock.status ||
215
- previousNormalized.mode !== nextBlock.mode ||
216
- previousNormalized.autoFile !== nextBlock.autoFile ||
217
- previousNormalized.dismissReason !== nextBlock.dismissReason;
218
- policyBlock.coverageDebt = nextBlock;
219
- if (changedFlag) {
220
- atomicWriteProjectDefinition(path, data);
221
- }
222
- const actor = options.actor ?? policyColonInvocation("set-coverage-debt");
223
- const note = options.note ?? "";
224
- const parts = [
225
- `actor=${actor}`,
226
- "coverageDebt.status=decided",
227
- `mode=${nextBlock.mode}`,
228
- `autoFile=${String(nextBlock.autoFile)}`,
229
- `dismissReason=${JSON.stringify(nextBlock.dismissReason)}`,
230
- `previous=${JSON.stringify(previous ?? null)}`,
231
- ];
232
- if (note) {
233
- parts.push(`note=${note.replace(/\n/g, " ").replace(/\r/g, " ")}`);
234
- }
235
- appendAuditLog(projectRoot, parts.join(" "));
236
- return { changed: changedFlag };
237
- });
238
- const resolved = resolveCoverageDebt(projectRoot);
239
- const lines = [
240
- `\u2713 ${FIELD_COVERAGE_DEBT} status=decided mode=${resolved.mode}.`,
241
- changed
242
- ? " audit: meta/policy-changes.log updated."
243
- : " no-op: value already matched (audit entry still appended for trail).",
244
- formatCoverageDebtStatusLine(resolved),
245
- ];
246
- return { exitCode: 0, stdout: `${lines.join("\n")}\n`, changed };
130
+ /**
131
+ * One-line standing disclosure when either setting is non-default (#3314).
132
+ * Silent when both default / invalid (invalid is fail-closed off).
133
+ */
134
+ export function coverageCheckResumeDisclosureLine(debt, resume) {
135
+ const debtNonDefault = debt.source !== "default-on-error" && debt.mode !== DEFAULT_COVERAGE_DEBT_MODE;
136
+ const resumeNonDefault = resume.source !== "default-on-error" && resume.localStamp === "on";
137
+ if (!debtNonDefault && !resumeNonDefault) {
138
+ return null;
247
139
  }
248
- catch (err) {
249
- const message = err instanceof Error ? err.message : String(err);
250
- if (message.includes("PROJECT-DEFINITION not found") ||
251
- message.includes("ENOENT") ||
252
- message.includes("no such file")) {
253
- return {
254
- exitCode: 2,
255
- stdout: `\u274c PROJECT-DEFINITION not found under ${projectRoot}\n`,
256
- changed: false,
257
- };
258
- }
259
- return { exitCode: 2, stdout: `\u274c Config error: ${message}\n`, changed: false };
140
+ const parts = [];
141
+ if (debtNonDefault) {
142
+ parts.push(`coverageDebt.mode=${debt.mode}`);
143
+ }
144
+ if (resumeNonDefault) {
145
+ parts.push("checkResume.localStamp=on");
260
146
  }
147
+ return `[deft policy] ${parts.join(" ")} (reserved; not a ship gate).`;
148
+ }
149
+ /** Resolve both settings and format the standing disclosure, or null. */
150
+ export function maybeFormatCoverageCheckResumeDisclosure(projectRoot) {
151
+ return coverageCheckResumeDisclosureLine(resolveCoverageDebt(projectRoot), resolveCheckResume(projectRoot));
261
152
  }
262
153
  //# sourceMappingURL=coverage-debt.js.map
@@ -3,8 +3,8 @@ export * from "./agents-md-advisory.js";
3
3
  export * from "./autonomy.js";
4
4
  export * from "./capacity.js";
5
5
  export * from "./ceremony-dial.js";
6
+ export * from "./ceremony-dial-escalation.js";
6
7
  export * from "./check-resume.js";
7
- export * from "./coverage-check-resume-presets.js";
8
8
  export * from "./coverage-debt.js";
9
9
  export * from "./decisions.js";
10
10
  export * from "./deft-directive-disable.js";
@@ -22,8 +22,8 @@ export * from "./agents-md-advisory.js";
22
22
  export * from "./autonomy.js";
23
23
  export * from "./capacity.js";
24
24
  export * from "./ceremony-dial.js";
25
+ export * from "./ceremony-dial-escalation.js";
25
26
  export * from "./check-resume.js";
26
- export * from "./coverage-check-resume-presets.js";
27
27
  export * from "./coverage-debt.js";
28
28
  export * from "./decisions.js";
29
29
  export * from "./deft-directive-disable.js";
@@ -5,6 +5,7 @@
5
5
  * Interoperates with #3267 plan.metadata.literal_acceptance_commands.
6
6
  */
7
7
  import { attachLiteralAcceptanceCommands, readStoredLiteralAcceptanceCommands, } from "../literal-acceptance/index.js";
8
+ import { readAcceptanceClauses, serializeAcceptanceClauses } from "../verify-ac/clauses.js";
8
9
  import { PLAN_ACCEPTANCE_KEY, } from "./types.js";
9
10
  function asRecord(value) {
10
11
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
@@ -73,6 +74,22 @@ export function validatePlanAcceptance(value) {
73
74
  if ("source_rung" in rec && rec.source_rung !== undefined && !isAcSourceRung(rec.source_rung)) {
74
75
  errors.push('plan.acceptance.source_rung must be "stated" | "derived" | "project_floor"');
75
76
  }
77
+ if ("clauses" in rec && rec.clauses !== undefined && !Array.isArray(rec.clauses)) {
78
+ errors.push("plan.acceptance.clauses must be an array");
79
+ }
80
+ if (Array.isArray(rec.clauses)) {
81
+ rec.clauses.forEach((entry, index) => {
82
+ const row = asRecord(entry);
83
+ if (row === null) {
84
+ errors.push(`plan.acceptance.clauses[${index}] must be an object`);
85
+ return;
86
+ }
87
+ const text = isNonEmptyString(row.text) ? row.text.trim() : "";
88
+ if (text.length === 0) {
89
+ errors.push(`plan.acceptance.clauses[${index}].text must be a non-empty string`);
90
+ }
91
+ });
92
+ }
76
93
  const commands = coerceCommands(rec.commands);
77
94
  const noneStated = rec.none_stated === true;
78
95
  if (commands.length === 0 && !noneStated) {
@@ -110,6 +127,7 @@ export function readPlanAcceptance(plan) {
110
127
  if (noneStated && commands.length === 0) {
111
128
  sourceRung = isAcSourceRung(rec.source_rung) ? rec.source_rung : "project_floor";
112
129
  }
130
+ const clauses = readAcceptanceClauses(rec);
113
131
  return {
114
132
  commands,
115
133
  none_stated: noneStated,
@@ -119,6 +137,7 @@ export function readPlanAcceptance(plan) {
119
137
  : isNonEmptyString(rec.derivedReason)
120
138
  ? rec.derivedReason
121
139
  : null,
140
+ ...(clauses.length > 0 ? { clauses } : {}),
122
141
  };
123
142
  }
124
143
  }
@@ -181,6 +200,9 @@ export function attachPlanAcceptance(plan, acceptance) {
181
200
  if (acceptance.derived_reason) {
182
201
  serializable.derived_reason = acceptance.derived_reason;
183
202
  }
203
+ if (acceptance.clauses !== undefined && acceptance.clauses.length > 0) {
204
+ serializable.clauses = serializeAcceptanceClauses(acceptance.clauses);
205
+ }
184
206
  let next = {
185
207
  ...plan,
186
208
  [PLAN_ACCEPTANCE_KEY]: serializable,
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Empty verify:ac resolution is not a green run when there is no suite floor (#3334).
3
+ *
4
+ * Framework source keeps today's floor-pass (suite gates own the floor).
5
+ * Consumer / no-suite projects fail closed with a distinct soft_empty outcome.
6
+ */
7
+ import type { PlanAcceptance } from "./types.js";
8
+ export declare const EMPTY_AC_OUTCOME: "soft_empty";
9
+ export declare const EMPTY_AC_CAUSE = "no acceptance stamped \u2014 floor is empty in this project";
10
+ export declare const EMPTY_AC_REMEDY = "stamp plan.acceptance (author commands or run intake capture / task issue:ingest)";
11
+ export type VerifyAcResolution = "verified-pass" | "empty-pass" | "soft_empty" | "fail" | "config" | "skipped";
12
+ /** True when this project check composition includes a suite gate (#3188). */
13
+ export declare function projectHasSuiteFloor(projectRoot: string): boolean;
14
+ export declare function isSoftEmptyAcText(text: string): boolean;
15
+ export interface EmptyAcResolutionInput {
16
+ readonly ok: boolean;
17
+ readonly code: number;
18
+ readonly runsLength: number;
19
+ readonly commandCount: number;
20
+ readonly rejectedCount: number;
21
+ readonly resolution?: VerifyAcResolution;
22
+ }
23
+ /** Zero executable commands, no rejected/unpromoted ledger, not already classified. */
24
+ export declare function isEmptyAcResolution(input: EmptyAcResolutionInput): boolean;
25
+ export declare function formatSoftEmptyMessage(acceptance: PlanAcceptance, quiet?: boolean): string;
26
+ //# sourceMappingURL=empty-resolution.d.ts.map
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Empty verify:ac resolution is not a green run when there is no suite floor (#3334).
3
+ *
4
+ * Framework source keeps today's floor-pass (suite gates own the floor).
5
+ * Consumer / no-suite projects fail closed with a distinct soft_empty outcome.
6
+ */
7
+ import { isFrameworkRepoRoot } from "../check/context.js";
8
+ export const EMPTY_AC_OUTCOME = "soft_empty";
9
+ export const EMPTY_AC_CAUSE = "no acceptance stamped — floor is empty in this project";
10
+ export const EMPTY_AC_REMEDY = "stamp plan.acceptance (author commands or run intake capture / task issue:ingest)";
11
+ /** True when this project check composition includes a suite gate (#3188). */
12
+ export function projectHasSuiteFloor(projectRoot) {
13
+ return isFrameworkRepoRoot(projectRoot);
14
+ }
15
+ export function isSoftEmptyAcText(text) {
16
+ return /soft_empty\s*\(#3334\)/i.test(text);
17
+ }
18
+ /** Zero executable commands, no rejected/unpromoted ledger, not already classified. */
19
+ export function isEmptyAcResolution(input) {
20
+ if (input.resolution === "config" || input.resolution === "skipped") {
21
+ return false;
22
+ }
23
+ if (input.code === 2) {
24
+ return false;
25
+ }
26
+ if (input.rejectedCount > 0 || input.commandCount > 0 || input.runsLength > 0) {
27
+ return false;
28
+ }
29
+ return true;
30
+ }
31
+ export function formatSoftEmptyMessage(acceptance, quiet) {
32
+ if (quiet) {
33
+ return "";
34
+ }
35
+ return (`verify:ac ${EMPTY_AC_OUTCOME} (#3334) [rung=${acceptance.source_rung}]: ` +
36
+ `${EMPTY_AC_CAUSE}. ${EMPTY_AC_REMEDY}.`);
37
+ }
38
+ //# sourceMappingURL=empty-resolution.js.map
@@ -3,15 +3,22 @@
3
3
  *
4
4
  * Runs plan.acceptance.commands (or #3267 literal ledger) via the shared
5
5
  * literal-acceptance runner. Records source_rung in the result message.
6
- * Project floor with empty commands is a soft pass (suite gates own the floor
7
- * inside full `task check`); standalone done paths still surface the rung.
6
+ * Project floor with empty commands is a soft pass only when a suite floor
7
+ * exists (suite gates own the floor inside full `task check`). With no suite
8
+ * floor, empty resolution is soft_empty — not a green run (#3334).
8
9
  */
9
10
  import { type EvaluateLiteralAcceptanceOptions, type LiteralAcceptanceGateResult } from "../literal-acceptance/index.js";
11
+ import { type ClauseWalkResult } from "../verify-ac/clauses.js";
12
+ import { type VerifyAcResolution } from "./empty-resolution.js";
10
13
  import type { AcSourceRung, PlanAcceptance } from "./types.js";
11
14
  export interface VerifyAcResult extends LiteralAcceptanceGateResult {
12
15
  readonly sourceRung: AcSourceRung;
13
16
  readonly noneStated: boolean;
14
17
  readonly acceptance: PlanAcceptance;
18
+ readonly resolution: VerifyAcResolution;
19
+ readonly resolvedCommandCount: number;
20
+ readonly clauseOutcomes?: readonly ClauseWalkResult[];
21
+ readonly clauseWalked?: boolean;
15
22
  }
16
23
  export interface EvaluateVerifyAcOptions extends EvaluateLiteralAcceptanceOptions {
17
24
  /**
@@ -36,6 +43,31 @@ export interface EvaluateVerifyAcOptions extends EvaluateLiteralAcceptanceOption
36
43
  readonly bankOnPass?: boolean;
37
44
  /** Optional scope id override for the bank ledger (default plan.id / path). */
38
45
  readonly bankScopeId?: string | null;
46
+ /**
47
+ * Injected run-summary JSONL for product-oracle integrity (#3322).
48
+ * Undefined → read DEFT_RUN_SUMMARY_PATH / default dest; null → skip disk.
49
+ */
50
+ readonly runSummaryText?: string | null;
51
+ /** When false, skip #3322 oracle integrity. Default true. */
52
+ readonly applyOracleIntegrity?: boolean;
53
+ /** Env seam for run-summary dest resolution (#3322 / #3334). */
54
+ readonly env?: NodeJS.ProcessEnv;
55
+ /**
56
+ * Inject suite-floor detection (tests). Default: framework source has a
57
+ * suite floor; consumer projects do not (#3334).
58
+ */
59
+ readonly hasSuiteFloor?: boolean;
60
+ /**
61
+ * When true, skip the acceptance run-summary emit so the path helper can
62
+ * emit after the #3285 bank checkpoint.
63
+ */
64
+ readonly skipAcceptanceEmit?: boolean;
65
+ /**
66
+ * Active scope key for product-oracle check_id namespacing (#3337).
67
+ * Prefer plan.id; path stem when id is missing. Multi-active verify:ac
68
+ * under one session must not share a single global `verify:ac` check id.
69
+ */
70
+ readonly oracleScopeKey?: string | null;
39
71
  }
40
72
  /**
41
73
  * Evaluate product AC from an in-memory plan.
@@ -45,6 +77,12 @@ export declare function evaluateVerifyAcFromPlan(plan: Record<string, unknown>,
45
77
  * Evaluate from xBRIEF path.
46
78
  */
47
79
  export declare function evaluateVerifyAcFromPath(xbriefPath: string, options?: EvaluateVerifyAcOptions): VerifyAcResult;
80
+ /**
81
+ * Unique product-oracle scope key for one active xBRIEF path (#3337).
82
+ * Relative path is always unique across active roots; plan.id alone is not
83
+ * (duplicate ids / same stem in xbrief+vbrief). Prefer `id@relPath` when both exist.
84
+ */
85
+ export declare function resolveOracleScopeKey(plan: Record<string, unknown>, xbriefPath: string, projectRoot: string): string;
48
86
  /** Pure: product AC is required at every ceremony depth (#3284 / #3267 / #3156). */
49
87
  export declare function isVerifyAcRequiredAtCeremonyDepth(_depth: string | null | undefined): boolean;
50
88
  //# sourceMappingURL=evaluate.d.ts.map