@lmzhen/dsh-evolution-feedback 0.3.71 → 0.3.72

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/lib/index.js CHANGED
@@ -223,6 +223,14 @@ var EvolutionFeedback = class EvolutionFeedback {
223
223
  if (total === 0) return 0;
224
224
  return (record.positive - record.negative) / total;
225
225
  }
226
+ /**
227
+ * Deep-copy view of the live aggregate.
228
+ *
229
+ * OPT-26 (2026-09): `@internal` — test-support API, NO production consumer
230
+ * in the family (mirrors the explicit posture of evolution-replay's
231
+ * `plansSnapshot()`). External consumers should read the rendered
232
+ * score/warn surfaces instead of relying on this shape.
233
+ */
226
234
  snapshot() {
227
235
  const copyRecords = (table) => {
228
236
  const out = {};
@@ -373,12 +381,28 @@ function parseCache(raw, warn = () => {}) {
373
381
  /** Per-record numeric-domain validation (S6.4): a record whose `positive` or
374
382
  * `negative` is not a finite number >= 0, or whose `lastNote` is not a string,
375
383
  * would fold as NaN into the usage aggregate — skip it with a warn instead of
376
- * corrupting the state. A record with no valid count is dropped entirely. */
384
+ * corrupting the state. A record with no valid count is dropped entirely.
385
+ * OPT-25 (2026-09): a count ABOVE `MAX_MIGRATED_EVENTS_PER_RECORD` is now
386
+ * CLAMPED here with a warn, symmetric with the migration reader's clamp (same
387
+ * file, same threat model — V24-05 covered the migration side only). Before,
388
+ * a poisoned boot-cache count folded as-is into every downstream decision;
389
+ * the clamp bounds the skew to the same ceiling migration accepts. */
377
390
  function sanitizeCacheRecords(input, kind, warn) {
378
391
  const out = {};
379
392
  for (const [target, value] of Object.entries(input)) {
380
393
  const record = sanitizeFeedbackRecord(value, target, kind, warn);
381
- if (record) out[target] = record;
394
+ if (record) {
395
+ let clamped;
396
+ if (record.positive > MAX_MIGRATED_EVENTS_PER_RECORD || record.negative > MAX_MIGRATED_EVENTS_PER_RECORD) {
397
+ warn(`evolution-feedback: cache record for ${kind} "${target}" exceeds the clamp ceiling (${MAX_MIGRATED_EVENTS_PER_RECORD}) — clamping like the migration reader`);
398
+ clamped = {
399
+ positive: Math.min(record.positive, MAX_MIGRATED_EVENTS_PER_RECORD),
400
+ negative: Math.min(record.negative, MAX_MIGRATED_EVENTS_PER_RECORD),
401
+ ...record.lastNote !== void 0 ? { lastNote: record.lastNote } : {}
402
+ };
403
+ }
404
+ out[target] = clamped ?? record;
405
+ }
382
406
  }
383
407
  return out;
384
408
  }
@@ -98,6 +98,14 @@ export declare class EvolutionFeedback {
98
98
  refold(): Promise<void>;
99
99
  record(target: string, rating: 'positive' | 'negative', note?: string, kind?: 'skill' | 'session'): void;
100
100
  score(target: string, kind?: 'skill' | 'session'): number;
101
+ /**
102
+ * Deep-copy view of the live aggregate.
103
+ *
104
+ * OPT-26 (2026-09): `@internal` — test-support API, NO production consumer
105
+ * in the family (mirrors the explicit posture of evolution-replay's
106
+ * `plansSnapshot()`). External consumers should read the rendered
107
+ * score/warn surfaces instead of relying on this shape.
108
+ */
101
109
  snapshot(): FeedbackState;
102
110
  /** Await the pending record-task chain (unload safety; rc.66). */
103
111
  waitIdle(): Promise<unknown>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-feedback",
3
3
  "description": "Feedback-to-quality scoring for self-evolution (community build)",
4
- "version": "0.3.71",
4
+ "version": "0.3.72",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -31,18 +31,18 @@
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
33
  "@deepseek-ai/schemastery": "^3.18.1",
34
- "@lmzhen/dsh-evolution-core": "^0.3.71"
34
+ "@lmzhen/dsh-evolution-core": "^0.3.72"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "@deepseek-ai/dsh-invariants": "^0.1.5-rc.2",
38
38
  "@deepseek-ai/cordis": "^4.0.1",
39
- "@lmzhen/dsh-evolution-io": "^0.3.71",
40
- "@lmzhen/dsh-skill-usage": "^0.3.71"
39
+ "@lmzhen/dsh-evolution-io": "^0.3.72",
40
+ "@lmzhen/dsh-skill-usage": "^0.3.72"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@deepseek-ai/dsh-invariants": "^0.1.5-rc.2",
44
- "@lmzhen/dsh-evolution-io": "^0.3.71",
45
- "@lmzhen/dsh-evolution-io-node": "^0.3.71",
46
- "@lmzhen/dsh-skill-usage": "^0.3.71"
44
+ "@lmzhen/dsh-evolution-io": "^0.3.72",
45
+ "@lmzhen/dsh-evolution-io-node": "^0.3.72",
46
+ "@lmzhen/dsh-skill-usage": "^0.3.72"
47
47
  }
48
48
  }