@deftai/directive-core 0.96.0 → 0.98.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/authz/classify.js +291 -0
- package/dist/cache/archive.js +10 -4
- package/dist/check/cached-orchestrator.d.ts +5 -0
- package/dist/check/cached-orchestrator.js +18 -1
- package/dist/check/gate-lists.d.ts +20 -0
- package/dist/check/gate-lists.js +54 -9
- package/dist/check/index.d.ts +1 -1
- package/dist/check/index.js +1 -1
- package/dist/check/orchestrator.d.ts +4 -0
- package/dist/check/orchestrator.js +4 -0
- package/dist/consumer-check-contract/evaluate.d.ts +124 -0
- package/dist/consumer-check-contract/evaluate.js +699 -0
- package/dist/consumer-check-contract/index.d.ts +5 -0
- package/dist/consumer-check-contract/index.js +5 -0
- package/dist/delivery-attempt/disk-begin.d.ts +51 -0
- package/dist/delivery-attempt/disk-begin.js +68 -0
- package/dist/delivery-attempt/evaluate.d.ts +26 -0
- package/dist/delivery-attempt/evaluate.js +443 -0
- package/dist/delivery-attempt/fingerprint.d.ts +32 -0
- package/dist/delivery-attempt/fingerprint.js +100 -0
- package/dist/delivery-attempt/handoff.d.ts +25 -0
- package/dist/delivery-attempt/handoff.js +102 -0
- package/dist/delivery-attempt/index.d.ts +17 -0
- package/dist/delivery-attempt/index.js +17 -0
- package/dist/delivery-attempt/ledger.d.ts +169 -0
- package/dist/delivery-attempt/ledger.js +758 -0
- package/dist/delivery-attempt/material-delta.d.ts +38 -0
- package/dist/delivery-attempt/material-delta.js +126 -0
- package/dist/delivery-attempt/types.d.ts +210 -0
- package/dist/delivery-attempt/types.js +77 -0
- package/dist/doctor/checks.d.ts +7 -0
- package/dist/doctor/checks.js +83 -0
- package/dist/doctor/index.d.ts +1 -0
- package/dist/doctor/index.js +1 -0
- package/dist/doctor/main.js +12 -0
- package/dist/doctor/openclaw-soft-rebind.d.ts +26 -0
- package/dist/doctor/openclaw-soft-rebind.js +164 -0
- package/dist/hooks/dispatcher.d.ts +7 -1
- package/dist/hooks/dispatcher.js +117 -13
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/init-deposit/gitignore.js +7 -0
- package/dist/init-deposit/hygiene.d.ts +70 -1
- package/dist/init-deposit/hygiene.js +582 -8
- package/dist/init-deposit/init-deposit.js +5 -0
- package/dist/init-deposit/refresh.js +3 -0
- package/dist/init-deposit/scaffold.js +277 -4
- package/dist/init-deposit/skill-discovery-deposit.js +15 -0
- package/dist/policy/check-resume.d.ts +72 -0
- package/dist/policy/check-resume.js +253 -0
- package/dist/policy/coverage-check-resume-presets.d.ts +46 -0
- package/dist/policy/coverage-check-resume-presets.js +228 -0
- package/dist/policy/coverage-debt.d.ts +76 -0
- package/dist/policy/coverage-debt.js +262 -0
- package/dist/policy/index.d.ts +3 -0
- package/dist/policy/index.js +50 -21
- package/dist/pr-merge-readiness/ci-gate.d.ts +29 -1
- package/dist/pr-merge-readiness/ci-gate.js +191 -24
- package/dist/pr-merge-readiness/compute.js +10 -1
- package/dist/pr-merge-readiness/index.d.ts +2 -1
- package/dist/pr-merge-readiness/index.js +2 -1
- package/dist/pr-merge-readiness/output.js +14 -0
- package/dist/pr-merge-readiness/platform-status.d.ts +29 -0
- package/dist/pr-merge-readiness/platform-status.js +49 -0
- package/dist/pr-watch/constants.d.ts +10 -0
- package/dist/pr-watch/constants.js +12 -1
- package/dist/pr-watch/main.js +16 -1
- package/dist/pr-watch/probe.js +13 -1
- package/dist/pr-watch/types.d.ts +3 -2
- package/dist/pr-watch/watch.js +14 -7
- package/dist/release/auto-hatch.d.ts +114 -0
- package/dist/release/auto-hatch.js +301 -0
- package/dist/release/coverage-debt-ledger.d.ts +22 -0
- package/dist/release/coverage-debt-ledger.js +157 -0
- package/dist/release/index.d.ts +3 -0
- package/dist/release/index.js +3 -0
- package/dist/release/pipeline.js +164 -12
- package/dist/release/suite-stamp.d.ts +44 -0
- package/dist/release/suite-stamp.js +133 -0
- package/dist/release/types.d.ts +19 -0
- package/dist/scope-provenance/digest.d.ts +67 -0
- package/dist/scope-provenance/digest.js +188 -0
- package/dist/scope-provenance/evaluate.d.ts +82 -0
- package/dist/scope-provenance/evaluate.js +528 -0
- package/dist/scope-provenance/index.d.ts +6 -0
- package/dist/scope-provenance/index.js +6 -0
- package/dist/session/compact-ritual.d.ts +96 -0
- package/dist/session/compact-ritual.js +237 -0
- package/dist/session/compact-ritual.spec.d.ts +2 -0
- package/dist/session/compact-ritual.spec.js +21 -0
- package/dist/session/coverage-check-resume-nudge.d.ts +34 -0
- package/dist/session/coverage-check-resume-nudge.js +66 -0
- package/dist/session/index.d.ts +3 -0
- package/dist/session/index.js +3 -0
- package/dist/session/openclaw-soft-rebind-deposit.d.ts +46 -0
- package/dist/session/openclaw-soft-rebind-deposit.js +165 -0
- package/dist/session/session-start.js +21 -0
- package/dist/test-boundary/evaluate.d.ts +54 -0
- package/dist/test-boundary/evaluate.js +368 -0
- package/dist/test-boundary/index.d.ts +6 -0
- package/dist/test-boundary/index.js +6 -0
- package/dist/test-boundary/policy.d.ts +52 -0
- package/dist/test-boundary/policy.js +182 -0
- package/dist/triage/bootstrap/gitignore.d.ts +1 -1
- package/dist/triage/bootstrap/gitignore.js +15 -1
- package/dist/triage/classify/label-mirror.d.ts +31 -1
- package/dist/triage/classify/label-mirror.js +78 -6
- package/dist/triage/help/registry-data.d.ts +6 -6
- package/dist/triage/help/registry-data.js +12 -3
- package/dist/vbrief-activate/activate.js +22 -6
- package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
- package/dist/vbrief-validate/plan-hooks.js +54 -0
- package/dist/xbrief/styles.js +33 -17
- package/package.json +7 -3
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundled presets for coverageDebt + checkResume (#3189).
|
|
3
|
+
*
|
|
4
|
+
* One project decision (not five micro-toggles in v1).
|
|
5
|
+
* Later does not write decided; Strict / Hatch-aware / dismiss-with-reason do.
|
|
6
|
+
*
|
|
7
|
+
* Both fields are written in a single PROJECT-DEFINITION mutation lock so a
|
|
8
|
+
* partial decision (coverageDebt decided, checkResume still unset) cannot
|
|
9
|
+
* persist after a mid-write failure.
|
|
10
|
+
*/
|
|
11
|
+
export declare const COVERAGE_CHECK_RESUME_PRESETS: readonly ["strict", "hatch-aware"];
|
|
12
|
+
export type CoverageCheckResumePreset = (typeof COVERAGE_CHECK_RESUME_PRESETS)[number];
|
|
13
|
+
export interface ApplyCoverageCheckResumePresetOptions {
|
|
14
|
+
readonly actor?: string;
|
|
15
|
+
readonly note?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ApplyCoverageCheckResumePresetResult {
|
|
18
|
+
readonly exitCode: 0 | 1 | 2;
|
|
19
|
+
readonly stdout: string;
|
|
20
|
+
readonly changed: boolean;
|
|
21
|
+
readonly preset: CoverageCheckResumePreset | "dismiss" | "later";
|
|
22
|
+
}
|
|
23
|
+
/** True when either policy is still unset (coupled v1 nag). */
|
|
24
|
+
export declare function isCoverageCheckResumeUndecided(projectRoot: string): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Apply Strict: coverageDebt.mode=off, checkResume.localStamp=off, both decided.
|
|
27
|
+
* Recommended for most apps.
|
|
28
|
+
*/
|
|
29
|
+
export declare function applyStrictCoverageCheckResumePreset(projectRoot: string, options?: ApplyCoverageCheckResumePresetOptions): ApplyCoverageCheckResumePresetResult;
|
|
30
|
+
/**
|
|
31
|
+
* Apply Hatch-aware: coverageDebt.mode=hatch autoFile=false; localStamp=on for DX.
|
|
32
|
+
* CI still never trusts local stamps.
|
|
33
|
+
*/
|
|
34
|
+
export declare function applyHatchAwareCoverageCheckResumePreset(projectRoot: string, options?: ApplyCoverageCheckResumePresetOptions): ApplyCoverageCheckResumePresetResult;
|
|
35
|
+
/**
|
|
36
|
+
* Dismiss-with-reason: marks both decided as fail-closed (mode off / localStamp off)
|
|
37
|
+
* and records the reason for policy:show / doctor visibility. Stops the session nag.
|
|
38
|
+
*/
|
|
39
|
+
export declare function dismissCoverageCheckResume(projectRoot: string, reason: string, options?: ApplyCoverageCheckResumePresetOptions): ApplyCoverageCheckResumePresetResult;
|
|
40
|
+
/**
|
|
41
|
+
* Later: does NOT write status=decided. Caller skips for this session;
|
|
42
|
+
* next session-start ritual nags again.
|
|
43
|
+
*/
|
|
44
|
+
export declare function applyLaterCoverageCheckResumeSkip(): ApplyCoverageCheckResumePresetResult;
|
|
45
|
+
export declare function formatCoverageCheckResumeBundleStatus(projectRoot: string): string;
|
|
46
|
+
//# sourceMappingURL=coverage-check-resume-presets.d.ts.map
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundled presets for coverageDebt + checkResume (#3189).
|
|
3
|
+
*
|
|
4
|
+
* One project decision (not five micro-toggles in v1).
|
|
5
|
+
* Later does not write decided; Strict / Hatch-aware / dismiss-with-reason do.
|
|
6
|
+
*
|
|
7
|
+
* Both fields are written in a single PROJECT-DEFINITION mutation lock so a
|
|
8
|
+
* partial decision (coverageDebt decided, checkResume still unset) cannot
|
|
9
|
+
* persist after a mid-write failure.
|
|
10
|
+
*/
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
12
|
+
import { atomicWriteProjectDefinition, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
|
|
13
|
+
import { CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1, formatCheckResumeStatusLine, resolveCheckResume, resolveCheckResumeFromTypedBlock, } from "./check-resume.js";
|
|
14
|
+
import { DEFAULT_COVERAGE_DEBT_AUTO_FILE, formatCoverageDebtStatusLine, resolveCoverageDebt, resolveCoverageDebtFromTypedBlock, } from "./coverage-debt.js";
|
|
15
|
+
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY } from "./plan-extensions.js";
|
|
16
|
+
import { policyColonInvocation } from "./policy-invocation.js";
|
|
17
|
+
import { appendAuditLog, projectDefinitionPath } from "./resolve.js";
|
|
18
|
+
export const COVERAGE_CHECK_RESUME_PRESETS = ["strict", "hatch-aware"];
|
|
19
|
+
/** True when either policy is still unset (coupled v1 nag). */
|
|
20
|
+
export function isCoverageCheckResumeUndecided(projectRoot) {
|
|
21
|
+
const debt = resolveCoverageDebt(projectRoot);
|
|
22
|
+
const resume = resolveCheckResume(projectRoot);
|
|
23
|
+
return debt.status === "unset" || resume.status === "unset";
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Atomically write both coverageDebt and checkResume under one mutation lock.
|
|
27
|
+
*/
|
|
28
|
+
function writeCoverageCheckResumeBundle(projectRoot, input) {
|
|
29
|
+
const path = projectDefinitionPath(projectRoot);
|
|
30
|
+
try {
|
|
31
|
+
const writeResult = projectDefinitionMutationLock(projectRoot, () => {
|
|
32
|
+
const parsed = JSON.parse(readFileSync(path, { encoding: "utf8" }));
|
|
33
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
34
|
+
throw new Error(`PROJECT-DEFINITION at ${path} top-level value is not a JSON object`);
|
|
35
|
+
}
|
|
36
|
+
const data = parsed;
|
|
37
|
+
if (typeof data.plan !== "object" || data.plan === null || Array.isArray(data.plan)) {
|
|
38
|
+
if (data.plan === undefined) {
|
|
39
|
+
data.plan = {};
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
throw new Error("PROJECT-DEFINITION 'plan' is not an object");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const plan = data.plan;
|
|
46
|
+
migrateLegacyPolicyKey(plan);
|
|
47
|
+
const existingPolicy = plan[PLAN_POLICY_KEY];
|
|
48
|
+
if (typeof existingPolicy !== "object" ||
|
|
49
|
+
existingPolicy === null ||
|
|
50
|
+
Array.isArray(existingPolicy)) {
|
|
51
|
+
if (existingPolicy === undefined) {
|
|
52
|
+
plan[PLAN_POLICY_KEY] = {};
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
throw new Error("plan.policy is not an object");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const policyBlock = plan[PLAN_POLICY_KEY];
|
|
59
|
+
const previousDebt = policyBlock.coverageDebt;
|
|
60
|
+
const previousResume = policyBlock.checkResume;
|
|
61
|
+
const autoFile = input.mode === "hatch" ? (input.autoFile ?? DEFAULT_COVERAGE_DEBT_AUTO_FILE) : false;
|
|
62
|
+
const dismissReason = typeof input.dismissReason === "string" && input.dismissReason.trim().length > 0
|
|
63
|
+
? input.dismissReason.trim()
|
|
64
|
+
: null;
|
|
65
|
+
const nextDebt = {
|
|
66
|
+
status: "decided",
|
|
67
|
+
mode: input.mode,
|
|
68
|
+
autoFile,
|
|
69
|
+
dismissReason,
|
|
70
|
+
};
|
|
71
|
+
const nextResume = {
|
|
72
|
+
status: "decided",
|
|
73
|
+
localStamp: input.localStamp,
|
|
74
|
+
ciTrustsLocalStamp: CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1,
|
|
75
|
+
dismissReason,
|
|
76
|
+
};
|
|
77
|
+
const prevDebt = resolveCoverageDebtFromTypedBlock(previousDebt);
|
|
78
|
+
const prevResume = resolveCheckResumeFromTypedBlock(previousResume);
|
|
79
|
+
const changedFlag = prevDebt.status !== nextDebt.status ||
|
|
80
|
+
prevDebt.mode !== nextDebt.mode ||
|
|
81
|
+
prevDebt.autoFile !== nextDebt.autoFile ||
|
|
82
|
+
prevDebt.dismissReason !== nextDebt.dismissReason ||
|
|
83
|
+
prevResume.status !== nextResume.status ||
|
|
84
|
+
prevResume.localStamp !== nextResume.localStamp ||
|
|
85
|
+
prevResume.dismissReason !== nextResume.dismissReason;
|
|
86
|
+
// Single write of both keys — no partial decide on mid-failure.
|
|
87
|
+
policyBlock.coverageDebt = nextDebt;
|
|
88
|
+
policyBlock.checkResume = nextResume;
|
|
89
|
+
if (changedFlag) {
|
|
90
|
+
atomicWriteProjectDefinition(path, data);
|
|
91
|
+
}
|
|
92
|
+
let auditOk = true;
|
|
93
|
+
// Audit after durable write: failure must not reverse the decision, but
|
|
94
|
+
// the operator report must not claim the audit trail was updated.
|
|
95
|
+
try {
|
|
96
|
+
const note = input.note.replace(/\n/g, " ").replace(/\r/g, " ");
|
|
97
|
+
appendAuditLog(projectRoot, [
|
|
98
|
+
`actor=${input.actor}`,
|
|
99
|
+
`preset=${input.preset}`,
|
|
100
|
+
"coverageDebt.status=decided",
|
|
101
|
+
`mode=${nextDebt.mode}`,
|
|
102
|
+
`autoFile=${String(nextDebt.autoFile)}`,
|
|
103
|
+
"checkResume.status=decided",
|
|
104
|
+
`localStamp=${nextResume.localStamp}`,
|
|
105
|
+
"ciTrustsLocalStamp=false",
|
|
106
|
+
`dismissReason=${JSON.stringify(dismissReason)}`,
|
|
107
|
+
`previousDebt=${JSON.stringify(previousDebt ?? null)}`,
|
|
108
|
+
`previousResume=${JSON.stringify(previousResume ?? null)}`,
|
|
109
|
+
note ? `note=${note}` : "",
|
|
110
|
+
]
|
|
111
|
+
.filter(Boolean)
|
|
112
|
+
.join(" "));
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
auditOk = false;
|
|
116
|
+
}
|
|
117
|
+
return { changed: changedFlag, auditOk };
|
|
118
|
+
});
|
|
119
|
+
const summary = formatCoverageCheckResumeBundleStatus(projectRoot);
|
|
120
|
+
const auditLine = writeResult.auditOk
|
|
121
|
+
? writeResult.changed
|
|
122
|
+
? " audit: meta/policy-changes.log updated."
|
|
123
|
+
: " no-op: value already matched (audit entry still appended for trail)."
|
|
124
|
+
: " warning: policy write succeeded but audit log append failed; decision is durable.";
|
|
125
|
+
const lines = [
|
|
126
|
+
`\u2713 coverageDebt+checkResume decided via preset=${input.preset} (atomic write).`,
|
|
127
|
+
auditLine,
|
|
128
|
+
summary,
|
|
129
|
+
];
|
|
130
|
+
return {
|
|
131
|
+
exitCode: 0,
|
|
132
|
+
stdout: `${lines.join("\n")}\n`,
|
|
133
|
+
changed: writeResult.changed,
|
|
134
|
+
preset: input.preset,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
139
|
+
if (message.includes("PROJECT-DEFINITION not found") ||
|
|
140
|
+
message.includes("ENOENT") ||
|
|
141
|
+
message.includes("no such file")) {
|
|
142
|
+
return {
|
|
143
|
+
exitCode: 2,
|
|
144
|
+
stdout: `\u274c PROJECT-DEFINITION not found under ${projectRoot}\n`,
|
|
145
|
+
changed: false,
|
|
146
|
+
preset: input.preset,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
exitCode: 2,
|
|
151
|
+
stdout: `\u274c Config error: ${message}\n`,
|
|
152
|
+
changed: false,
|
|
153
|
+
preset: input.preset,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Apply Strict: coverageDebt.mode=off, checkResume.localStamp=off, both decided.
|
|
159
|
+
* Recommended for most apps.
|
|
160
|
+
*/
|
|
161
|
+
export function applyStrictCoverageCheckResumePreset(projectRoot, options = {}) {
|
|
162
|
+
return writeCoverageCheckResumeBundle(projectRoot, {
|
|
163
|
+
mode: "off",
|
|
164
|
+
autoFile: false,
|
|
165
|
+
localStamp: "off",
|
|
166
|
+
actor: options.actor ?? policyColonInvocation("coverage-check-resume-preset", " strict"),
|
|
167
|
+
note: options.note ?? "preset=strict",
|
|
168
|
+
preset: "strict",
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Apply Hatch-aware: coverageDebt.mode=hatch autoFile=false; localStamp=on for DX.
|
|
173
|
+
* CI still never trusts local stamps.
|
|
174
|
+
*/
|
|
175
|
+
export function applyHatchAwareCoverageCheckResumePreset(projectRoot, options = {}) {
|
|
176
|
+
return writeCoverageCheckResumeBundle(projectRoot, {
|
|
177
|
+
mode: "hatch",
|
|
178
|
+
autoFile: false,
|
|
179
|
+
localStamp: "on",
|
|
180
|
+
actor: options.actor ?? policyColonInvocation("coverage-check-resume-preset", " hatch-aware"),
|
|
181
|
+
note: options.note ?? "preset=hatch-aware",
|
|
182
|
+
preset: "hatch-aware",
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Dismiss-with-reason: marks both decided as fail-closed (mode off / localStamp off)
|
|
187
|
+
* and records the reason for policy:show / doctor visibility. Stops the session nag.
|
|
188
|
+
*/
|
|
189
|
+
export function dismissCoverageCheckResume(projectRoot, reason, options = {}) {
|
|
190
|
+
const trimmed = reason.trim();
|
|
191
|
+
if (trimmed.length === 0) {
|
|
192
|
+
return {
|
|
193
|
+
exitCode: 1,
|
|
194
|
+
stdout: "\u274c dismiss-with-reason requires a non-empty reason " +
|
|
195
|
+
"(Later without a reason does not decide; re-nag next ritual).\n",
|
|
196
|
+
changed: false,
|
|
197
|
+
preset: "dismiss",
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
return writeCoverageCheckResumeBundle(projectRoot, {
|
|
201
|
+
mode: "off",
|
|
202
|
+
autoFile: false,
|
|
203
|
+
localStamp: "off",
|
|
204
|
+
dismissReason: trimmed,
|
|
205
|
+
actor: options.actor ?? policyColonInvocation("coverage-check-resume-dismiss"),
|
|
206
|
+
note: options.note ?? `dismiss=${trimmed}`,
|
|
207
|
+
preset: "dismiss",
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Later: does NOT write status=decided. Caller skips for this session;
|
|
212
|
+
* next session-start ritual nags again.
|
|
213
|
+
*/
|
|
214
|
+
export function applyLaterCoverageCheckResumeSkip() {
|
|
215
|
+
return {
|
|
216
|
+
exitCode: 0,
|
|
217
|
+
stdout: "[deft policy] coverageDebt/checkResume: Later -- not decided. " +
|
|
218
|
+
"Fail-closed defaults remain (mode off, localStamp off, CI never trusts local stamps). " +
|
|
219
|
+
"Next interactive session-start ritual will remind you.\n",
|
|
220
|
+
changed: false,
|
|
221
|
+
preset: "later",
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
export function formatCoverageCheckResumeBundleStatus(projectRoot) {
|
|
225
|
+
return (`${formatCoverageDebtStatusLine(resolveCoverageDebt(projectRoot))}\n` +
|
|
226
|
+
`${formatCheckResumeStatusLine(resolveCheckResume(projectRoot))}`);
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=coverage-check-resume-presets.js.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coverage-debt hatch policy (#3189).
|
|
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.
|
|
10
|
+
*/
|
|
11
|
+
/** Canonical registered policy field name. */
|
|
12
|
+
export declare const FIELD_COVERAGE_DEBT = "plan.policy.coverageDebt";
|
|
13
|
+
/** Short alias for `policy:show --field=coverageDebt`. */
|
|
14
|
+
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
|
+
export declare const COVERAGE_DEBT_MODES: readonly ["off", "warn", "hatch"];
|
|
18
|
+
export type CoverageDebtMode = (typeof COVERAGE_DEBT_MODES)[number];
|
|
19
|
+
/** Fail-closed effective mode when unset / missing / invalid. */
|
|
20
|
+
export declare const DEFAULT_COVERAGE_DEBT_MODE: CoverageDebtMode;
|
|
21
|
+
/** Consumers default autoFile false even under hatch mode. */
|
|
22
|
+
export declare const DEFAULT_COVERAGE_DEBT_AUTO_FILE = false;
|
|
23
|
+
export interface CoverageDebtConfig {
|
|
24
|
+
readonly status: CoverageDebtStatus;
|
|
25
|
+
readonly mode: CoverageDebtMode;
|
|
26
|
+
readonly autoFile: boolean;
|
|
27
|
+
/** Present when decided via dismiss-with-reason (still visible to show/doctor). */
|
|
28
|
+
readonly dismissReason: string | null;
|
|
29
|
+
}
|
|
30
|
+
export type CoverageDebtSource = "typed" | "default" | "default-on-error";
|
|
31
|
+
export interface CoverageDebtResolved extends CoverageDebtConfig {
|
|
32
|
+
readonly source: CoverageDebtSource;
|
|
33
|
+
readonly error: string | null;
|
|
34
|
+
}
|
|
35
|
+
/** Validate a `plan.policy.coverageDebt` payload. */
|
|
36
|
+
export declare function validateCoverageDebt(value: unknown): string[];
|
|
37
|
+
/**
|
|
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.
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveCoverageDebtFromTypedBlock(raw: unknown): CoverageDebtResolved;
|
|
42
|
+
/** Resolve `plan.policy.coverageDebt` from PROJECT-DEFINITION (#3189). */
|
|
43
|
+
export declare function resolveCoverageDebt(projectRoot: string): CoverageDebtResolved;
|
|
44
|
+
/** Effective hatch soft-pass allowed only when decided mode=hatch. */
|
|
45
|
+
export declare function isCoverageDebtHatchAllowed(policy: CoverageDebtResolved): boolean;
|
|
46
|
+
/** Auto-file debt issues only when hatch is allowed AND autoFile true. */
|
|
47
|
+
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
|
+
export declare function formatCoverageDebtStatusLine(policy: CoverageDebtResolved): string;
|
|
54
|
+
export interface CoverageDebtPolicyField {
|
|
55
|
+
readonly name: typeof FIELD_COVERAGE_DEBT;
|
|
56
|
+
readonly current: CoverageDebtConfig;
|
|
57
|
+
readonly default: CoverageDebtConfig;
|
|
58
|
+
readonly source: string;
|
|
59
|
+
}
|
|
60
|
+
/** Inspector row for `policy:show --field=coverageDebt`. */
|
|
61
|
+
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;
|
|
76
|
+
//# sourceMappingURL=coverage-debt.d.ts.map
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coverage-debt hatch policy (#3189).
|
|
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.
|
|
10
|
+
*/
|
|
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";
|
|
16
|
+
/** Canonical registered policy field name. */
|
|
17
|
+
export const FIELD_COVERAGE_DEBT = "plan.policy.coverageDebt";
|
|
18
|
+
/** Short alias for `policy:show --field=coverageDebt`. */
|
|
19
|
+
export const FIELD_COVERAGE_DEBT_CLI_ALIAS = "coverageDebt";
|
|
20
|
+
export const COVERAGE_DEBT_STATUSES = ["unset", "decided"];
|
|
21
|
+
export const COVERAGE_DEBT_MODES = ["off", "warn", "hatch"];
|
|
22
|
+
/** Fail-closed effective mode when unset / missing / invalid. */
|
|
23
|
+
export const DEFAULT_COVERAGE_DEBT_MODE = "off";
|
|
24
|
+
/** Consumers default autoFile false even under hatch mode. */
|
|
25
|
+
export const DEFAULT_COVERAGE_DEBT_AUTO_FILE = false;
|
|
26
|
+
function defaultResolved(source, error = null) {
|
|
27
|
+
return {
|
|
28
|
+
status: "unset",
|
|
29
|
+
mode: DEFAULT_COVERAGE_DEBT_MODE,
|
|
30
|
+
autoFile: DEFAULT_COVERAGE_DEBT_AUTO_FILE,
|
|
31
|
+
dismissReason: null,
|
|
32
|
+
source,
|
|
33
|
+
error,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function isMode(value) {
|
|
37
|
+
return typeof value === "string" && COVERAGE_DEBT_MODES.includes(value);
|
|
38
|
+
}
|
|
39
|
+
function isStatus(value) {
|
|
40
|
+
return typeof value === "string" && COVERAGE_DEBT_STATUSES.includes(value);
|
|
41
|
+
}
|
|
42
|
+
/** Validate a `plan.policy.coverageDebt` payload. */
|
|
43
|
+
export function validateCoverageDebt(value) {
|
|
44
|
+
if (value === null || value === undefined) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
48
|
+
return [`${FIELD_COVERAGE_DEBT} must be an object; got ${typeof value}`];
|
|
49
|
+
}
|
|
50
|
+
const rec = value;
|
|
51
|
+
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
|
+
if ("mode" in rec && !isMode(rec.mode)) {
|
|
56
|
+
errors.push(`${FIELD_COVERAGE_DEBT}.mode must be one of ${COVERAGE_DEBT_MODES.join("|")}`);
|
|
57
|
+
}
|
|
58
|
+
if ("autoFile" in rec && typeof rec.autoFile !== "boolean") {
|
|
59
|
+
errors.push(`${FIELD_COVERAGE_DEBT}.autoFile must be a boolean`);
|
|
60
|
+
}
|
|
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
|
+
return errors;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
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.
|
|
71
|
+
*/
|
|
72
|
+
export function resolveCoverageDebtFromTypedBlock(raw) {
|
|
73
|
+
const errors = validateCoverageDebt(raw);
|
|
74
|
+
if (errors.length > 0) {
|
|
75
|
+
return defaultResolved("default-on-error", errors[0] ?? "invalid coverageDebt block");
|
|
76
|
+
}
|
|
77
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
78
|
+
return defaultResolved("default");
|
|
79
|
+
}
|
|
80
|
+
const block = raw;
|
|
81
|
+
const status = isStatus(block.status) ? block.status : "unset";
|
|
82
|
+
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
|
+
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;
|
|
91
|
+
return {
|
|
92
|
+
status,
|
|
93
|
+
mode: status === "decided" ? mode : DEFAULT_COVERAGE_DEBT_MODE,
|
|
94
|
+
autoFile,
|
|
95
|
+
dismissReason: status === "decided" ? dismissReason : null,
|
|
96
|
+
source: "typed",
|
|
97
|
+
error: null,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/** Resolve `plan.policy.coverageDebt` from PROJECT-DEFINITION (#3189). */
|
|
101
|
+
export function resolveCoverageDebt(projectRoot) {
|
|
102
|
+
const [data, err] = loadProjectDefinition(projectRoot);
|
|
103
|
+
if (data === null) {
|
|
104
|
+
return defaultResolved("default-on-error", err);
|
|
105
|
+
}
|
|
106
|
+
const policyBlock = readPlanPolicy(data.plan);
|
|
107
|
+
if (typeof policyBlock !== "object" ||
|
|
108
|
+
policyBlock === null ||
|
|
109
|
+
Array.isArray(policyBlock) ||
|
|
110
|
+
!("coverageDebt" in policyBlock)) {
|
|
111
|
+
return defaultResolved("default");
|
|
112
|
+
}
|
|
113
|
+
return resolveCoverageDebtFromTypedBlock(policyBlock.coverageDebt);
|
|
114
|
+
}
|
|
115
|
+
/** Effective hatch soft-pass allowed only when decided mode=hatch. */
|
|
116
|
+
export function isCoverageDebtHatchAllowed(policy) {
|
|
117
|
+
return policy.status === "decided" && policy.mode === "hatch";
|
|
118
|
+
}
|
|
119
|
+
/** Auto-file debt issues only when hatch is allowed AND autoFile true. */
|
|
120
|
+
export function isCoverageDebtAutoFileAllowed(policy) {
|
|
121
|
+
return isCoverageDebtHatchAllowed(policy) && policy.autoFile === true;
|
|
122
|
+
}
|
|
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
|
+
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}).`);
|
|
134
|
+
}
|
|
135
|
+
function fieldFromResolved(resolved) {
|
|
136
|
+
return {
|
|
137
|
+
name: FIELD_COVERAGE_DEBT,
|
|
138
|
+
current: {
|
|
139
|
+
status: resolved.status,
|
|
140
|
+
mode: resolved.mode,
|
|
141
|
+
autoFile: resolved.autoFile,
|
|
142
|
+
dismissReason: resolved.dismissReason,
|
|
143
|
+
},
|
|
144
|
+
default: {
|
|
145
|
+
status: "unset",
|
|
146
|
+
mode: DEFAULT_COVERAGE_DEBT_MODE,
|
|
147
|
+
autoFile: DEFAULT_COVERAGE_DEBT_AUTO_FILE,
|
|
148
|
+
dismissReason: null,
|
|
149
|
+
},
|
|
150
|
+
source: resolved.source,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/** Inspector row for `policy:show --field=coverageDebt`. */
|
|
154
|
+
export function inspectCoverageDebt(data, projectRoot) {
|
|
155
|
+
if (data === null) {
|
|
156
|
+
return fieldFromResolved(defaultResolved("default"));
|
|
157
|
+
}
|
|
158
|
+
const policyBlock = readPlanPolicy(data.plan);
|
|
159
|
+
if (typeof policyBlock !== "object" ||
|
|
160
|
+
policyBlock === null ||
|
|
161
|
+
Array.isArray(policyBlock) ||
|
|
162
|
+
!("coverageDebt" in policyBlock)) {
|
|
163
|
+
if (projectRoot !== undefined) {
|
|
164
|
+
return fieldFromResolved(resolveCoverageDebt(projectRoot));
|
|
165
|
+
}
|
|
166
|
+
return fieldFromResolved(defaultResolved("default"));
|
|
167
|
+
}
|
|
168
|
+
return fieldFromResolved(resolveCoverageDebtFromTypedBlock(policyBlock.coverageDebt));
|
|
169
|
+
}
|
|
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 };
|
|
247
|
+
}
|
|
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 };
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=coverage-debt.js.map
|
package/dist/policy/index.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export * from "./agents-md-advisory.js";
|
|
2
2
|
export * from "./autonomy.js";
|
|
3
3
|
export * from "./capacity.js";
|
|
4
|
+
export * from "./check-resume.js";
|
|
5
|
+
export * from "./coverage-check-resume-presets.js";
|
|
6
|
+
export * from "./coverage-debt.js";
|
|
4
7
|
export * from "./decisions.js";
|
|
5
8
|
export * from "./deft-directive-disable.js";
|
|
6
9
|
export * from "./delivery-branch.js";
|