@deftai/directive-core 0.101.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 (71) 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/intake/issue-ingest.js +42 -0
  18. package/dist/policy/ceremony-dial-escalation.d.ts +52 -0
  19. package/dist/policy/ceremony-dial-escalation.js +92 -0
  20. package/dist/policy/ceremony-dial.d.ts +3 -1
  21. package/dist/policy/ceremony-dial.js +52 -1
  22. package/dist/policy/check-resume.d.ts +9 -38
  23. package/dist/policy/check-resume.js +18 -156
  24. package/dist/policy/coverage-debt.d.ts +18 -37
  25. package/dist/policy/coverage-debt.js +38 -147
  26. package/dist/policy/index.d.ts +1 -1
  27. package/dist/policy/index.js +1 -1
  28. package/dist/product-first-done-gate/acceptance.js +22 -0
  29. package/dist/product-first-done-gate/empty-resolution.d.ts +26 -0
  30. package/dist/product-first-done-gate/empty-resolution.js +38 -0
  31. package/dist/product-first-done-gate/evaluate.d.ts +28 -2
  32. package/dist/product-first-done-gate/evaluate.js +209 -53
  33. package/dist/product-first-done-gate/index.d.ts +1 -0
  34. package/dist/product-first-done-gate/index.js +1 -0
  35. package/dist/product-first-done-gate/types.d.ts +3 -0
  36. package/dist/product-first-done-gate/types.js +0 -7
  37. package/dist/run-summary/emit.d.ts +11 -1
  38. package/dist/run-summary/emit.js +48 -2
  39. package/dist/run-summary/index.d.ts +2 -1
  40. package/dist/run-summary/index.js +2 -1
  41. package/dist/run-summary/path.d.ts +1 -1
  42. package/dist/run-summary/path.js +1 -1
  43. package/dist/run-summary/share.d.ts +25 -0
  44. package/dist/run-summary/share.js +106 -0
  45. package/dist/run-summary/types.d.ts +60 -2
  46. package/dist/run-summary/types.js +7 -0
  47. package/dist/scope/acceptance-activate-gate.d.ts +23 -0
  48. package/dist/scope/acceptance-activate-gate.js +70 -0
  49. package/dist/scope/index.d.ts +1 -0
  50. package/dist/scope/index.js +1 -0
  51. package/dist/scope/transition.js +5 -0
  52. package/dist/session/index.d.ts +0 -1
  53. package/dist/session/index.js +0 -1
  54. package/dist/session/orientation-compression.js +19 -9
  55. package/dist/session/session-start.d.ts +6 -0
  56. package/dist/session/session-start.js +44 -22
  57. package/dist/session/toolchain-preflight.d.ts +15 -0
  58. package/dist/session/toolchain-preflight.js +65 -5
  59. package/dist/verify-ac/clauses.d.ts +51 -0
  60. package/dist/verify-ac/clauses.js +490 -0
  61. package/dist/verify-ac/evaluate.d.ts +55 -0
  62. package/dist/verify-ac/evaluate.js +145 -0
  63. package/dist/verify-ac/flag.d.ts +35 -0
  64. package/dist/verify-ac/flag.js +104 -0
  65. package/dist/verify-ac/index.d.ts +10 -0
  66. package/dist/verify-ac/index.js +10 -0
  67. package/package.json +7 -3
  68. package/dist/policy/coverage-check-resume-presets.d.ts +0 -46
  69. package/dist/policy/coverage-check-resume-presets.js +0 -228
  70. package/dist/session/coverage-check-resume-nudge.d.ts +0 -34
  71. package/dist/session/coverage-check-resume-nudge.js +0 -66
@@ -1,45 +1,29 @@
1
1
  /**
2
- * Check-resume / local suite-stamp policy (#3189).
2
+ * Check-resume / local suite-stamp policy (#3189 / #3314).
3
3
  *
4
- * Consent / SoT surface for consumer expansion of local suite-stamp resume
5
- * (#3188). Unset is fail-closed (localStamp off). CI never trusts a local
6
- * stamp in v1 — `ciTrustsLocalStamp` is fixed false.
4
+ * Plain optional setting. Default off. Fail-closed when absent or invalid.
5
+ * Reserved consumer expansion not implemented. CI never trusts a laptop stamp.
7
6
  */
8
- import { readFileSync } from "node:fs";
9
- import { atomicWriteProjectDefinition, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
10
- import { migrateLegacyPolicyKey, PLAN_POLICY_KEY, readPlanPolicy } from "./plan-extensions.js";
11
- import { policyColonInvocation } from "./policy-invocation.js";
12
- import { appendAuditLog, loadProjectDefinition, projectDefinitionPath } from "./resolve.js";
7
+ import { readPlanPolicy } from "./plan-extensions.js";
8
+ import { loadProjectDefinition } from "./resolve.js";
13
9
  /** Canonical registered policy field name. */
14
10
  export const FIELD_CHECK_RESUME = "plan.policy.checkResume";
15
11
  /** Short alias for `policy:show --field=checkResume`. */
16
12
  export const FIELD_CHECK_RESUME_CLI_ALIAS = "checkResume";
17
- export const CHECK_RESUME_STATUSES = ["unset", "decided"];
18
13
  export const CHECK_RESUME_LOCAL_STAMP = ["off", "on"];
19
- /** Fail-closed when unset / missing. */
14
+ /** Fail-closed when absent / missing / invalid. */
20
15
  export const DEFAULT_CHECK_RESUME_LOCAL_STAMP = "off";
21
- /**
22
- * FIXED false in v1 (#3189 non-goal). Separate RFC required to ever allow CI
23
- * to trust a laptop suite stamp.
24
- */
25
- export const CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1 = false;
26
16
  function defaultResolved(source, error = null) {
27
17
  return {
28
- status: "unset",
29
18
  localStamp: DEFAULT_CHECK_RESUME_LOCAL_STAMP,
30
- ciTrustsLocalStamp: CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1,
31
- dismissReason: null,
32
19
  source,
33
20
  error,
34
21
  };
35
22
  }
36
- function isStatus(value) {
37
- return typeof value === "string" && CHECK_RESUME_STATUSES.includes(value);
38
- }
39
23
  function isLocalStamp(value) {
40
24
  return (typeof value === "string" && CHECK_RESUME_LOCAL_STAMP.includes(value));
41
25
  }
42
- /** Validate a `plan.policy.checkResume` payload. */
26
+ /** Validate a `plan.policy.checkResume` payload. Extra keys are ignored. */
43
27
  export function validateCheckResume(value) {
44
28
  if (value === null || value === undefined) {
45
29
  return [];
@@ -48,27 +32,16 @@ export function validateCheckResume(value) {
48
32
  return [`${FIELD_CHECK_RESUME} must be an object; got ${typeof value}`];
49
33
  }
50
34
  const rec = value;
51
- const errors = [];
52
- if ("status" in rec && !isStatus(rec.status)) {
53
- errors.push(`${FIELD_CHECK_RESUME}.status must be one of ${CHECK_RESUME_STATUSES.join("|")}`);
54
- }
55
35
  if ("localStamp" in rec && !isLocalStamp(rec.localStamp)) {
56
- errors.push(`${FIELD_CHECK_RESUME}.localStamp must be one of ${CHECK_RESUME_LOCAL_STAMP.join("|")}`);
57
- }
58
- if ("ciTrustsLocalStamp" in rec && rec.ciTrustsLocalStamp !== false) {
59
- errors.push(`${FIELD_CHECK_RESUME}.ciTrustsLocalStamp must be false in v1 (CI never trusts local stamps)`);
60
- }
61
- if ("dismissReason" in rec &&
62
- rec.dismissReason !== null &&
63
- typeof rec.dismissReason !== "string") {
64
- errors.push(`${FIELD_CHECK_RESUME}.dismissReason must be a string or null`);
36
+ return [
37
+ `${FIELD_CHECK_RESUME}.localStamp must be one of ${CHECK_RESUME_LOCAL_STAMP.join("|")}`,
38
+ ];
65
39
  }
66
- return errors;
40
+ return [];
67
41
  }
68
42
  /**
69
- * Resolve a typed block. Missing/invalid → localStamp off, status unset.
70
- * `ciTrustsLocalStamp` is always forced false regardless of input truthiness
71
- * (malformed non-false is validation error → fail-closed default).
43
+ * Resolve a typed block. Missing/invalid → localStamp off.
44
+ * Leftover ritual fields (status/dismissReason/ciTrustsLocalStamp) are ignored.
72
45
  */
73
46
  export function resolveCheckResumeFromTypedBlock(raw) {
74
47
  const errors = validateCheckResume(raw);
@@ -79,25 +52,16 @@ export function resolveCheckResumeFromTypedBlock(raw) {
79
52
  return defaultResolved("default");
80
53
  }
81
54
  const block = raw;
82
- const status = isStatus(block.status) ? block.status : "unset";
83
- const localStampRaw = isLocalStamp(block.localStamp)
55
+ const localStamp = isLocalStamp(block.localStamp)
84
56
  ? block.localStamp
85
57
  : DEFAULT_CHECK_RESUME_LOCAL_STAMP;
86
- // Fail-closed: local stamp only when decided.
87
- const localStamp = status === "decided" ? localStampRaw : DEFAULT_CHECK_RESUME_LOCAL_STAMP;
88
- const dismissReason = typeof block.dismissReason === "string" && block.dismissReason.trim().length > 0
89
- ? block.dismissReason.trim()
90
- : null;
91
58
  return {
92
- status,
93
59
  localStamp,
94
- ciTrustsLocalStamp: CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1,
95
- dismissReason: status === "decided" ? dismissReason : null,
96
60
  source: "typed",
97
61
  error: null,
98
62
  };
99
63
  }
100
- /** Resolve `plan.policy.checkResume` from PROJECT-DEFINITION (#3189). */
64
+ /** Resolve `plan.policy.checkResume` from PROJECT-DEFINITION (#3314). */
101
65
  export function resolveCheckResume(projectRoot) {
102
66
  const [data, err] = loadProjectDefinition(projectRoot);
103
67
  if (data === null) {
@@ -112,33 +76,21 @@ export function resolveCheckResume(projectRoot) {
112
76
  }
113
77
  return resolveCheckResumeFromTypedBlock(policyBlock.checkResume);
114
78
  }
115
- /** Local suite stamp resume only when decided localStamp=on. */
79
+ /** Local suite stamp resume only when localStamp=on. Reserved — unused by check/release. */
116
80
  export function isLocalStampResumeAllowed(policy) {
117
- return policy.status === "decided" && policy.localStamp === "on";
118
- }
119
- /** Always false in v1 — CI must re-run suite, never trust a laptop stamp. */
120
- export function isCiTrustsLocalStampAllowed(_policy) {
121
- return CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1;
81
+ return policy.localStamp === "on";
122
82
  }
123
83
  export function formatCheckResumeStatusLine(policy) {
124
- const dismiss = policy.dismissReason !== null ? ` dismissReason=${JSON.stringify(policy.dismissReason)}` : "";
125
- return (`[deft policy] checkResume status=${policy.status} localStamp=${policy.localStamp} ` +
126
- `ciTrustsLocalStamp=${String(policy.ciTrustsLocalStamp)}${dismiss} (source=${policy.source}).`);
84
+ return (`[deft policy] checkResume localStamp=${policy.localStamp} ` + `(source=${policy.source}).`);
127
85
  }
128
86
  function fieldFromResolved(resolved) {
129
87
  return {
130
88
  name: FIELD_CHECK_RESUME,
131
89
  current: {
132
- status: resolved.status,
133
90
  localStamp: resolved.localStamp,
134
- ciTrustsLocalStamp: CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1,
135
- dismissReason: resolved.dismissReason,
136
91
  },
137
92
  default: {
138
- status: "unset",
139
93
  localStamp: DEFAULT_CHECK_RESUME_LOCAL_STAMP,
140
- ciTrustsLocalStamp: CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1,
141
- dismissReason: null,
142
94
  },
143
95
  source: resolved.source,
144
96
  };
@@ -160,94 +112,4 @@ export function inspectCheckResume(data, projectRoot) {
160
112
  }
161
113
  return fieldFromResolved(resolveCheckResumeFromTypedBlock(policyBlock.checkResume));
162
114
  }
163
- /** Persist decided checkResume (preset or dismiss-with-reason). */
164
- export function writeCheckResume(projectRoot, options) {
165
- const path = projectDefinitionPath(projectRoot);
166
- try {
167
- const { changed } = projectDefinitionMutationLock(projectRoot, () => {
168
- const parsed = JSON.parse(readFileSync(path, { encoding: "utf8" }));
169
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
170
- throw new Error(`PROJECT-DEFINITION at ${path} top-level value is not a JSON object`);
171
- }
172
- const data = parsed;
173
- if (typeof data.plan !== "object" || data.plan === null || Array.isArray(data.plan)) {
174
- if (data.plan === undefined) {
175
- data.plan = {};
176
- }
177
- else {
178
- throw new Error("PROJECT-DEFINITION 'plan' is not an object");
179
- }
180
- }
181
- const plan = data.plan;
182
- migrateLegacyPolicyKey(plan);
183
- const existingPolicy = plan[PLAN_POLICY_KEY];
184
- if (typeof existingPolicy !== "object" ||
185
- existingPolicy === null ||
186
- Array.isArray(existingPolicy)) {
187
- if (existingPolicy === undefined) {
188
- plan[PLAN_POLICY_KEY] = {};
189
- }
190
- else {
191
- throw new Error("plan.policy is not an object");
192
- }
193
- }
194
- const policyBlock = plan[PLAN_POLICY_KEY];
195
- const previous = policyBlock.checkResume;
196
- const dismissReason = typeof options.dismissReason === "string" && options.dismissReason.trim().length > 0
197
- ? options.dismissReason.trim()
198
- : null;
199
- const nextBlock = {
200
- status: "decided",
201
- localStamp: options.localStamp,
202
- ciTrustsLocalStamp: CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1,
203
- dismissReason,
204
- };
205
- const previousNormalized = resolveCheckResumeFromTypedBlock(previous);
206
- const changedFlag = previousNormalized.status !== nextBlock.status ||
207
- previousNormalized.localStamp !== nextBlock.localStamp ||
208
- previousNormalized.dismissReason !== nextBlock.dismissReason;
209
- policyBlock.checkResume = nextBlock;
210
- if (changedFlag) {
211
- atomicWriteProjectDefinition(path, data);
212
- }
213
- const actor = options.actor ?? policyColonInvocation("set-check-resume");
214
- const note = options.note ?? "";
215
- const parts = [
216
- `actor=${actor}`,
217
- "checkResume.status=decided",
218
- `localStamp=${nextBlock.localStamp}`,
219
- "ciTrustsLocalStamp=false",
220
- `dismissReason=${JSON.stringify(nextBlock.dismissReason)}`,
221
- `previous=${JSON.stringify(previous ?? null)}`,
222
- ];
223
- if (note) {
224
- parts.push(`note=${note.replace(/\n/g, " ").replace(/\r/g, " ")}`);
225
- }
226
- appendAuditLog(projectRoot, parts.join(" "));
227
- return { changed: changedFlag };
228
- });
229
- const resolved = resolveCheckResume(projectRoot);
230
- const lines = [
231
- `\u2713 ${FIELD_CHECK_RESUME} status=decided localStamp=${resolved.localStamp}.`,
232
- changed
233
- ? " audit: meta/policy-changes.log updated."
234
- : " no-op: value already matched (audit entry still appended for trail).",
235
- formatCheckResumeStatusLine(resolved),
236
- ];
237
- return { exitCode: 0, stdout: `${lines.join("\n")}\n`, changed };
238
- }
239
- catch (err) {
240
- const message = err instanceof Error ? err.message : String(err);
241
- if (message.includes("PROJECT-DEFINITION not found") ||
242
- message.includes("ENOENT") ||
243
- message.includes("no such file")) {
244
- return {
245
- exitCode: 2,
246
- stdout: `\u274c PROJECT-DEFINITION not found under ${projectRoot}\n`,
247
- changed: false,
248
- };
249
- }
250
- return { exitCode: 2, stdout: `\u274c Config error: ${message}\n`, changed: false };
251
- }
252
- }
253
115
  //# sourceMappingURL=check-resume.js.map
@@ -1,55 +1,43 @@
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
  */
8
+ import { type CheckResumeResolved } from "./check-resume.js";
11
9
  /** Canonical registered policy field name. */
12
10
  export declare const FIELD_COVERAGE_DEBT = "plan.policy.coverageDebt";
13
11
  /** Short alias for `policy:show --field=coverageDebt`. */
14
12
  export declare const FIELD_COVERAGE_DEBT_CLI_ALIAS = "coverageDebt";
15
- export declare const COVERAGE_DEBT_STATUSES: readonly ["unset", "decided"];
16
- export type CoverageDebtStatus = (typeof COVERAGE_DEBT_STATUSES)[number];
17
13
  export declare const COVERAGE_DEBT_MODES: readonly ["off", "warn", "hatch"];
18
14
  export type CoverageDebtMode = (typeof COVERAGE_DEBT_MODES)[number];
19
- /** Fail-closed effective mode when unset / missing / invalid. */
15
+ /** Fail-closed effective mode when absent / missing / invalid. */
20
16
  export declare const DEFAULT_COVERAGE_DEBT_MODE: CoverageDebtMode;
21
17
  /** Consumers default autoFile false even under hatch mode. */
22
18
  export declare const DEFAULT_COVERAGE_DEBT_AUTO_FILE = false;
23
19
  export interface CoverageDebtConfig {
24
- readonly status: CoverageDebtStatus;
25
20
  readonly mode: CoverageDebtMode;
26
21
  readonly autoFile: boolean;
27
- /** Present when decided via dismiss-with-reason (still visible to show/doctor). */
28
- readonly dismissReason: string | null;
29
22
  }
30
23
  export type CoverageDebtSource = "typed" | "default" | "default-on-error";
31
24
  export interface CoverageDebtResolved extends CoverageDebtConfig {
32
25
  readonly source: CoverageDebtSource;
33
26
  readonly error: string | null;
34
27
  }
35
- /** Validate a `plan.policy.coverageDebt` payload. */
28
+ /** Validate a `plan.policy.coverageDebt` payload. Extra keys are ignored. */
36
29
  export declare function validateCoverageDebt(value: unknown): string[];
37
30
  /**
38
- * Resolve a typed block. Missing/invalid → fail-closed mode off, status unset.
39
- * Unset is not the same as decided-off for nag purposes.
31
+ * Resolve a typed block. Missing/invalid → fail-closed mode off.
32
+ * `mode` is the setting; leftover ritual fields (status/dismissReason) are ignored.
40
33
  */
41
34
  export declare function resolveCoverageDebtFromTypedBlock(raw: unknown): CoverageDebtResolved;
42
- /** Resolve `plan.policy.coverageDebt` from PROJECT-DEFINITION (#3189). */
35
+ /** Resolve `plan.policy.coverageDebt` from PROJECT-DEFINITION (#3314). */
43
36
  export declare function resolveCoverageDebt(projectRoot: string): CoverageDebtResolved;
44
- /** Effective hatch soft-pass allowed only when decided mode=hatch. */
37
+ /** Effective hatch soft-pass allowed only when mode=hatch. Reserved — unused by check/release. */
45
38
  export declare function isCoverageDebtHatchAllowed(policy: CoverageDebtResolved): boolean;
46
39
  /** Auto-file debt issues only when hatch is allowed AND autoFile true. */
47
40
  export declare function isCoverageDebtAutoFileAllowed(policy: CoverageDebtResolved): boolean;
48
- /**
49
- * Non-goal guard (#3189): consumer trees never auto-file coverage-debt on
50
- * deftai/directive. Ledger is always the project under check.
51
- */
52
- export declare function coverageDebtLedgerRepoIsSelfOnly(): true;
53
41
  export declare function formatCoverageDebtStatusLine(policy: CoverageDebtResolved): string;
54
42
  export interface CoverageDebtPolicyField {
55
43
  readonly name: typeof FIELD_COVERAGE_DEBT;
@@ -59,18 +47,11 @@ export interface CoverageDebtPolicyField {
59
47
  }
60
48
  /** Inspector row for `policy:show --field=coverageDebt`. */
61
49
  export declare function inspectCoverageDebt(data: Record<string, unknown> | null, projectRoot?: string): CoverageDebtPolicyField;
62
- export interface WriteCoverageDebtOptions {
63
- readonly mode: CoverageDebtMode;
64
- readonly autoFile?: boolean;
65
- readonly dismissReason?: string | null;
66
- readonly actor?: string;
67
- readonly note?: string;
68
- }
69
- export interface WriteCoverageDebtResult {
70
- readonly exitCode: 0 | 2;
71
- readonly stdout: string;
72
- readonly changed: boolean;
73
- }
74
- /** Persist decided coverageDebt (preset or dismiss-with-reason). */
75
- export declare function writeCoverageDebt(projectRoot: string, options: WriteCoverageDebtOptions): WriteCoverageDebtResult;
50
+ /**
51
+ * One-line standing disclosure when either setting is non-default (#3314).
52
+ * Silent when both default / invalid (invalid is fail-closed off).
53
+ */
54
+ export declare function coverageCheckResumeDisclosureLine(debt: CoverageDebtResolved, resume: Pick<CheckResumeResolved, "localStamp" | "source">): string | null;
55
+ /** Resolve both settings and format the standing disclosure, or null. */
56
+ export declare function maybeFormatCoverageCheckResumeDisclosure(projectRoot: string): string | null;
76
57
  //# sourceMappingURL=coverage-debt.d.ts.map
@@ -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";