@deftai/directive-core 0.75.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 +77 -22
- package/dist/agents-md-budget/skill-frontmatter.d.ts +2 -0
- package/dist/agents-md-budget/skill-frontmatter.js +4 -3
- package/dist/cache/operations.d.ts +2 -0
- package/dist/cache/operations.js +16 -1
- 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/init-deposit/scaffold.js +28 -17
- package/dist/intake/issue-ingest.js +23 -4
- 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/packaging/openpackage-tiers.d.ts +12 -0
- package/dist/packaging/openpackage-tiers.js +42 -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/preflight/evaluate.d.ts +5 -2
- package/dist/preflight/evaluate.js +9 -4
- package/dist/preflight/index.d.ts +1 -1
- package/dist/preflight/index.js +1 -1
- package/dist/release/pipeline-fixture.d.ts +3 -0
- package/dist/release/pipeline-fixture.js +11 -0
- package/dist/release/pipeline.js +4 -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/scope/transition.js +17 -2
- package/dist/scope/vbrief-ref.d.ts +6 -0
- package/dist/scope/vbrief-ref.js +21 -2
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +1 -0
- package/dist/session/posture.d.ts +50 -0
- package/dist/session/posture.js +152 -0
- package/dist/session/ritual-sentinel.d.ts +2 -0
- package/dist/session/ritual-sentinel.js +3 -0
- package/dist/session/session-start.d.ts +8 -0
- package/dist/session/session-start.js +43 -0
- package/dist/session/verify-session-ritual.d.ts +6 -0
- package/dist/session/verify-session-ritual.js +67 -4
- package/dist/triage/bootstrap/gitignore.js +8 -4
- package/dist/triage/cache-path.js +16 -3
- package/dist/triage/welcome/writers.js +2 -0
- package/dist/ts-check-lane/index.d.ts +1 -1
- package/dist/ts-check-lane/index.js +1 -1
- package/dist/ts-check-lane/run-lane.d.ts +5 -0
- package/dist/ts-check-lane/run-lane.js +15 -0
- package/dist/vbrief-build/constants.d.ts +3 -3
- package/dist/vbrief-build/constants.js +4 -3
- package/dist/vbrief-build/index.d.ts +1 -1
- package/dist/vbrief-build/index.js +1 -1
- 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). */
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
+
import { getOpenPackageDefaultInstallTier, resolveOpenPackageTierSkills, } from "../packaging/openpackage-tiers.js";
|
|
3
4
|
import { AGENTS_MANAGED_CLOSE } from "../platform/constants.js";
|
|
4
5
|
import { resolveAgentsMdBudget, } from "../policy/agents-md-budget.js";
|
|
5
6
|
import { measureSkillFrontmatter } from "./skill-frontmatter.js";
|
|
6
7
|
/**
|
|
7
8
|
* Layered absolute north-star for the always-on managed surface (#2372 / #2450 / #2452).
|
|
8
9
|
*
|
|
9
|
-
* `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
|
+
*
|
|
10
15
|
* When `plan.policy.agentsMdBudget.absoluteMaxBytes` is set, growth past that seeded
|
|
11
16
|
* ratchet fails closed; distance-to-north-star is always reported. Optional release-gate
|
|
12
17
|
* north-star enforcement: DEFT_AGENTS_MD_BUDGET_ENFORCE_NORTH_STAR=1 (waiver:
|
|
@@ -19,6 +24,9 @@ import { measureSkillFrontmatter } from "./skill-frontmatter.js";
|
|
|
19
24
|
*/
|
|
20
25
|
export const ABSOLUTE_MANAGED_MAX_BYTES = 8192;
|
|
21
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;
|
|
22
30
|
/** Bootstrap host hooks are 0 B until #2438 ships. */
|
|
23
31
|
export const BOOTSTRAP_HOOK_BYTES = 0;
|
|
24
32
|
/** Rough UTF-8 bytes-per-token estimate for advisory reporting (~8192 B ≈ ~2048 tok). */
|
|
@@ -123,12 +131,23 @@ function resolveHarnessProfile(budget, projectRoot) {
|
|
|
123
131
|
}
|
|
124
132
|
return "none";
|
|
125
133
|
}
|
|
126
|
-
function
|
|
134
|
+
function resolveBootstrapSkillTier(budget, projectRoot) {
|
|
127
135
|
const env = process.env.DEFT_AGENTS_MD_BUDGET_SKILL_TIER?.trim();
|
|
128
136
|
if (env === "daily-core" || env === "all" || env === "none") {
|
|
129
137
|
return env;
|
|
130
138
|
}
|
|
131
|
-
|
|
139
|
+
if (budget?.skillFrontmatterTier !== undefined) {
|
|
140
|
+
return budget.skillFrontmatterTier;
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
if (getOpenPackageDefaultInstallTier(projectRoot) === "daily-core") {
|
|
144
|
+
return "daily-core";
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// No OpenPackage manifest at this project root — fall through.
|
|
149
|
+
}
|
|
150
|
+
return "all";
|
|
132
151
|
}
|
|
133
152
|
/** Measure managed + DD-3 skill frontmatter + bootstrap hooks. */
|
|
134
153
|
export function measureBootstrapSurface(projectRoot, managedText, budget) {
|
|
@@ -137,11 +156,19 @@ export function measureBootstrapSurface(projectRoot, managedText, budget) {
|
|
|
137
156
|
return managedResult;
|
|
138
157
|
}
|
|
139
158
|
const harnessProfile = resolveHarnessProfile(budget, projectRoot);
|
|
140
|
-
const tier =
|
|
159
|
+
const tier = resolveBootstrapSkillTier(budget, projectRoot);
|
|
160
|
+
let dailyCoreSkills;
|
|
161
|
+
try {
|
|
162
|
+
dailyCoreSkills = resolveOpenPackageTierSkills(projectRoot, "daily-core");
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// Maintainer trees without packaging/openpackage fall back to hardcoded daily-core.
|
|
166
|
+
}
|
|
141
167
|
const skillFrontmatter = measureSkillFrontmatter(projectRoot, {
|
|
142
168
|
harnessProfile,
|
|
143
169
|
tier,
|
|
144
170
|
bytesPerToken: ABSOLUTE_BYTES_PER_TOKEN_ESTIMATE,
|
|
171
|
+
dailyCoreSkills,
|
|
145
172
|
});
|
|
146
173
|
const totalBytes = managedResult.bytes + skillFrontmatter.bytes + BOOTSTRAP_HOOK_BYTES;
|
|
147
174
|
return {
|
|
@@ -166,7 +193,7 @@ function formatNorthStarOverage(measure) {
|
|
|
166
193
|
return formatNorthStarOverageBytes(measure.bytes - ABSOLUTE_MANAGED_MAX_BYTES, measure.estimatedTokens - ABSOLUTE_MANAGED_MAX_TOKENS);
|
|
167
194
|
}
|
|
168
195
|
function formatCombinedNorthStarOverage(bootstrap) {
|
|
169
|
-
return formatNorthStarOverageBytes(bootstrap.totalBytes -
|
|
196
|
+
return formatNorthStarOverageBytes(bootstrap.totalBytes - COMBINED_ALWAYS_ON_MAX_BYTES, bootstrap.totalEstimatedTokens - COMBINED_ALWAYS_ON_MAX_TOKENS);
|
|
170
197
|
}
|
|
171
198
|
function formatNorthStarDistance(measure) {
|
|
172
199
|
const overBytes = measure.bytes - ABSOLUTE_MANAGED_MAX_BYTES;
|
|
@@ -180,14 +207,14 @@ function formatNorthStarDistance(measure) {
|
|
|
180
207
|
`${formatNorthStarOverage(measure)}).`);
|
|
181
208
|
}
|
|
182
209
|
function formatCombinedNorthStarDistance(bootstrap) {
|
|
183
|
-
const overBytes = bootstrap.totalBytes -
|
|
184
|
-
const overTokens = bootstrap.totalEstimatedTokens -
|
|
210
|
+
const overBytes = bootstrap.totalBytes - COMBINED_ALWAYS_ON_MAX_BYTES;
|
|
211
|
+
const overTokens = bootstrap.totalEstimatedTokens - COMBINED_ALWAYS_ON_MAX_TOKENS;
|
|
185
212
|
if (overBytes <= 0 && overTokens <= 0) {
|
|
186
213
|
return (`north-star: combined always-on ${bootstrap.totalBytes} bytes ` +
|
|
187
214
|
`(~${bootstrap.totalEstimatedTokens} tok) within ` +
|
|
188
|
-
`≤${
|
|
215
|
+
`≤${COMBINED_ALWAYS_ON_MAX_BYTES} B / ~${COMBINED_ALWAYS_ON_MAX_TOKENS} tok target.`);
|
|
189
216
|
}
|
|
190
|
-
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 ` +
|
|
191
218
|
`(current ${bootstrap.totalBytes} bytes / ~${bootstrap.totalEstimatedTokens} tok — ` +
|
|
192
219
|
`${formatCombinedNorthStarOverage(bootstrap)}).`);
|
|
193
220
|
}
|
|
@@ -204,12 +231,24 @@ function formatBootstrapItemization(bootstrap) {
|
|
|
204
231
|
}
|
|
205
232
|
function formatAbsoluteAdvisory(bootstrap) {
|
|
206
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
|
+
}
|
|
207
249
|
return (`⚠ verify:agents-md-budget: always-on bootstrap advisory — ` +
|
|
208
250
|
`${formatBootstrapItemization(bootstrap)}.\n` +
|
|
209
|
-
|
|
210
|
-
`of ${ABSOLUTE_MANAGED_MAX_BYTES} bytes / ~${ABSOLUTE_MANAGED_MAX_TOKENS} tok ` +
|
|
211
|
-
`(OVER: ${formatNorthStarOverage(measure)}).\n` +
|
|
212
|
-
` ${formatCombinedNorthStarDistance(bootstrap)}\n` +
|
|
251
|
+
`${violationLines.join("\n")}\n` +
|
|
213
252
|
" Advisory only — set plan.policy.agentsMdBudget.absoluteMaxBytes to enable " +
|
|
214
253
|
"fail-closed managed growth ratchet (#2452).\n" +
|
|
215
254
|
" Optional DD-3 ratchet: plan.policy.agentsMdBudget.skillFrontmatterMaxBytes (#2463).\n" +
|
|
@@ -248,13 +287,29 @@ function formatSkillFrontmatterRefusal(bootstrap, skillFrontmatterMaxBytes, proj
|
|
|
248
287
|
}
|
|
249
288
|
function formatNorthStarRefusal(bootstrap, projectRoot) {
|
|
250
289
|
const measure = bootstrap.managed;
|
|
251
|
-
const
|
|
252
|
-
|
|
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} ` +
|
|
253
309
|
`(project_root=${projectRoot}, release-gate mode).\n` +
|
|
254
310
|
` ${formatBootstrapItemization(bootstrap)}\n` +
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
" 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" +
|
|
258
313
|
" or set DEFT_ALLOW_ABSOLUTE_BUDGET_WAIVER=1 for a time-boxed operator waiver (#2452).");
|
|
259
314
|
}
|
|
260
315
|
function enforceNorthStarEnabled() {
|
|
@@ -267,8 +322,8 @@ function attachNorthStarNote(result, bootstrap, options) {
|
|
|
267
322
|
const measure = bootstrap.managed;
|
|
268
323
|
const overManagedBytes = measure.bytes > ABSOLUTE_MANAGED_MAX_BYTES;
|
|
269
324
|
const overManagedTokens = measure.estimatedTokens > ABSOLUTE_MANAGED_MAX_TOKENS;
|
|
270
|
-
const overCombinedBytes = bootstrap.totalBytes >
|
|
271
|
-
const overCombinedTokens = bootstrap.totalEstimatedTokens >
|
|
325
|
+
const overCombinedBytes = bootstrap.totalBytes > COMBINED_ALWAYS_ON_MAX_BYTES;
|
|
326
|
+
const overCombinedTokens = bootstrap.totalEstimatedTokens > COMBINED_ALWAYS_ON_MAX_TOKENS;
|
|
272
327
|
if (options.advisoryOnly) {
|
|
273
328
|
if (!overManagedBytes && !overManagedTokens && !overCombinedBytes && !overCombinedTokens) {
|
|
274
329
|
return result;
|
|
@@ -425,8 +480,8 @@ export function evaluate(projectRoot, options = {}) {
|
|
|
425
480
|
}
|
|
426
481
|
const overNorthStarManaged = measure.bytes > ABSOLUTE_MANAGED_MAX_BYTES ||
|
|
427
482
|
measure.estimatedTokens > ABSOLUTE_MANAGED_MAX_TOKENS;
|
|
428
|
-
const overNorthStarCombined = bootstrap.totalBytes >
|
|
429
|
-
bootstrap.totalEstimatedTokens >
|
|
483
|
+
const overNorthStarCombined = bootstrap.totalBytes > COMBINED_ALWAYS_ON_MAX_BYTES ||
|
|
484
|
+
bootstrap.totalEstimatedTokens > COMBINED_ALWAYS_ON_MAX_TOKENS;
|
|
430
485
|
if (enforceNorthStarEnabled() &&
|
|
431
486
|
(overNorthStarManaged || overNorthStarCombined) &&
|
|
432
487
|
!northStarWaiverActive()) {
|
|
@@ -27,6 +27,8 @@ export interface MeasureSkillFrontmatterOptions {
|
|
|
27
27
|
readonly tier?: SkillFrontmatterTier;
|
|
28
28
|
readonly harnessProfile?: HarnessProfile;
|
|
29
29
|
readonly bytesPerToken?: number;
|
|
30
|
+
/** Override daily-core skill names (from OpenPackage resolveTierSkills). */
|
|
31
|
+
readonly dailyCoreSkills?: readonly string[];
|
|
30
32
|
}
|
|
31
33
|
/** Measure harness-injected skill frontmatter bytes for the configured profile/tier. */
|
|
32
34
|
export declare function measureSkillFrontmatter(projectRoot: string, options?: MeasureSkillFrontmatterOptions): SkillFrontmatterMeasure;
|
|
@@ -61,14 +61,14 @@ function listSkillDirs(skillsRoot) {
|
|
|
61
61
|
.map((entry) => entry.name)
|
|
62
62
|
.sort();
|
|
63
63
|
}
|
|
64
|
-
function
|
|
64
|
+
function skillInSelectedTier(skillName, tier, dailyCore) {
|
|
65
65
|
if (tier === "none") {
|
|
66
66
|
return false;
|
|
67
67
|
}
|
|
68
68
|
if (tier === "all") {
|
|
69
69
|
return true;
|
|
70
70
|
}
|
|
71
|
-
return
|
|
71
|
+
return dailyCore.has(skillName);
|
|
72
72
|
}
|
|
73
73
|
/** Measure harness-injected skill frontmatter bytes for the configured profile/tier. */
|
|
74
74
|
export function measureSkillFrontmatter(projectRoot, options = {}) {
|
|
@@ -76,6 +76,7 @@ export function measureSkillFrontmatter(projectRoot, options = {}) {
|
|
|
76
76
|
const tier = options.tier ?? "all";
|
|
77
77
|
const bytesPerToken = options.bytesPerToken ?? 4;
|
|
78
78
|
const skillsRoot = options.skillsRoot ?? defaultSkillsRoot(projectRoot);
|
|
79
|
+
const dailyCore = options.dailyCoreSkills !== undefined ? new Set(options.dailyCoreSkills) : DAILY_CORE_SET;
|
|
79
80
|
if (harnessProfile === "none" || tier === "none") {
|
|
80
81
|
return {
|
|
81
82
|
bytes: 0,
|
|
@@ -88,7 +89,7 @@ export function measureSkillFrontmatter(projectRoot, options = {}) {
|
|
|
88
89
|
}
|
|
89
90
|
const entries = [];
|
|
90
91
|
for (const skillName of listSkillDirs(skillsRoot)) {
|
|
91
|
-
if (!
|
|
92
|
+
if (!skillInSelectedTier(skillName, tier, dailyCore)) {
|
|
92
93
|
continue;
|
|
93
94
|
}
|
|
94
95
|
const skillPath = join(skillsRoot, skillName, "SKILL.md");
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { ProjectionContainmentError } from "../fs/projection-containment.js";
|
|
1
2
|
import { type CacheCaps, type EntryUsage } from "./quota.js";
|
|
2
3
|
import { SCANNER_VERSION } from "./scanner.js";
|
|
3
4
|
import { type Clock } from "./time.js";
|
|
4
5
|
import type { CacheGetOptions, CachePutOptions, GetResult, PutResult } from "./types.js";
|
|
6
|
+
export { ProjectionContainmentError as CacheContainmentError };
|
|
5
7
|
/** Write a cache entry (mirrors `cache.cache_put`). */
|
|
6
8
|
export declare function cachePut(source: string, key: string, raw: Record<string, unknown>, options?: CachePutOptions): PutResult;
|
|
7
9
|
/** Read a cache entry (mirrors `cache.cache_get`). */
|
package/dist/cache/operations.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync, unlinkSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { assertProjectionContained, ProjectionContainmentError, } from "../fs/projection-containment.js";
|
|
3
4
|
import { pyRepr } from "../scm/py-format.js";
|
|
4
5
|
import { ALLOWED_SOURCES, REPO_RE, SOURCE_TTL_SECONDS } from "./constants.js";
|
|
5
6
|
import { CacheCapBreachedError, CacheError, CacheNotFoundError, CacheValidationError, } from "./errors.js";
|
|
@@ -10,6 +11,15 @@ import { enforceCaps, predictEvictionSet, resolveCaps, } from "./quota.js";
|
|
|
10
11
|
import { flagsForMeta, SCANNER_VERSION, scan } from "./scanner.js";
|
|
11
12
|
import { addSeconds, parseIso, systemClock, utcIso } from "./time.js";
|
|
12
13
|
import { validateMeta } from "./validate.js";
|
|
14
|
+
export { ProjectionContainmentError as CacheContainmentError };
|
|
15
|
+
/** Refuse symlink-escaping `.deft-cache` before mkdir/read/write (#2470). */
|
|
16
|
+
function assertWritableCachePath(cacheRoot, ...segments) {
|
|
17
|
+
const cacheAbs = resolve(cacheRoot);
|
|
18
|
+
const projectDir = dirname(cacheAbs);
|
|
19
|
+
const target = segments.length > 0 ? join(cacheAbs, ...segments) : cacheAbs;
|
|
20
|
+
assertProjectionContained(projectDir, target);
|
|
21
|
+
return target;
|
|
22
|
+
}
|
|
13
23
|
function renderContent(source, raw) {
|
|
14
24
|
if (source === "github-issue") {
|
|
15
25
|
const number = raw.number;
|
|
@@ -82,6 +92,7 @@ export function cachePut(source, key, raw, options = {}) {
|
|
|
82
92
|
}
|
|
83
93
|
const expires = addSeconds(fetched, ttl);
|
|
84
94
|
const cacheRoot = options.cacheRoot ?? ".deft-cache";
|
|
95
|
+
assertWritableCachePath(cacheRoot);
|
|
85
96
|
const edir = entryDir(source, key, cacheRoot);
|
|
86
97
|
const rawText = pythonJsonDump(raw);
|
|
87
98
|
const rawSize = Buffer.byteLength(rawText, "utf8");
|
|
@@ -182,6 +193,7 @@ export function cachePut(source, key, raw, options = {}) {
|
|
|
182
193
|
export function cacheGet(source, key, options = {}) {
|
|
183
194
|
const clock = options.clock ?? systemClock;
|
|
184
195
|
const cacheRoot = options.cacheRoot ?? ".deft-cache";
|
|
196
|
+
assertWritableCachePath(cacheRoot);
|
|
185
197
|
const allowStale = options.allowStale ?? true;
|
|
186
198
|
const edir = entryDir(source, key, cacheRoot);
|
|
187
199
|
const metaRelPath = `${source}/${key}/meta.json`;
|
|
@@ -220,6 +232,7 @@ export function cacheInvalidate(source, key, options = {}) {
|
|
|
220
232
|
const clock = options.clock ?? systemClock;
|
|
221
233
|
validateKey(source, key);
|
|
222
234
|
const cacheRoot = options.cacheRoot ?? ".deft-cache";
|
|
235
|
+
assertWritableCachePath(cacheRoot);
|
|
223
236
|
const edir = entryDir(source, key, cacheRoot);
|
|
224
237
|
const existed = existsSync(edir);
|
|
225
238
|
if (existed)
|
|
@@ -252,6 +265,7 @@ export function cachePrune(options = {}) {
|
|
|
252
265
|
throw new CacheError(`--older-than-days must be >= 0 (got ${JSON.stringify(olderThanDays)})`);
|
|
253
266
|
}
|
|
254
267
|
const cacheRoot = options.cacheRoot ?? ".deft-cache";
|
|
268
|
+
assertWritableCachePath(cacheRoot);
|
|
255
269
|
if (!existsSync(cacheRoot))
|
|
256
270
|
return [];
|
|
257
271
|
const cutoff = addSeconds(clock.now(), -olderThanDays * 24 * 60 * 60);
|
|
@@ -313,6 +327,7 @@ function collectMetaPathsUnder(srcRoot) {
|
|
|
313
327
|
export function cachePruneToCap(options = {}) {
|
|
314
328
|
const clock = options.clock ?? systemClock;
|
|
315
329
|
const cacheRoot = options.cacheRoot ?? ".deft-cache";
|
|
330
|
+
assertWritableCachePath(cacheRoot);
|
|
316
331
|
const resolved = options.caps ?? resolveCaps();
|
|
317
332
|
if (!resolved.maxBytes && !resolved.maxEntries)
|
|
318
333
|
return [];
|
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).
|