@deftai/directive-core 0.76.0 → 0.78.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/agents-md-budget/evaluate.d.ts +8 -1
- package/dist/agents-md-budget/evaluate.js +54 -19
- package/dist/content-contracts/skills/skill-frontmatter.js +4 -0
- package/dist/doctor/constants.js +3 -3
- package/dist/doctor/index.d.ts +1 -0
- package/dist/doctor/index.js +1 -0
- package/dist/doctor/main.d.ts +1 -0
- package/dist/doctor/main.js +39 -1
- package/dist/doctor/payload-staleness.js +62 -63
- package/dist/doctor/release-availability.d.ts +28 -0
- package/dist/doctor/release-availability.js +35 -0
- package/dist/doctor/types.d.ts +3 -0
- package/dist/eval/crud-telemetry.d.ts +5 -4
- package/dist/eval/crud-telemetry.js +9 -5
- package/dist/eval/health.d.ts +5 -4
- package/dist/eval/health.js +24 -16
- package/dist/eval/readback.js +2 -8
- package/dist/eval/triggers.d.ts +76 -0
- package/dist/eval/triggers.js +259 -0
- package/dist/eval-triggers-relocation/evaluate.d.ts +36 -0
- package/dist/eval-triggers-relocation/evaluate.js +115 -0
- package/dist/eval-triggers-relocation/index.d.ts +2 -0
- package/dist/eval-triggers-relocation/index.js +2 -0
- package/dist/hooks/dispatcher.d.ts +43 -0
- package/dist/hooks/dispatcher.js +171 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.js +3 -0
- package/dist/hooks/scope.d.ts +12 -0
- package/dist/hooks/scope.js +46 -0
- package/dist/hooks/tools.d.ts +4 -0
- package/dist/hooks/tools.js +23 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/init-deposit/agent-hooks.d.ts +22 -0
- package/dist/init-deposit/agent-hooks.js +228 -0
- package/dist/init-deposit/hygiene.js +3 -0
- package/dist/init-deposit/index.d.ts +1 -0
- package/dist/init-deposit/index.js +1 -0
- package/dist/init-deposit/init-deposit.js +2 -0
- package/dist/init-deposit/refresh.js +2 -0
- package/dist/intake/reconcile-issues.d.ts +1 -0
- package/dist/intake/reconcile-issues.js +51 -49
- package/dist/metrics/index.d.ts +2 -0
- package/dist/metrics/index.js +2 -0
- package/dist/metrics/resolve-metrics-home.d.ts +50 -0
- package/dist/metrics/resolve-metrics-home.js +125 -0
- package/dist/plan-sequence/index.d.ts +3 -0
- package/dist/plan-sequence/index.js +3 -0
- package/dist/plan-sequence/store.d.ts +6 -0
- package/dist/plan-sequence/store.js +29 -0
- package/dist/plan-sequence/types.d.ts +79 -0
- package/dist/plan-sequence/types.js +220 -0
- package/dist/release/build-dist.js +1 -0
- package/dist/release/version.d.ts +2 -0
- package/dist/release/version.js +42 -0
- package/dist/release-e2e/greenfield-python-free-smoke-cli.js +40 -1
- package/dist/release-e2e/greenfield-python-free-smoke.d.ts +2 -0
- package/dist/release-e2e/greenfield-python-free-smoke.js +28 -8
- package/dist/release-e2e/npm-ops.js +20 -6
- package/dist/session/verify-session-ritual.d.ts +16 -0
- package/dist/session/verify-session-ritual.js +63 -0
- package/dist/triage/help/registry-data.d.ts +12 -1
- package/dist/triage/help/registry-data.js +22 -1
- package/dist/verify-env/agent-hooks.d.ts +11 -0
- package/dist/verify-env/agent-hooks.js +45 -0
- package/dist/verify-env/command-spawn.d.ts +29 -0
- package/dist/verify-env/command-spawn.js +97 -0
- package/dist/verify-env/index.d.ts +2 -0
- package/dist/verify-env/index.js +2 -0
- package/package.json +19 -3
|
@@ -4,7 +4,11 @@ export type OutputStream = "stdout" | "stderr" | "none";
|
|
|
4
4
|
/**
|
|
5
5
|
* Layered absolute north-star for the always-on managed surface (#2372 / #2450 / #2452).
|
|
6
6
|
*
|
|
7
|
-
* `ABSOLUTE_MANAGED_MAX_BYTES` is the relocation north-star (<=8192 B / ~2k tok)
|
|
7
|
+
* `ABSOLUTE_MANAGED_MAX_BYTES` is the managed-section relocation north-star (<=8192 B / ~2k tok)
|
|
8
|
+
* — Phase-2 success bar. `COMBINED_ALWAYS_ON_MAX_BYTES` is the Phase-3 combined always-on
|
|
9
|
+
* north-star (managed + DD-3 + hooks), raised to 9 KB after diminishing returns on further
|
|
10
|
+
* thinning (#2531 closeout).
|
|
11
|
+
*
|
|
8
12
|
* When `plan.policy.agentsMdBudget.absoluteMaxBytes` is set, growth past that seeded
|
|
9
13
|
* ratchet fails closed; distance-to-north-star is always reported. Optional release-gate
|
|
10
14
|
* north-star enforcement: DEFT_AGENTS_MD_BUDGET_ENFORCE_NORTH_STAR=1 (waiver:
|
|
@@ -17,6 +21,9 @@ export type OutputStream = "stdout" | "stderr" | "none";
|
|
|
17
21
|
*/
|
|
18
22
|
export declare const ABSOLUTE_MANAGED_MAX_BYTES = 8192;
|
|
19
23
|
export declare const ABSOLUTE_MANAGED_MAX_TOKENS = 2000;
|
|
24
|
+
/** Phase-3 combined always-on north-star (managed + DD-3 + hooks) — 9 KB (#2531). */
|
|
25
|
+
export declare const COMBINED_ALWAYS_ON_MAX_BYTES = 9216;
|
|
26
|
+
export declare const COMBINED_ALWAYS_ON_MAX_TOKENS = 2304;
|
|
20
27
|
/** Bootstrap host hooks are 0 B until #2438 ships. */
|
|
21
28
|
export declare const BOOTSTRAP_HOOK_BYTES = 0;
|
|
22
29
|
/** Rough UTF-8 bytes-per-token estimate for advisory reporting (~8192 B ≈ ~2048 tok). */
|
|
@@ -7,7 +7,11 @@ import { measureSkillFrontmatter } from "./skill-frontmatter.js";
|
|
|
7
7
|
/**
|
|
8
8
|
* Layered absolute north-star for the always-on managed surface (#2372 / #2450 / #2452).
|
|
9
9
|
*
|
|
10
|
-
* `ABSOLUTE_MANAGED_MAX_BYTES` is the relocation north-star (<=8192 B / ~2k tok)
|
|
10
|
+
* `ABSOLUTE_MANAGED_MAX_BYTES` is the managed-section relocation north-star (<=8192 B / ~2k tok)
|
|
11
|
+
* — Phase-2 success bar. `COMBINED_ALWAYS_ON_MAX_BYTES` is the Phase-3 combined always-on
|
|
12
|
+
* north-star (managed + DD-3 + hooks), raised to 9 KB after diminishing returns on further
|
|
13
|
+
* thinning (#2531 closeout).
|
|
14
|
+
*
|
|
11
15
|
* When `plan.policy.agentsMdBudget.absoluteMaxBytes` is set, growth past that seeded
|
|
12
16
|
* ratchet fails closed; distance-to-north-star is always reported. Optional release-gate
|
|
13
17
|
* north-star enforcement: DEFT_AGENTS_MD_BUDGET_ENFORCE_NORTH_STAR=1 (waiver:
|
|
@@ -20,6 +24,9 @@ import { measureSkillFrontmatter } from "./skill-frontmatter.js";
|
|
|
20
24
|
*/
|
|
21
25
|
export const ABSOLUTE_MANAGED_MAX_BYTES = 8192;
|
|
22
26
|
export const ABSOLUTE_MANAGED_MAX_TOKENS = 2000;
|
|
27
|
+
/** Phase-3 combined always-on north-star (managed + DD-3 + hooks) — 9 KB (#2531). */
|
|
28
|
+
export const COMBINED_ALWAYS_ON_MAX_BYTES = 9216;
|
|
29
|
+
export const COMBINED_ALWAYS_ON_MAX_TOKENS = 2304;
|
|
23
30
|
/** Bootstrap host hooks are 0 B until #2438 ships. */
|
|
24
31
|
export const BOOTSTRAP_HOOK_BYTES = 0;
|
|
25
32
|
/** Rough UTF-8 bytes-per-token estimate for advisory reporting (~8192 B ≈ ~2048 tok). */
|
|
@@ -186,7 +193,7 @@ function formatNorthStarOverage(measure) {
|
|
|
186
193
|
return formatNorthStarOverageBytes(measure.bytes - ABSOLUTE_MANAGED_MAX_BYTES, measure.estimatedTokens - ABSOLUTE_MANAGED_MAX_TOKENS);
|
|
187
194
|
}
|
|
188
195
|
function formatCombinedNorthStarOverage(bootstrap) {
|
|
189
|
-
return formatNorthStarOverageBytes(bootstrap.totalBytes -
|
|
196
|
+
return formatNorthStarOverageBytes(bootstrap.totalBytes - COMBINED_ALWAYS_ON_MAX_BYTES, bootstrap.totalEstimatedTokens - COMBINED_ALWAYS_ON_MAX_TOKENS);
|
|
190
197
|
}
|
|
191
198
|
function formatNorthStarDistance(measure) {
|
|
192
199
|
const overBytes = measure.bytes - ABSOLUTE_MANAGED_MAX_BYTES;
|
|
@@ -200,14 +207,14 @@ function formatNorthStarDistance(measure) {
|
|
|
200
207
|
`${formatNorthStarOverage(measure)}).`);
|
|
201
208
|
}
|
|
202
209
|
function formatCombinedNorthStarDistance(bootstrap) {
|
|
203
|
-
const overBytes = bootstrap.totalBytes -
|
|
204
|
-
const overTokens = bootstrap.totalEstimatedTokens -
|
|
210
|
+
const overBytes = bootstrap.totalBytes - COMBINED_ALWAYS_ON_MAX_BYTES;
|
|
211
|
+
const overTokens = bootstrap.totalEstimatedTokens - COMBINED_ALWAYS_ON_MAX_TOKENS;
|
|
205
212
|
if (overBytes <= 0 && overTokens <= 0) {
|
|
206
213
|
return (`north-star: combined always-on ${bootstrap.totalBytes} bytes ` +
|
|
207
214
|
`(~${bootstrap.totalEstimatedTokens} tok) within ` +
|
|
208
|
-
`≤${
|
|
215
|
+
`≤${COMBINED_ALWAYS_ON_MAX_BYTES} B / ~${COMBINED_ALWAYS_ON_MAX_TOKENS} tok target.`);
|
|
209
216
|
}
|
|
210
|
-
return (`north-star: combined always-on ≤${
|
|
217
|
+
return (`north-star: combined always-on ≤${COMBINED_ALWAYS_ON_MAX_BYTES} B / ~${COMBINED_ALWAYS_ON_MAX_TOKENS} tok ` +
|
|
211
218
|
`(current ${bootstrap.totalBytes} bytes / ~${bootstrap.totalEstimatedTokens} tok — ` +
|
|
212
219
|
`${formatCombinedNorthStarOverage(bootstrap)}).`);
|
|
213
220
|
}
|
|
@@ -224,12 +231,24 @@ function formatBootstrapItemization(bootstrap) {
|
|
|
224
231
|
}
|
|
225
232
|
function formatAbsoluteAdvisory(bootstrap) {
|
|
226
233
|
const measure = bootstrap.managed;
|
|
234
|
+
const overManagedBytes = measure.bytes > ABSOLUTE_MANAGED_MAX_BYTES;
|
|
235
|
+
const overManagedTokens = measure.estimatedTokens > ABSOLUTE_MANAGED_MAX_TOKENS;
|
|
236
|
+
const overCombinedBytes = bootstrap.totalBytes > COMBINED_ALWAYS_ON_MAX_BYTES;
|
|
237
|
+
const overCombinedTokens = bootstrap.totalEstimatedTokens > COMBINED_ALWAYS_ON_MAX_TOKENS;
|
|
238
|
+
const violationLines = [];
|
|
239
|
+
if (overManagedBytes || overManagedTokens) {
|
|
240
|
+
violationLines.push(` Managed section alone: ${measure.bytes} bytes (~${measure.estimatedTokens} tok) exceeds the north-star ` +
|
|
241
|
+
`of ${ABSOLUTE_MANAGED_MAX_BYTES} bytes / ~${ABSOLUTE_MANAGED_MAX_TOKENS} tok ` +
|
|
242
|
+
`(OVER: ${formatNorthStarOverage(measure)}).`);
|
|
243
|
+
}
|
|
244
|
+
if (overCombinedBytes || overCombinedTokens) {
|
|
245
|
+
violationLines.push(` Combined always-on: ${bootstrap.totalBytes} bytes (~${bootstrap.totalEstimatedTokens} tok) exceeds the north-star ` +
|
|
246
|
+
`of ${COMBINED_ALWAYS_ON_MAX_BYTES} bytes / ~${COMBINED_ALWAYS_ON_MAX_TOKENS} tok ` +
|
|
247
|
+
`(OVER: ${formatCombinedNorthStarOverage(bootstrap)}).`);
|
|
248
|
+
}
|
|
227
249
|
return (`⚠ verify:agents-md-budget: always-on bootstrap advisory — ` +
|
|
228
250
|
`${formatBootstrapItemization(bootstrap)}.\n` +
|
|
229
|
-
|
|
230
|
-
`of ${ABSOLUTE_MANAGED_MAX_BYTES} bytes / ~${ABSOLUTE_MANAGED_MAX_TOKENS} tok ` +
|
|
231
|
-
`(OVER: ${formatNorthStarOverage(measure)}).\n` +
|
|
232
|
-
` ${formatCombinedNorthStarDistance(bootstrap)}\n` +
|
|
251
|
+
`${violationLines.join("\n")}\n` +
|
|
233
252
|
" Advisory only — set plan.policy.agentsMdBudget.absoluteMaxBytes to enable " +
|
|
234
253
|
"fail-closed managed growth ratchet (#2452).\n" +
|
|
235
254
|
" Optional DD-3 ratchet: plan.policy.agentsMdBudget.skillFrontmatterMaxBytes (#2463).\n" +
|
|
@@ -268,13 +287,29 @@ function formatSkillFrontmatterRefusal(bootstrap, skillFrontmatterMaxBytes, proj
|
|
|
268
287
|
}
|
|
269
288
|
function formatNorthStarRefusal(bootstrap, projectRoot) {
|
|
270
289
|
const measure = bootstrap.managed;
|
|
271
|
-
const
|
|
272
|
-
|
|
290
|
+
const overNorthStarManaged = measure.bytes > ABSOLUTE_MANAGED_MAX_BYTES ||
|
|
291
|
+
measure.estimatedTokens > ABSOLUTE_MANAGED_MAX_TOKENS;
|
|
292
|
+
const overNorthStarCombined = bootstrap.totalBytes > COMBINED_ALWAYS_ON_MAX_BYTES ||
|
|
293
|
+
bootstrap.totalEstimatedTokens > COMBINED_ALWAYS_ON_MAX_TOKENS;
|
|
294
|
+
const headline = overNorthStarManaged && overNorthStarCombined
|
|
295
|
+
? "managed and combined always-on surfaces exceed the north-star ceiling"
|
|
296
|
+
: overNorthStarManaged
|
|
297
|
+
? "managed section exceeds the north-star ceiling"
|
|
298
|
+
: "combined always-on surface exceeds the north-star ceiling";
|
|
299
|
+
const detailLines = [];
|
|
300
|
+
if (overNorthStarManaged) {
|
|
301
|
+
detailLines.push(` managed section: ${measure.bytes}/${ABSOLUTE_MANAGED_MAX_BYTES} bytes ` +
|
|
302
|
+
`(OVER: ${formatNorthStarOverage(measure)})`);
|
|
303
|
+
}
|
|
304
|
+
if (overNorthStarCombined) {
|
|
305
|
+
detailLines.push(` combined always-on: ${bootstrap.totalBytes}/${COMBINED_ALWAYS_ON_MAX_BYTES} bytes ` +
|
|
306
|
+
`(OVER: ${formatCombinedNorthStarOverage(bootstrap)})`);
|
|
307
|
+
}
|
|
308
|
+
return (`❌ verify:agents-md-budget: ${headline} ` +
|
|
273
309
|
`(project_root=${projectRoot}, release-gate mode).\n` +
|
|
274
310
|
` ${formatBootstrapItemization(bootstrap)}\n` +
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
" Thin the managed section and/or tier DD-3 skills toward the <=8192 B target,\n" +
|
|
311
|
+
`${detailLines.join("\n")}\n` +
|
|
312
|
+
" Thin the managed section and/or tier DD-3 skills toward the combined ≤9 KB target,\n" +
|
|
278
313
|
" or set DEFT_ALLOW_ABSOLUTE_BUDGET_WAIVER=1 for a time-boxed operator waiver (#2452).");
|
|
279
314
|
}
|
|
280
315
|
function enforceNorthStarEnabled() {
|
|
@@ -287,8 +322,8 @@ function attachNorthStarNote(result, bootstrap, options) {
|
|
|
287
322
|
const measure = bootstrap.managed;
|
|
288
323
|
const overManagedBytes = measure.bytes > ABSOLUTE_MANAGED_MAX_BYTES;
|
|
289
324
|
const overManagedTokens = measure.estimatedTokens > ABSOLUTE_MANAGED_MAX_TOKENS;
|
|
290
|
-
const overCombinedBytes = bootstrap.totalBytes >
|
|
291
|
-
const overCombinedTokens = bootstrap.totalEstimatedTokens >
|
|
325
|
+
const overCombinedBytes = bootstrap.totalBytes > COMBINED_ALWAYS_ON_MAX_BYTES;
|
|
326
|
+
const overCombinedTokens = bootstrap.totalEstimatedTokens > COMBINED_ALWAYS_ON_MAX_TOKENS;
|
|
292
327
|
if (options.advisoryOnly) {
|
|
293
328
|
if (!overManagedBytes && !overManagedTokens && !overCombinedBytes && !overCombinedTokens) {
|
|
294
329
|
return result;
|
|
@@ -445,8 +480,8 @@ export function evaluate(projectRoot, options = {}) {
|
|
|
445
480
|
}
|
|
446
481
|
const overNorthStarManaged = measure.bytes > ABSOLUTE_MANAGED_MAX_BYTES ||
|
|
447
482
|
measure.estimatedTokens > ABSOLUTE_MANAGED_MAX_TOKENS;
|
|
448
|
-
const overNorthStarCombined = bootstrap.totalBytes >
|
|
449
|
-
bootstrap.totalEstimatedTokens >
|
|
483
|
+
const overNorthStarCombined = bootstrap.totalBytes > COMBINED_ALWAYS_ON_MAX_BYTES ||
|
|
484
|
+
bootstrap.totalEstimatedTokens > COMBINED_ALWAYS_ON_MAX_TOKENS;
|
|
450
485
|
if (enforceNorthStarEnabled() &&
|
|
451
486
|
(overNorthStarManaged || overNorthStarCombined) &&
|
|
452
487
|
!northStarWaiverActive()) {
|
package/dist/doctor/constants.js
CHANGED
|
@@ -29,13 +29,13 @@ export const DOCTOR_ALLOWED_FLAGS = [
|
|
|
29
29
|
/** npm consumer deposit after #2022 Phase 3 -- Python scripts/ tree is intentionally absent. */
|
|
30
30
|
export const NPM_PACKAGE_NAME = "@deftai/directive";
|
|
31
31
|
// #2182: payload-staleness is the only doctor check that can reach a network
|
|
32
|
-
// endpoint (git ls-remote
|
|
33
|
-
//
|
|
32
|
+
// endpoint (git ls-remote verifies the pinned ref and `npm view` compares a
|
|
33
|
+
// release-tag install with the latest stable package). It is OFF by default (offline tier)
|
|
34
34
|
// and requires the explicit `--network` flag; this line discloses exactly
|
|
35
35
|
// which tool + registry class it may contact BEFORE the check runs, and the
|
|
36
36
|
// skip line tells an offline run how to opt in.
|
|
37
37
|
export const NETWORK_DISCLOSURE_LINE = "[deft doctor] --network: this run may contact your git remote (framework " +
|
|
38
|
-
"repo host) and
|
|
38
|
+
"repo host) and the npm registry (registry.npmjs.org) to " +
|
|
39
39
|
`look up the latest ${NPM_PACKAGE_NAME} version.`;
|
|
40
40
|
export const PAYLOAD_STALENESS_OFFLINE_SKIP_MESSAGE = "skip -- offline tier (default). Run `deft doctor --network` to check " +
|
|
41
41
|
"framework currency against your git remote and the npm registry " +
|
package/dist/doctor/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from "./main.js";
|
|
|
8
8
|
export * from "./manifest.js";
|
|
9
9
|
export * from "./paths.js";
|
|
10
10
|
export * from "./payload-staleness.js";
|
|
11
|
+
export * from "./release-availability.js";
|
|
11
12
|
export * from "./taskfile.js";
|
|
12
13
|
export * from "./types.js";
|
|
13
14
|
export * from "./which.js";
|
package/dist/doctor/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export * from "./main.js";
|
|
|
8
8
|
export * from "./manifest.js";
|
|
9
9
|
export * from "./paths.js";
|
|
10
10
|
export * from "./payload-staleness.js";
|
|
11
|
+
export * from "./release-availability.js";
|
|
11
12
|
export * from "./taskfile.js";
|
|
12
13
|
export * from "./types.js";
|
|
13
14
|
export * from "./which.js";
|
package/dist/doctor/main.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type ResolutionFacts } from "../resolution/index.js";
|
|
|
2
2
|
import { createPlainSink } from "./output.js";
|
|
3
3
|
import type { DoctorSeams, Finding, ResolutionSummary } from "./types.js";
|
|
4
4
|
export declare function cmdDoctor(args: readonly string[], seams?: DoctorSeams): number;
|
|
5
|
+
export declare function runAgentHooksHealthCheck(projectRoot: string, consumerContext: boolean, sink: ReturnType<typeof createPlainSink>, addFinding: (finding: Finding) => void, seams: DoctorSeams): void;
|
|
5
6
|
/**
|
|
6
7
|
* Never emit a bare `task ...` remediation in a project without Taskfile wiring
|
|
7
8
|
* (#2267). The `directive` surface always works; `task deft:X` is optional and
|
package/dist/doctor/main.js
CHANGED
|
@@ -6,6 +6,7 @@ import { describeShadowedPlanExtension, detectShadowedPlanExtensions, } from "..
|
|
|
6
6
|
import { loadProjectDefinition } from "../policy/resolve.js";
|
|
7
7
|
import { checkLocalEngineIntegrity, classify, detectPackageManager, evaluateSkew, reconcileVersions, plan as resolvePlan, } from "../resolution/index.js";
|
|
8
8
|
import { resolveUserMdPath } from "../user-config/resolve-user-md.js";
|
|
9
|
+
import { evaluateAgentHooks } from "../verify-env/agent-hooks.js";
|
|
9
10
|
import { agentsRefreshPlan, hasV3ManagedMarker } from "./agents-md.js";
|
|
10
11
|
import { runChecks } from "./checks.js";
|
|
11
12
|
import { CONSUMER_FRAMEWORK_DIRS, EXPECTED_CONTENT_DIRS, EXPECTED_FRAMEWORK_DIRS, NETWORK_DISCLOSURE_LINE, PAYLOAD_STALENESS_OFFLINE_SKIP_MESSAGE, TASKFILE_INCLUDE_SNIPPET, UV_INSTALL_URL, } from "./constants.js";
|
|
@@ -162,8 +163,14 @@ export function cmdDoctor(args, seams = {}) {
|
|
|
162
163
|
if (!jsonMode) {
|
|
163
164
|
sink.blank();
|
|
164
165
|
}
|
|
166
|
+
sink.info("Checking agent-host hook registration...");
|
|
167
|
+
runAgentHooksHealthCheck(projectRoot, consumerContext, sink, addFinding, seams);
|
|
168
|
+
if (!jsonMode) {
|
|
169
|
+
sink.blank();
|
|
170
|
+
}
|
|
165
171
|
// #2182: payload-staleness is the only doctor check that can reach a
|
|
166
|
-
// registry (git
|
|
172
|
+
// registry (git verifies the pin; npm compares stable release availability).
|
|
173
|
+
// It stays in the OFFLINE
|
|
167
174
|
// tier (skipped) unless the operator explicitly opts into the NETWORK tier
|
|
168
175
|
// via `--network`, and the tool + registry class is disclosed BEFORE the
|
|
169
176
|
// check runs -- never silently, never as a side effect of a read-only
|
|
@@ -331,6 +338,37 @@ export function cmdDoctor(args, seams = {}) {
|
|
|
331
338
|
sink.finalWarn(`System check completed with ${warningCount} warning(s).`);
|
|
332
339
|
return 0;
|
|
333
340
|
}
|
|
341
|
+
export function runAgentHooksHealthCheck(projectRoot, consumerContext, sink, addFinding, seams) {
|
|
342
|
+
const checkName = "agent-hooks-registration";
|
|
343
|
+
if (!consumerContext) {
|
|
344
|
+
const reason = "maintainer source checkout; project hook deposit is consumer-only";
|
|
345
|
+
sink.info(`${checkName}: skip -- ${reason}`);
|
|
346
|
+
addFinding({ severity: "skip", message: reason, check: checkName, status: "skip" });
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
try {
|
|
350
|
+
const result = (seams.evaluateAgentHooks ?? evaluateAgentHooks)(projectRoot);
|
|
351
|
+
if (result.code === 0) {
|
|
352
|
+
sink.success(`${checkName}: healthy`);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
const message = `${checkName}: ${result.message.replace(/\s+/g, " ").trim()}`;
|
|
356
|
+
sink.warn(message);
|
|
357
|
+
addFinding({
|
|
358
|
+
severity: "warning",
|
|
359
|
+
message,
|
|
360
|
+
check: checkName,
|
|
361
|
+
status: result.code === 2 ? "unavailable" : "non-functional",
|
|
362
|
+
registrations: result.registrations,
|
|
363
|
+
suggestion: "deft update",
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
catch (cause) {
|
|
367
|
+
const message = `${checkName}: probe failed -- ${String(cause)}`;
|
|
368
|
+
sink.warn(message);
|
|
369
|
+
addFinding({ severity: "warning", message, check: checkName, suggestion: "deft update" });
|
|
370
|
+
}
|
|
371
|
+
}
|
|
334
372
|
function runInstallIntegrityChecks(projectRoot, sink, addFinding, seams) {
|
|
335
373
|
if (runningInsideDeftRepo(projectRoot, seams)) {
|
|
336
374
|
sink.info("Skipping install-integrity checks -- running inside the deft framework repo (no install manifest in the source checkout).");
|
|
@@ -4,6 +4,7 @@ import { detectCanonicalVendoredManifest, isNpmManaged } from "../init-deposit/m
|
|
|
4
4
|
import { CANONICAL_UPGRADE_COMMAND, NPM_PACKAGE_NAME, upgradeCommandFor, VENDORED_NPM_DEPOSIT_UPGRADE_COMMAND, } from "./constants.js";
|
|
5
5
|
import { locateManifest, parseInstallManifest } from "./manifest.js";
|
|
6
6
|
import { readTextSafe, resolveDefaultFrameworkRoot } from "./paths.js";
|
|
7
|
+
import { evaluateReleaseAvailability } from "./release-availability.js";
|
|
7
8
|
function isDeftFrameworkRepo(projectRoot, readText = readTextSafe) {
|
|
8
9
|
try {
|
|
9
10
|
const agents = join(projectRoot, "AGENTS.md");
|
|
@@ -38,42 +39,6 @@ function parseRemoteSha(stdout) {
|
|
|
38
39
|
const firstLine = stdout.split("\n").find((ln) => ln.trim()) ?? "";
|
|
39
40
|
return firstLine.trim().split(/\s+/)[0] ?? "";
|
|
40
41
|
}
|
|
41
|
-
function parseSemver(version) {
|
|
42
|
-
const normalized = version.trim().replace(/^v/i, "");
|
|
43
|
-
const parts = [];
|
|
44
|
-
for (const segment of normalized.split(".")) {
|
|
45
|
-
const numeric = Number.parseInt(segment.split("-")[0] ?? "", 10);
|
|
46
|
-
if (Number.isNaN(numeric)) {
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
49
|
-
parts.push(numeric);
|
|
50
|
-
}
|
|
51
|
-
return parts.length > 0 ? parts : [0];
|
|
52
|
-
}
|
|
53
|
-
function semverLessThan(left, right) {
|
|
54
|
-
const a = parseSemver(left);
|
|
55
|
-
const b = parseSemver(right);
|
|
56
|
-
const len = Math.max(a.length, b.length);
|
|
57
|
-
for (let i = 0; i < len; i += 1) {
|
|
58
|
-
const av = a[i] ?? 0;
|
|
59
|
-
const bv = b[i] ?? 0;
|
|
60
|
-
if (av < bv) {
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
if (av > bv) {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
function manifestVersion(ref, tag) {
|
|
70
|
-
const candidate = (tag || ref).trim().replace(/^refs\/tags\//, "");
|
|
71
|
-
const normalized = candidate.replace(/^v/i, "");
|
|
72
|
-
if (!/^\d+(?:\.\d+)*/.test(normalized)) {
|
|
73
|
-
return "";
|
|
74
|
-
}
|
|
75
|
-
return normalized;
|
|
76
|
-
}
|
|
77
42
|
function defaultNpmViewVersion() {
|
|
78
43
|
const proc = spawnSync("npm", ["view", NPM_PACKAGE_NAME, "version"], {
|
|
79
44
|
encoding: "utf8",
|
|
@@ -82,6 +47,25 @@ function defaultNpmViewVersion() {
|
|
|
82
47
|
const version = (proc.stdout ?? "").trim().split("\n")[0]?.trim() ?? "";
|
|
83
48
|
return { ok: proc.status === 0 && version.length > 0, version };
|
|
84
49
|
}
|
|
50
|
+
function emitReleaseAvailable(checkName, installedVersion, latestVersion, ref, upgradeCommand, sink, addFinding) {
|
|
51
|
+
const msg = `Newer framework release available (installed v${installedVersion}; ` +
|
|
52
|
+
`latest v${latestVersion} from npm registry). ` +
|
|
53
|
+
`Recommendation: run \`${upgradeCommand}\`.`;
|
|
54
|
+
sink.warn(msg);
|
|
55
|
+
addFinding({
|
|
56
|
+
severity: "warning",
|
|
57
|
+
message: msg,
|
|
58
|
+
check: checkName,
|
|
59
|
+
status: "stale",
|
|
60
|
+
staleness_kind: "newer-release",
|
|
61
|
+
ref,
|
|
62
|
+
installed_version: installedVersion,
|
|
63
|
+
latest_version: latestVersion,
|
|
64
|
+
remote_version: latestVersion,
|
|
65
|
+
resolver: "npm-view",
|
|
66
|
+
suggestion: upgradeCommand,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
85
69
|
function emitUnverified(checkName, reason, sink, addFinding) {
|
|
86
70
|
const msg = `payload currency UNVERIFIED — ${reason}`;
|
|
87
71
|
sink.warn(msg);
|
|
@@ -101,8 +85,8 @@ function resolveUpgradeCommand(projectRoot, manifest, isFile, pm = "npm") {
|
|
|
101
85
|
}
|
|
102
86
|
return vendored ? VENDORED_NPM_DEPOSIT_UPGRADE_COMMAND : CANONICAL_UPGRADE_COMMAND;
|
|
103
87
|
}
|
|
104
|
-
function emitStale(checkName, installedLabel, remoteLabel, ref, upgradeCommand, sink, addFinding, extras = {}
|
|
105
|
-
const msg = `Framework payload is stale (installed ${installedLabel} behind
|
|
88
|
+
function emitStale(checkName, installedLabel, remoteLabel, ref, upgradeCommand, sink, addFinding, extras = {}) {
|
|
89
|
+
const msg = `Framework payload is stale (installed ${installedLabel} behind remote ${remoteLabel} for ref '${ref}'). ` +
|
|
106
90
|
`Recommendation: run \`${upgradeCommand}\` from any shell with Node ≥ 20.`;
|
|
107
91
|
sink.warn(msg);
|
|
108
92
|
addFinding({
|
|
@@ -209,37 +193,52 @@ export function runPayloadStalenessCheck(projectRoot, sink, addFinding, seams =
|
|
|
209
193
|
catch {
|
|
210
194
|
remoteResult = { ok: false, stdout: "" };
|
|
211
195
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
196
|
+
const remoteSha = remoteResult.ok ? parseRemoteSha(remoteResult.stdout) : "";
|
|
197
|
+
if (remoteSha && installedSha !== remoteSha) {
|
|
198
|
+
emitStale(checkName, `sha ${installedSha.slice(0, 8)}...`, `sha ${remoteSha.slice(0, 8)}...`, ref, upgradeCommand, sink, addFinding, {
|
|
199
|
+
installed_sha: installedSha,
|
|
200
|
+
remote_sha: remoteSha,
|
|
201
|
+
resolver: "git-ls-remote",
|
|
202
|
+
staleness_kind: "pinned-ref-moved",
|
|
203
|
+
});
|
|
204
|
+
return;
|
|
222
205
|
}
|
|
223
|
-
const
|
|
224
|
-
const
|
|
225
|
-
if (
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
installed_version: installedVersion,
|
|
229
|
-
remote_version: npmResult.version,
|
|
230
|
-
resolver: "npm-view",
|
|
231
|
-
}, "npm registry");
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
if (installedVersion === npmResult.version.replace(/^v/i, "")) {
|
|
235
|
-
sink.info(`${checkName}: current (version matches npm registry)`);
|
|
206
|
+
const installedCandidate = (tag || ref).trim().replace(/^refs\/tags\//, "");
|
|
207
|
+
const applicability = evaluateReleaseAvailability(installedCandidate, null);
|
|
208
|
+
if (applicability.status === "not-applicable") {
|
|
209
|
+
if (remoteSha && installedSha === remoteSha) {
|
|
210
|
+
sink.info(`${checkName}: current (sha matches remote; ref is not a release tag)`);
|
|
236
211
|
return;
|
|
237
212
|
}
|
|
238
|
-
|
|
213
|
+
const reason = remoteResult.ok
|
|
214
|
+
? "ls-remote produced no sha; ref is not a release tag"
|
|
215
|
+
: "could not reach remote; ref is not a release tag";
|
|
216
|
+
sink.info(`${checkName}: skip -- ${reason}`);
|
|
217
|
+
emitUnverified(checkName, reason, sink, addFinding);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
let npmResult;
|
|
221
|
+
try {
|
|
222
|
+
npmResult = runNpmView();
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
npmResult = { ok: false, version: "" };
|
|
226
|
+
}
|
|
227
|
+
const availability = evaluateReleaseAvailability(installedCandidate, npmResult.ok ? npmResult.version : null);
|
|
228
|
+
if (availability.status === "available") {
|
|
229
|
+
emitReleaseAvailable(checkName, availability.installedVersion, availability.latestVersion, ref, upgradeCommand, sink, addFinding);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (availability.status === "current") {
|
|
233
|
+
sink.info(`${checkName}: current (installed release >= npm latest)`);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (availability.status === "prerelease-ignored") {
|
|
237
|
+
sink.info(`${checkName}: current (npm candidate is a prerelease; stable install retained)`);
|
|
239
238
|
return;
|
|
240
239
|
}
|
|
241
240
|
const reason = remoteResult.ok
|
|
242
|
-
? "
|
|
241
|
+
? "npm registry release lookup unavailable or returned a non-publishable version"
|
|
243
242
|
: "could not reach remote (git ls-remote / npm view both unavailable)";
|
|
244
243
|
sink.info(`${checkName}: skip -- ${reason}`);
|
|
245
244
|
emitUnverified(checkName, reason, sink, addFinding);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Outcomes produced by the reusable latest-release comparison. */
|
|
2
|
+
export type ReleaseAvailabilityStatus = "available" | "current" | "prerelease-ignored" | "not-applicable" | "unverified";
|
|
3
|
+
/** Network resolver that supplied the latest published version. */
|
|
4
|
+
export type ReleaseAvailabilityResolver = "npm-view";
|
|
5
|
+
/** Typed result consumed by doctor today and safe-idle upgrade prompting later. */
|
|
6
|
+
export type ReleaseAvailabilityResult = {
|
|
7
|
+
readonly status: "available" | "current" | "prerelease-ignored";
|
|
8
|
+
readonly installedVersion: string;
|
|
9
|
+
readonly latestVersion: string;
|
|
10
|
+
readonly resolver: ReleaseAvailabilityResolver;
|
|
11
|
+
} | {
|
|
12
|
+
readonly status: "not-applicable";
|
|
13
|
+
readonly installedVersion: null;
|
|
14
|
+
readonly latestVersion: string | null;
|
|
15
|
+
readonly resolver: ReleaseAvailabilityResolver;
|
|
16
|
+
} | {
|
|
17
|
+
readonly status: "unverified";
|
|
18
|
+
readonly installedVersion: string;
|
|
19
|
+
readonly latestVersion: null;
|
|
20
|
+
readonly resolver: ReleaseAvailabilityResolver;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Compare an installed release with a resolver-provided latest release.
|
|
24
|
+
* Stable installs deliberately ignore prerelease candidates even when their
|
|
25
|
+
* numeric core is newer.
|
|
26
|
+
*/
|
|
27
|
+
export declare function evaluateReleaseAvailability(installed: string, latest: string | null, resolver?: ReleaseAvailabilityResolver): ReleaseAvailabilityResult;
|
|
28
|
+
//# sourceMappingURL=release-availability.d.ts.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { comparePublishableVersions, isPrereleaseTag, isPublishable } from "../release/version.js";
|
|
2
|
+
function normalizePublishableVersion(version) {
|
|
3
|
+
if (version === null)
|
|
4
|
+
return null;
|
|
5
|
+
const candidate = version.trim().replace(/^refs\/tags\//, "");
|
|
6
|
+
if (!candidate || !isPublishable(candidate))
|
|
7
|
+
return null;
|
|
8
|
+
return candidate.replace(/^v/, "");
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Compare an installed release with a resolver-provided latest release.
|
|
12
|
+
* Stable installs deliberately ignore prerelease candidates even when their
|
|
13
|
+
* numeric core is newer.
|
|
14
|
+
*/
|
|
15
|
+
export function evaluateReleaseAvailability(installed, latest, resolver = "npm-view") {
|
|
16
|
+
const installedVersion = normalizePublishableVersion(installed);
|
|
17
|
+
const latestVersion = normalizePublishableVersion(latest);
|
|
18
|
+
if (installedVersion === null) {
|
|
19
|
+
return { status: "not-applicable", installedVersion, latestVersion, resolver };
|
|
20
|
+
}
|
|
21
|
+
if (latestVersion === null) {
|
|
22
|
+
return { status: "unverified", installedVersion, latestVersion, resolver };
|
|
23
|
+
}
|
|
24
|
+
if (!isPrereleaseTag(installedVersion) && isPrereleaseTag(latestVersion)) {
|
|
25
|
+
return { status: "prerelease-ignored", installedVersion, latestVersion, resolver };
|
|
26
|
+
}
|
|
27
|
+
const comparison = comparePublishableVersions(installedVersion, latestVersion);
|
|
28
|
+
return {
|
|
29
|
+
status: comparison < 0 ? "available" : "current",
|
|
30
|
+
installedVersion,
|
|
31
|
+
latestVersion,
|
|
32
|
+
resolver,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=release-availability.js.map
|
package/dist/doctor/types.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { ShadowedPlanExtension } from "../policy/plan-extensions.js";
|
|
|
3
3
|
import type { EngineProbeResult } from "../resolution/classify.js";
|
|
4
4
|
import type { ResolutionMode } from "../resolution/index.js";
|
|
5
5
|
import type { ResolveUserMdResult } from "../user-config/resolve-user-md.js";
|
|
6
|
+
import type { AgentHookHealthResult } from "../verify-env/agent-hooks.js";
|
|
6
7
|
export declare const EXIT_CLEAN = 0;
|
|
7
8
|
export declare const EXIT_DRIFT = 1;
|
|
8
9
|
export declare const EXIT_CONFIG_ERROR = 2;
|
|
@@ -135,5 +136,7 @@ export interface DoctorSeams {
|
|
|
135
136
|
* its `plan` object; returns [] when no project definition is present.
|
|
136
137
|
*/
|
|
137
138
|
readonly detectPlanExtensionShadows?: (projectRoot: string) => readonly ShadowedPlanExtension[];
|
|
139
|
+
/** Read-only agent-host hook registration probe (#2438). */
|
|
140
|
+
readonly evaluateAgentHooks?: (projectRoot: string) => AgentHookHealthResult;
|
|
138
141
|
}
|
|
139
142
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/** Relative path under the resolved metrics home for helped / value telemetry (#2545). */
|
|
2
|
+
export declare const CRUD_METRICS_HISTORY_REL = "helped/crud-metrics.jsonl";
|
|
2
3
|
export type CrudOperation = "create" | "read" | "update" | "delete";
|
|
3
4
|
export type ByteDiffMinimality = "surgical" | "whole-file-rewrite";
|
|
4
5
|
export interface CrudOperationMetric {
|
|
@@ -31,9 +32,9 @@ export declare function classifyByteDiffMinimality(before: string, after: string
|
|
|
31
32
|
readonly kind: ByteDiffMinimality;
|
|
32
33
|
readonly changedRatio: number;
|
|
33
34
|
};
|
|
34
|
-
/** Absolute path to the versioned CRUD metrics ledger (#1703 Tier 1). */
|
|
35
|
-
export declare function crudMetricsHistoryPath(projectRoot: string): string;
|
|
36
|
-
/** Append CRUD operation metrics to the versioned ledger (#1703 Tier 1). */
|
|
35
|
+
/** Absolute path to the versioned CRUD metrics ledger (#1703 Tier 1 / #2545). */
|
|
36
|
+
export declare function crudMetricsHistoryPath(projectRoot: string): string | null;
|
|
37
|
+
/** Append CRUD operation metrics to the versioned ledger (#1703 Tier 1 / #2545). */
|
|
37
38
|
export declare function persistCrudMetrics(projectRoot: string, metrics: readonly CrudOperationMetric[]): void;
|
|
38
39
|
/**
|
|
39
40
|
* Instrumented vBRIEF/xBRIEF CRUD chokepoint (#1703 Tier 1).
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
3
|
import { readCorePackageVersion } from "../engine-version.js";
|
|
4
|
-
import {
|
|
4
|
+
import { helpedMetricsHistoryPath } from "../metrics/resolve-metrics-home.js";
|
|
5
5
|
import { scanVbrief } from "../vbrief-validate/conformance.js";
|
|
6
6
|
import { validateVbriefSchema } from "../vbrief-validate/schema.js";
|
|
7
|
-
|
|
7
|
+
/** Relative path under the resolved metrics home for helped / value telemetry (#2545). */
|
|
8
|
+
export const CRUD_METRICS_HISTORY_REL = "helped/crud-metrics.jsonl";
|
|
8
9
|
/** Whole-file rewrites change at least half of the bytes or re-serialize with high drift. */
|
|
9
10
|
export const BYTE_DIFF_WHOLE_FILE_THRESHOLD = 0.5;
|
|
10
11
|
function isRecord(value) {
|
|
@@ -85,16 +86,19 @@ function sanitizeInlineMessage(message) {
|
|
|
85
86
|
function ensureParentDir(path) {
|
|
86
87
|
mkdirSync(dirname(path), { recursive: true });
|
|
87
88
|
}
|
|
88
|
-
/** Absolute path to the versioned CRUD metrics ledger (#1703 Tier 1). */
|
|
89
|
+
/** Absolute path to the versioned CRUD metrics ledger (#1703 Tier 1 / #2545). */
|
|
89
90
|
export function crudMetricsHistoryPath(projectRoot) {
|
|
90
|
-
return
|
|
91
|
+
return helpedMetricsHistoryPath(projectRoot);
|
|
91
92
|
}
|
|
92
|
-
/** Append CRUD operation metrics to the versioned ledger (#1703 Tier 1). */
|
|
93
|
+
/** Append CRUD operation metrics to the versioned ledger (#1703 Tier 1 / #2545). */
|
|
93
94
|
export function persistCrudMetrics(projectRoot, metrics) {
|
|
94
95
|
if (metrics.length === 0) {
|
|
95
96
|
return;
|
|
96
97
|
}
|
|
97
98
|
const path = crudMetricsHistoryPath(projectRoot);
|
|
99
|
+
if (path === null) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
98
102
|
mkdirSync(dirname(path), { recursive: true });
|
|
99
103
|
for (const metric of metrics) {
|
|
100
104
|
appendFileSync(path, `${JSON.stringify(metric)}\n`, "utf8");
|
package/dist/eval/health.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const HEALTH_SCHEMA_VERSION: 1;
|
|
2
|
-
|
|
2
|
+
/** Relative path under the resolved metrics home for eval:health history (#2545). */
|
|
3
|
+
export declare const HEALTH_HISTORY_REL = "health/health-history.jsonl";
|
|
3
4
|
/** One static Tier-0 gate probe aggregated into the health score. */
|
|
4
5
|
export interface GateProbeResult {
|
|
5
6
|
readonly id: string;
|
|
@@ -37,15 +38,15 @@ export interface EvaluateHealthResult {
|
|
|
37
38
|
readonly report: HealthReport | null;
|
|
38
39
|
readonly message: string;
|
|
39
40
|
}
|
|
40
|
-
/** Absolute path to the versioned health history ledger. */
|
|
41
|
-
export declare function healthHistoryPath(projectRoot: string): string;
|
|
41
|
+
/** Absolute path to the versioned health history ledger (#2545). */
|
|
42
|
+
export declare function healthHistoryPath(projectRoot: string): string | null;
|
|
42
43
|
/** Detect the canonical wipCap unsatisfiable-nudge contradiction (#1694). */
|
|
43
44
|
export declare function detectWipCapUnsatisfiableNudge(projectRoot: string): ContradictionEvidence | null;
|
|
44
45
|
/** Run all registered contradictory-gate detectors. */
|
|
45
46
|
export declare function detectContradictoryGates(projectRoot: string): ContradictionEvidence[];
|
|
46
47
|
/** Compute a 0-100 score from gate pass rate minus contradiction penalty. */
|
|
47
48
|
export declare function computeHealthScore(gates: readonly GateProbeResult[], contradictions: readonly ContradictionEvidence[]): number;
|
|
48
|
-
/** Append one health run to the versioned ledger (#1703 Tier 0). */
|
|
49
|
+
/** Append one health run to the versioned ledger (#1703 Tier 0 / #2545). */
|
|
49
50
|
export declare function persistHealthRun(projectRoot: string, report: HealthReport): void;
|
|
50
51
|
/** Aggregate Tier-0 static gates into a versioned framework health score (#1703). */
|
|
51
52
|
export declare function evaluateHealth(options?: EvaluateHealthOptions): EvaluateHealthResult;
|