@lmzhen/dsh-evolution-feedback 0.3.80 → 0.3.82

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
@@ -30,6 +30,17 @@ const CACHE_VERSION = 2;
30
30
  * live in the ACTIVE log (bounded by `EVENT_LOG_ROTATE_AT`), so the next fold
31
31
  * is complete. Package-private tunable, not a config surface. */
32
32
  const CACHE_SNAP_EVERY = 1024;
33
+ /** C-events-dispatch-1 (v43 audit): the timeline read flags a DROPPED band — an
34
+ * unreadable archive (EISDIR/EACCES) or a body this v1 reader cannot interpret
35
+ * (damaged, or a future version). The boot cache stores the fold BASELINE
36
+ * (`lastSeq`), and `foldWithDelta` only folds `seq > lastSeq`, so folding a
37
+ * truncated read back into it would seal the dropped band against every later
38
+ * boot — the loss becomes unrecoverable without deleting the cache by hand.
39
+ * Both cache writers withhold the refresh when that flag is set (the cache is
40
+ * disposable by contract, so the last COMPLETE fold simply stays) and say so
41
+ * through this ONE message, deduped to a single warn per process (V5-32) because
42
+ * `refold()` re-runs `restore()` before every quality push. */
43
+ const TRUNCATED_TIMELINE_WARN = "evolution-feedback: the evolution event timeline is TRUNCATED - at least one segment (an unreadable archive, or a body this v1 reader cannot interpret, e.g. a future version) was dropped, so the folded counts are incomplete; the boot cache was NOT updated (a truncated lastSeq would seal the missing band against every later boot)";
33
44
  var EvolutionFeedback = class EvolutionFeedback {
34
45
  state = {
35
46
  skills: {},
@@ -74,6 +85,21 @@ var EvolutionFeedback = class EvolutionFeedback {
74
85
  attachIo(io) {
75
86
  this.io = io;
76
87
  }
88
+ /** V5-32 posture applied to the read side (C-events-dispatch-1, v43): an
89
+ * event a repeated read keeps reporting must not warn on each read —
90
+ * `refold()` calls `restore()` before every quality push. Shares the bounded
91
+ * `warnedMessages` set (and its FIFO eviction) with the append-failure warn.
92
+ * @param message - the warn text; identical text warns once per process.
93
+ */
94
+ warnOnce(message) {
95
+ if (this.warnedMessages.has(message)) return;
96
+ if (this.warnedMessages.size >= this.WARNED_CAP) {
97
+ const oldest = this.warnedMessages.values().next().value;
98
+ if (oldest !== void 0) this.warnedMessages.delete(oldest);
99
+ }
100
+ this.warnedMessages.add(message);
101
+ this.warn(message);
102
+ }
77
103
  /** Durable-note map key (V4-41): a target shares one record per mode. */
78
104
  noteKey(mode, target) {
79
105
  return `${mode}\u0000${target}`;
@@ -99,7 +125,9 @@ var EvolutionFeedback = class EvolutionFeedback {
99
125
  }
100
126
  const initial = await readEvolutionTimeline(io, eventsPath, archiveNames);
101
127
  const lateArchives = (await listEventArchives(io, eventsPath)).filter((name) => !archiveNames.includes(name));
102
- const events = lateArchives.length === 0 ? initial.events : (await readEvolutionTimeline(io, eventsPath, [...archiveNames, ...lateArchives])).events;
128
+ const later = lateArchives.length === 0 ? null : await readEvolutionTimeline(io, eventsPath, [...archiveNames, ...lateArchives]);
129
+ const events = later?.events ?? initial.events;
130
+ const truncated = initial.malformed || later?.malformed === true;
103
131
  const cache = parseCache(rawCache, this.warn);
104
132
  const maxSeq = events.reduce((max, event) => Math.max(max, event.seq), 0);
105
133
  const floor = events[0]?.seq ?? 0;
@@ -129,7 +157,8 @@ var EvolutionFeedback = class EvolutionFeedback {
129
157
  if (oldest === void 0) break;
130
158
  this.durableNote.delete(oldest);
131
159
  }
132
- if (maxSeq > 0 && (!cache || cache.lastSeq < maxSeq)) try {
160
+ if (truncated) this.warnOnce(TRUNCATED_TIMELINE_WARN);
161
+ else if (maxSeq > 0 && (!cache || cache.lastSeq < maxSeq)) try {
133
162
  await io.writeText(path, JSON.stringify({
134
163
  version: CACHE_VERSION,
135
164
  lastSeq: maxSeq,
@@ -255,7 +284,11 @@ var EvolutionFeedback = class EvolutionFeedback {
255
284
  const recordIo = this.io;
256
285
  if (!path || !eventsPath || !recordIo) return;
257
286
  try {
258
- const { events } = await readEvolutionTimeline(recordIo, eventsPath);
287
+ const { events, malformed } = await readEvolutionTimeline(recordIo, eventsPath);
288
+ if (malformed) {
289
+ this.warnOnce(TRUNCATED_TIMELINE_WARN);
290
+ return;
291
+ }
259
292
  const maxSeq = events.reduce((max, event) => Math.max(max, event.seq), 0);
260
293
  if (maxSeq === 0) return;
261
294
  let rawCache;
@@ -71,6 +71,13 @@ export declare class EvolutionFeedback {
71
71
  constructor(io?: IoLike, home?: string, pathOverride?: string, warn?: (message: string) => void);
72
72
  /** Bind the evolution IO backend after construction (S6.4 deferred binding). */
73
73
  attachIo(io: IoLike): void;
74
+ /** V5-32 posture applied to the read side (C-events-dispatch-1, v43): an
75
+ * event a repeated read keeps reporting must not warn on each read —
76
+ * `refold()` calls `restore()` before every quality push. Shares the bounded
77
+ * `warnedMessages` set (and its FIFO eviction) with the append-failure warn.
78
+ * @param message - the warn text; identical text warns once per process.
79
+ */
80
+ private warnOnce;
74
81
  /** Durable-note map key (V4-41): a target shares one record per mode. */
75
82
  private noteKey;
76
83
  private mutate;
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.80",
4
+ "version": "0.3.82",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -27,16 +27,16 @@
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
29
  "@deepseek-ai/schemastery": "^3.18.1",
30
- "@lmzhen/dsh-evolution-core": "^0.3.80"
30
+ "@lmzhen/dsh-evolution-core": "^0.3.82"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@deepseek-ai/cordis": "^4.0.1",
34
- "@lmzhen/dsh-evolution-io": "^0.3.80",
35
- "@lmzhen/dsh-skill-usage": "^0.3.80"
34
+ "@lmzhen/dsh-evolution-io": "^0.3.82",
35
+ "@lmzhen/dsh-skill-usage": "^0.3.82"
36
36
  },
37
37
  "devDependencies": {
38
- "@lmzhen/dsh-evolution-io": "^0.3.80",
39
- "@lmzhen/dsh-evolution-io-node": "^0.3.80",
40
- "@lmzhen/dsh-skill-usage": "^0.3.80"
38
+ "@lmzhen/dsh-evolution-io": "^0.3.82",
39
+ "@lmzhen/dsh-evolution-io-node": "^0.3.82",
40
+ "@lmzhen/dsh-skill-usage": "^0.3.82"
41
41
  }
42
42
  }