@deftai/directive-core 0.76.0 → 0.77.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.js +2 -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/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/index.d.ts +1 -0
- package/dist/index.js +1 -0
- 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/version.d.ts +2 -0
- package/dist/release/version.js +42 -0
- package/dist/release-e2e/npm-ops.js +20 -6
- package/dist/verify-env/command-spawn.d.ts +24 -0
- package/dist/verify-env/command-spawn.js +78 -0
- package/dist/verify-env/index.d.ts +1 -0
- package/dist/verify-env/index.js +1 -0
- package/package.json +7 -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.js
CHANGED
|
@@ -163,7 +163,8 @@ export function cmdDoctor(args, seams = {}) {
|
|
|
163
163
|
sink.blank();
|
|
164
164
|
}
|
|
165
165
|
// #2182: payload-staleness is the only doctor check that can reach a
|
|
166
|
-
// registry (git
|
|
166
|
+
// registry (git verifies the pin; npm compares stable release availability).
|
|
167
|
+
// It stays in the OFFLINE
|
|
167
168
|
// tier (skipped) unless the operator explicitly opts into the NETWORK tier
|
|
168
169
|
// via `--network`, and the tool + registry class is disclosed BEFORE the
|
|
169
170
|
// check runs -- never silently, never as a side effect of a read-only
|
|
@@ -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
|
|
@@ -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;
|
package/dist/eval/health.js
CHANGED
|
@@ -3,7 +3,8 @@ import { dirname, resolve } from "node:path";
|
|
|
3
3
|
import { agentsRefreshPlan } from "../doctor/agents-md.js";
|
|
4
4
|
import { evaluate as evaluateEncoding } from "../encoding/evaluate.js";
|
|
5
5
|
import { readCorePackageVersion } from "../engine-version.js";
|
|
6
|
-
import {
|
|
6
|
+
import { resolveProjectDefinitionPath } from "../layout/resolve.js";
|
|
7
|
+
import { healthMetricsHistoryPath } from "../metrics/resolve-metrics-home.js";
|
|
7
8
|
import { readPlanPolicy } from "../policy/plan-extensions.js";
|
|
8
9
|
import { classifyOnboarding, detectPriorState } from "../triage/welcome/prior-state.js";
|
|
9
10
|
import { validateLinks } from "../validate-content/index.js";
|
|
@@ -11,7 +12,8 @@ import { evaluateConformance } from "../vbrief-validate/conformance.js";
|
|
|
11
12
|
import { validateWipCapOnPlan } from "../vbrief-validate/plan-hooks.js";
|
|
12
13
|
import { evaluateContentManifest } from "../verify-source/content-manifest.js";
|
|
13
14
|
export const HEALTH_SCHEMA_VERSION = 1;
|
|
14
|
-
|
|
15
|
+
/** Relative path under the resolved metrics home for eval:health history (#2545). */
|
|
16
|
+
export const HEALTH_HISTORY_REL = "health/health-history.jsonl";
|
|
15
17
|
function sanitizeOneLine(value) {
|
|
16
18
|
return value.replace(/\r?\n/g, " ");
|
|
17
19
|
}
|
|
@@ -99,9 +101,9 @@ function probeContentManifest(projectRoot) {
|
|
|
99
101
|
detail: result.code === 0 ? undefined : result.message.split("\n")[0],
|
|
100
102
|
};
|
|
101
103
|
}
|
|
102
|
-
/** Absolute path to the versioned health history ledger. */
|
|
104
|
+
/** Absolute path to the versioned health history ledger (#2545). */
|
|
103
105
|
export function healthHistoryPath(projectRoot) {
|
|
104
|
-
return
|
|
106
|
+
return healthMetricsHistoryPath(projectRoot);
|
|
105
107
|
}
|
|
106
108
|
/** Detect the canonical wipCap unsatisfiable-nudge contradiction (#1694). */
|
|
107
109
|
export function detectWipCapUnsatisfiableNudge(projectRoot) {
|
|
@@ -154,9 +156,12 @@ export function computeHealthScore(gates, contradictions) {
|
|
|
154
156
|
const penalty = contradictions.length * 15;
|
|
155
157
|
return Math.max(0, base - penalty);
|
|
156
158
|
}
|
|
157
|
-
/** Append one health run to the versioned ledger (#1703 Tier 0). */
|
|
159
|
+
/** Append one health run to the versioned ledger (#1703 Tier 0 / #2545). */
|
|
158
160
|
export function persistHealthRun(projectRoot, report) {
|
|
159
161
|
const path = healthHistoryPath(projectRoot);
|
|
162
|
+
if (path === null) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
160
165
|
mkdirSync(dirname(path), { recursive: true });
|
|
161
166
|
appendFileSync(path, `${JSON.stringify(report)}\n`, "utf8");
|
|
162
167
|
}
|
|
@@ -223,17 +228,20 @@ export function evaluateHealth(options = {}) {
|
|
|
223
228
|
contradictions,
|
|
224
229
|
};
|
|
225
230
|
if (persist) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
231
|
+
const ledgerPath = healthHistoryPath(projectRoot);
|
|
232
|
+
if (ledgerPath !== null) {
|
|
233
|
+
try {
|
|
234
|
+
persistHealthRun(projectRoot, report);
|
|
235
|
+
}
|
|
236
|
+
catch (err) {
|
|
237
|
+
const persistError = `eval:health: failed to persist health history: ${String(err)}`;
|
|
238
|
+
const healthy = score === 100 && contradictions.length === 0;
|
|
239
|
+
return {
|
|
240
|
+
code: healthy ? 0 : 1,
|
|
241
|
+
report,
|
|
242
|
+
message: `${formatHumanReport(report)}\n${persistError}`,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
237
245
|
}
|
|
238
246
|
}
|
|
239
247
|
const healthy = score === 100 && contradictions.length === 0;
|
package/dist/eval/readback.js
CHANGED
|
@@ -28,14 +28,8 @@ function parseJsonObjectOrNull(text) {
|
|
|
28
28
|
return null;
|
|
29
29
|
}
|
|
30
30
|
function readHealthHistoryRows(projectRoot) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
path = healthHistoryPath(projectRoot);
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
return []; // No xbrief/ layout; no health history.
|
|
37
|
-
}
|
|
38
|
-
if (!existsSync(path)) {
|
|
31
|
+
const path = healthHistoryPath(projectRoot);
|
|
32
|
+
if (path === null || !existsSync(path)) {
|
|
39
33
|
return [];
|
|
40
34
|
}
|
|
41
35
|
let text;
|
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export * as intake from "./intake/index.js";
|
|
|
25
25
|
export * as layout from "./layout/index.js";
|
|
26
26
|
export * as legacyBridge from "./legacy-bridge/index.js";
|
|
27
27
|
export * as lifecycle from "./lifecycle/index.js";
|
|
28
|
+
export * as metrics from "./metrics/index.js";
|
|
28
29
|
export * as orchestration from "./orchestration/index.js";
|
|
29
30
|
export * as packs from "./packs/index.js";
|
|
30
31
|
export * as platform from "./platform/index.js";
|