@lmzhen/dsh-evolution-maintenance 0.3.17 → 0.3.18

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.
@@ -1,4 +1,4 @@
1
- import { HEALTH_STAMP_RE, computeDedupGroups, computePrefixClusters, duplicateHeadings, frontmatterYamlUnsafeValues, missingSupportPointers, narrowNameMatches, overlongLines, parseFrontmatter, usageObserved } from "@lmzhen/dsh-evolution-core";
1
+ import { HEALTH_STAMP_RE, MIN_STAMP_BODY_CHARS, computeDedupGroups, computePrefixClusters, duplicateHeadings, frontmatterYamlUnsafeValues, missingSupportPointers, narrowNameMatches, overlongLines, parseFrontmatter, usageObserved } from "@lmzhen/dsh-evolution-core";
2
2
  //#region lib/types/drift-scan.js
3
3
  /**
4
4
  * Skill-library snapshot assembly for drift scanning.
@@ -26,7 +26,8 @@ async function snapshotFromLibrary(library, options = {}) {
26
26
  supportFiles: options.supportFiles?.get(entry.name),
27
27
  quality: options.quality?.get(entry.name),
28
28
  ...options.protected?.get(entry.name) !== void 0 ? { protected: options.protected.get(entry.name) } : {},
29
- ...options.catalogInvalid?.get(entry.name) === true ? { catalogInvalid: true } : {}
29
+ ...options.catalogInvalid?.get(entry.name) === true ? { catalogInvalid: true } : {},
30
+ ...options.usageObserved !== void 0 ? { usageObserved: options.usageObserved } : {}
30
31
  });
31
32
  }
32
33
  return snapshots;
@@ -76,13 +77,17 @@ function computeProbe(signal, target, snapshots) {
76
77
  if (!PROBE_SIGNALS.includes(signal)) return result(signal, [`unknown-signal '${signal}' (allowed: ${PROBE_SIGNALS.join(", ")})`], target);
77
78
  if (signal === "dedup_group") return result(signal, computeDedupGroups({ contents: new Map(snapshots.map((snapshot) => [snapshot.name, snapshot.body])) }).map((group) => group.join(" ~ ")));
78
79
  if (signal === "prefix_cluster") return result(signal, computePrefixClusters(snapshots.map((snapshot) => snapshot.name)).map((cluster) => `${cluster.key}: ${cluster.members.join(", ")}`));
79
- if (signal === "usage_observed") return result(signal, [`usage_observed=${snapshots.every((snapshot) => snapshot.usageObserved === true) ? "observed" : "unknown"}`]);
80
+ if (signal === "usage_observed") {
81
+ if (!(snapshots.length > 0 && snapshots.every((snapshot) => snapshot.usageObserved !== null && snapshot.usageObserved !== void 0))) return result(signal, ["usage_observed=unknown"]);
82
+ return result(signal, [`usage_observed=${snapshots.every((snapshot) => snapshot.usageObserved === true) ? "observed" : "unobserved"}`]);
83
+ }
80
84
  if (!target) return result(signal, ["skill-level signal requires a target skill name"]);
81
85
  const snapshot = findSnapshot(snapshots, target);
82
86
  if (!snapshot) return result(signal, [`skill '${target}' not found in snapshot`], target);
83
87
  const body = snapshot.body;
84
88
  switch (signal) {
85
89
  case "stamp_density": {
90
+ if (body.length < MIN_STAMP_BODY_CHARS) return result(signal, ["stamp_density=below-min-body", `body=${body.length} chars below MIN_STAMP_BODY_CHARS=${MIN_STAMP_BODY_CHARS}`], target);
86
91
  const kb = Math.max(1, body.length / 1024);
87
92
  const stamps = body.match(HEALTH_STAMP_RE) ?? [];
88
93
  return result(signal, [`stamp_density=${(stamps.length / kb).toFixed(2)}/KB (${stamps.length} stamps / ${Math.round(kb)}KB)`, ...stamps.slice(0, 10).map((stamp) => `stamp: ${stamp}`)], target);
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { i as snapshotFromLibrary, n as PROBE_SIGNALS, r as computeProbe, t as buildEnrichment } from "./enrichment-ePcRn3GX.js";
2
- import { AUTHORING_DESCRIPTION_BAR, DEFAULT_HEALTH_THRESHOLDS, DRIFT_MAX_LINE_CHARS, DRIFT_SIGNALS_VERSION, DRIFT_SIGNAL_NOUNS, LOW_QUALITY_THRESHOLD, MAINTAIN_PROMPT, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, computeDriftSignals, findDriftSignal, redactSecrets, verifyPromptBundle } from "@lmzhen/dsh-evolution-core";
1
+ import { i as snapshotFromLibrary, n as PROBE_SIGNALS, r as computeProbe, t as buildEnrichment } from "./enrichment-Di8Y4kev.js";
2
+ import { AUTHORING_DESCRIPTION_BAR, DEFAULT_HEALTH_THRESHOLDS, DRIFT_MAX_LINE_CHARS, DRIFT_SIGNALS_VERSION, DRIFT_SIGNAL_NOUNS, LOW_QUALITY_THRESHOLD, MAINTAIN_PROMPT, MIN_STAMP_BODY_CHARS, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, computeDriftSignals, findDriftSignal, redactSecrets, verifyPromptBundle } from "@lmzhen/dsh-evolution-core";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  //#region lib/types/render-facts.js
5
5
  /**
@@ -148,7 +148,9 @@ function validateAndNormalizeMaintainPlan(raw, report, validSignals) {
148
148
  if (typeof item.impact !== "string" || !IMPACTS.has(item.impact)) errors.push(`${path}.impact: invalid`);
149
149
  if (!isNonEmptyString(item.impact_reason)) errors.push(`${path}.impact_reason: required`);
150
150
  if (typeof item.reversibility !== "string" || !REVERSIBILITIES.has(item.reversibility)) errors.push(`${path}.reversibility: invalid`);
151
- if (!isNonEmptyString(item.undo_path)) if (item.reversibility === "none") item.undo_path = "n/a";
151
+ let normalizedUndoPath;
152
+ if (isNonEmptyString(item.undo_path)) normalizedUndoPath = item.undo_path;
153
+ else if (item.reversibility === "none") normalizedUndoPath = "n/a";
152
154
  else {
153
155
  const rev = typeof item.reversibility === "string" ? item.reversibility : "missing";
154
156
  errors.push(`${path}.undo_path: required (reversibility=${rev})`);
@@ -169,7 +171,7 @@ function validateAndNormalizeMaintainPlan(raw, report, validSignals) {
169
171
  impact: isNonEmptyString(item.impact) ? item.impact : "neutral",
170
172
  impact_reason: str(item.impact_reason, ""),
171
173
  reversibility: isNonEmptyString(item.reversibility) ? item.reversibility : "none",
172
- undo_path: str(item.undo_path, ""),
174
+ undo_path: normalizedUndoPath ?? str(item.undo_path, ""),
173
175
  confidence: typeof item.confidence === "number" ? item.confidence : 0,
174
176
  needs_human: item.needs_human === true,
175
177
  is_override: item.is_override === true,
@@ -253,7 +255,7 @@ function jointSignature(template, signalsVersion) {
253
255
  nouns: DRIFT_SIGNAL_NOUNS,
254
256
  thresholds: {
255
257
  stampDensityPerKb: DEFAULT_HEALTH_THRESHOLDS.stampDensityPerKb,
256
- minStampBodyChars: 2e3,
258
+ minStampBodyChars: MIN_STAMP_BODY_CHARS,
257
259
  softBodyChars: DEFAULT_HEALTH_THRESHOLDS.softBodyChars,
258
260
  descriptionChars: AUTHORING_DESCRIPTION_BAR,
259
261
  qualityLow: LOW_QUALITY_THRESHOLD,
@@ -321,12 +323,14 @@ async function runMaintain(runtime, options = {}) {
321
323
  ok: false,
322
324
  error: "dsh-evolution prompt bundle integrity check failed; refusing to run maintain"
323
325
  };
326
+ const usageObserved = options.usageObserved ? options.usageObserved() : void 0;
324
327
  const snapshots = await snapshotFromLibrary(runtime.library, {
325
328
  supportFiles: options.supportFiles ? options.supportFiles() : void 0,
326
329
  descriptions: options.descriptions ? options.descriptions() : void 0,
327
330
  quality: options.quality ? options.quality() : void 0,
328
331
  protected: options.protected ? options.protected() : void 0,
329
- catalogInvalid: options.catalogInvalid ? options.catalogInvalid() : void 0
332
+ catalogInvalid: options.catalogInvalid ? options.catalogInvalid() : void 0,
333
+ usageObserved
330
334
  });
331
335
  if (snapshots.length === 0) return {
332
336
  ok: true,
@@ -334,7 +338,7 @@ async function runMaintain(runtime, options = {}) {
334
338
  verdict: "no_issues",
335
339
  text: "Maintenance scan: empty skill library. Nothing to do."
336
340
  };
337
- const { facts, report, signalsVersion, signature } = buildMaintainFacts(snapshots, options.usageObserved ? options.usageObserved() : void 0, options.redact);
341
+ const { facts, report, signalsVersion, signature } = buildMaintainFacts(snapshots, usageObserved, options.redact);
338
342
  const template = renderMaintainTemplate(MAINTAIN_PROMPT, PROMPT_BUNDLE_ID, signalsVersion, signature);
339
343
  const prompt = `${facts}
340
344
 
@@ -342,7 +346,7 @@ async function runMaintain(runtime, options = {}) {
342
346
  const timeoutMs = options.timeoutMs ?? 6e5;
343
347
  const signal = AbortSignal.timeout(timeoutMs);
344
348
  abortSignal = signal;
345
- const agentOptions = { model: options.model ?? "deepseek-v4-pro" };
349
+ const agentOptions = { model: options.model ?? runtime.evolutionPolicy?.get()?.curatorModel ?? "deepseek-v4-pro" };
346
350
  if (options.provider) agentOptions.provider = options.provider;
347
351
  const runResult = await (await runtime.subagents.start("spawn", {
348
352
  label: "dsh-evolution-maintain",
@@ -359,6 +363,11 @@ async function runMaintain(runtime, options = {}) {
359
363
  outputSchema: {
360
364
  type: "object",
361
365
  additionalProperties: false,
366
+ required: [
367
+ "verdict",
368
+ "plan",
369
+ "notes"
370
+ ],
362
371
  properties: {
363
372
  verdict: { type: "string" },
364
373
  plan: {
@@ -366,6 +375,22 @@ async function runMaintain(runtime, options = {}) {
366
375
  items: {
367
376
  type: "object",
368
377
  additionalProperties: false,
378
+ required: [
379
+ "kind",
380
+ "names",
381
+ "rule",
382
+ "evidence",
383
+ "finding",
384
+ "recommendation",
385
+ "semantic_reasoning",
386
+ "impact",
387
+ "impact_reason",
388
+ "reversibility",
389
+ "undo_path",
390
+ "confidence",
391
+ "needs_human",
392
+ "is_override"
393
+ ],
369
394
  properties: {
370
395
  kind: { type: "string" },
371
396
  names: {
@@ -378,6 +403,7 @@ async function runMaintain(runtime, options = {}) {
378
403
  items: {
379
404
  type: "object",
380
405
  additionalProperties: false,
406
+ required: ["signal", "value"],
381
407
  properties: {
382
408
  signal: { type: "string" },
383
409
  value: { type: "string" }
package/lib/tools.js CHANGED
@@ -1,4 +1,4 @@
1
- import { i as snapshotFromLibrary, n as PROBE_SIGNALS, r as computeProbe, t as buildEnrichment } from "./enrichment-ePcRn3GX.js";
1
+ import { i as snapshotFromLibrary, n as PROBE_SIGNALS, r as computeProbe, t as buildEnrichment } from "./enrichment-Di8Y4kev.js";
2
2
  import { SkillLibrary, redactSecrets } from "@lmzhen/dsh-evolution-core";
3
3
  import { defineTool } from "@deepseek-ai/dsh-tools";
4
4
  //#region lib/types/tools.js
@@ -16,7 +16,7 @@ const name = "evolution-maintenance-tools";
16
16
  function apply(ctx, rawConfig = {}) {
17
17
  const config = rawConfig;
18
18
  ctx.inject(["tools"], (toolCtx) => {
19
- toolCtx.tools.register(defineTool({
19
+ toolCtx.get("tools").register(defineTool({
20
20
  name: "maintenance_probe",
21
21
  description: "Read-only deep-dive into maintenance scan signals: library-level group/cluster membership or per-skill detail (line numbers, pointer gaps, narrow shapes, stamp samples). Machine-derived from the same calculators as the facts block — never introduces new evidence ids. Output is JSON detail.",
22
22
  parameters: {
@@ -65,7 +65,8 @@ function apply(ctx, rawConfig = {}) {
65
65
  supportFiles: enrichment.supportFiles,
66
66
  quality: enrichment.quality,
67
67
  protected: enrichment.protected,
68
- catalogInvalid: enrichment.catalogInvalid
68
+ catalogInvalid: enrichment.catalogInvalid,
69
+ usageObserved: enrichment.usageObservedValue
69
70
  }));
70
71
  const redacted = redactSecrets(probe.detail.join("\n"));
71
72
  return {
@@ -26,6 +26,11 @@ export interface SnapshotOptions {
26
26
  protected?: ReadonlyMap<string, string> | undefined;
27
27
  /** Skills whose frontmatter the strict-YAML catalog cannot load (0.3.11). */
28
28
  catalogInvalid?: ReadonlyMap<string, boolean> | undefined;
29
+ /** Usage observation window status (library-wide, applied to every snapshot
30
+ * — the same value the facts block injects). The probe reads it straight off
31
+ * the snapshot (E-36): without it the probe always answered 'unknown' while
32
+ * the facts block reported the enrichment value. */
33
+ usageObserved?: boolean | undefined;
29
34
  }
30
35
  /**
31
36
  * Assemble drift snapshots from a library reader and optional enrichment.
@@ -17,6 +17,15 @@ export interface MaintainRuntime {
17
17
  };
18
18
  /** Parent agent/session handle passed through to the subagent, when available. */
19
19
  parent?: unknown;
20
+ /** Evolution-policy reader for model routing (same source as the curator:
21
+ * `policy.get().curatorModel`). Optional — a missing service falls back to
22
+ * the default model (E-55). `get()` may RESOLVE to undefined (a soft probe
23
+ * can return nothing), hence the type carries it. */
24
+ evolutionPolicy?: {
25
+ get(): {
26
+ curatorModel?: string | undefined;
27
+ } | undefined;
28
+ } | undefined;
20
29
  }
21
30
  export interface MaintainOptions {
22
31
  timeoutMs?: number;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-maintenance",
3
3
  "description": "Deterministic maintenance-scan surface: skill-library snapshot assembly, drift signals and mechanical-facts rendering (design 011) (community build)",
4
- "version": "0.3.17",
4
+ "version": "0.3.18",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -34,7 +34,7 @@
34
34
  ],
35
35
  "license": "MIT",
36
36
  "dependencies": {
37
- "@lmzhen/dsh-evolution-core": "^0.3.17"
37
+ "@lmzhen/dsh-evolution-core": "^0.3.18"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
@@ -42,7 +42,6 @@
42
42
  "@deepseek-ai/dsh-tools": "^0.1.1-rc.2"
43
43
  },
44
44
  "devDependencies": {
45
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
46
- "@deepseek-ai/schemastery": "^3.18.1"
45
+ "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2"
47
46
  }
48
47
  }