@lmzhen/dsh-evolution-replay 0.18.0 → 0.19.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/README.md CHANGED
@@ -13,6 +13,6 @@ replay`.
13
13
  ## Known limitations
14
14
 
15
15
  - No known durable consumer gaps at this time. Runtime contracts are covered by package and boundary tests.
16
- - **The activity-sidecar verdict is per read (`sourceCorrupt`).** When `apply()` meets bytes this build cannot read as the current format, every `compare()` result carries `sourceCorrupt: true` and its report states the leaderboard is NOT the recorded history (an empty list does not mean nothing happened); a later successful read clears the qualification (a repaired sidecar plus an io reload (HMR / plugin restart)), while the corruption stays observable through the one warn `apply()` emits at mark time.
16
+ - **The activity-sidecar verdict is per read, and an unanswered read is its own state.** When `apply()` meets bytes this build cannot read as the current format, every `compare()` result carries `sourceCorrupt: true` and its report states the leaderboard is NOT the recorded history (an empty list does not mean nothing happened); a later successful read clears the qualification (a repaired sidecar plus an io reload (HMR / plugin restart)), while the corruption stays observable through the one warn `apply()` emits at mark time. A read that **THREW** (the io failed, or no state could be produced at all) is neither verdict: `compare()` then carries `sourceReadFailure: <message>` and its report says the read FAILED — never "nothing was recorded". Any ANSWERED read (clean or corrupt) retires that failure, so both qualifications describe the current read rather than latching.
17
17
 
18
18
  **Runtime invariant:** No companion is published. The platform auto-assembles nothing and the family mounts no `<pkg>/invariant` cordis row, so a companion here would never execute (v37 S2.1 / I-3).
package/lib/index.js CHANGED
@@ -101,6 +101,12 @@ var EvolutionReplayDriver = class EvolutionReplayDriver {
101
101
  * clears it on a later clean read, so the qualification reflects the present
102
102
  * instead of latching for the driver lifetime. */
103
103
  sourceCorrupt = false;
104
+ /** 0.18.1 (S3): the backfill READ itself failed — held as its message. A
105
+ * thrown read used to skip both markers, so `sourceCorrupt` kept whatever
106
+ * the previous read said and a failed read presented as a clean one. Cleared
107
+ * by any ANSWERED read; never sets `sourceCorrupt` (a failed read is not
108
+ * evidence that the bytes are corrupt). */
109
+ sourceReadFailure = null;
104
110
  /** V26-05 (v25): plan ids recorded live BEFORE the backfill settled. A
105
111
  * plan-applied landing inside the one-shot `loadActivityState` read window is
106
112
  * recorded live AND persisted into the sidecar the backfill is reading —
@@ -203,6 +209,7 @@ var EvolutionReplayDriver = class EvolutionReplayDriver {
203
209
  */
204
210
  markSourceUnreadable() {
205
211
  this.sourceCorrupt = true;
212
+ this.sourceReadFailure = null;
206
213
  }
207
214
  /**
208
215
  * PLAN S5.1 (2026-09-16, audit P2-18): the counterpart clear — `apply()` calls
@@ -218,17 +225,30 @@ var EvolutionReplayDriver = class EvolutionReplayDriver {
218
225
  */
219
226
  markSourceReadable() {
220
227
  this.sourceCorrupt = false;
228
+ this.sourceReadFailure = null;
229
+ }
230
+ /**
231
+ * 0.18.1 (S3): the backfill read THREW (io error, or a state the reader could
232
+ * not produce at all). Neither verdict applies — the bytes were never judged.
233
+ * Before this the throw skipped both markers and `compare()` returned an
234
+ * unqualified leaderboard, presenting a failed read as an answered one.
235
+ * @param reason - the failure's message, as thrown.
236
+ */
237
+ markSourceReadFailed(reason) {
238
+ this.sourceReadFailure = reason;
221
239
  }
222
240
  compare(weights = this.weights) {
223
241
  const result = comparePlans([...this.plans], clampReplayWeights(weights, this.weights));
224
- if (!this.sourceCorrupt) return {
242
+ const failure = this.sourceReadFailure === null ? "" : `The activity sidecar read FAILED (${this.sourceReadFailure}) — this leaderboard carries no history from the sidecar, and a failed read is NOT "nothing was recorded".\n`;
243
+ if (!this.sourceCorrupt && this.sourceReadFailure === null) return {
225
244
  ...result,
226
245
  sourceCorrupt: false
227
246
  };
228
247
  return {
229
248
  ...result,
230
- sourceCorrupt: true,
231
- report: "The activity sidecar could not be read as an activity envelope (unparsable bytes, or no items array) — this leaderboard is NOT the recorded history, and an empty list does not mean nothing happened.\n" + result.report
249
+ sourceCorrupt: this.sourceCorrupt,
250
+ ...this.sourceReadFailure === null ? {} : { sourceReadFailure: this.sourceReadFailure },
251
+ report: failure + (this.sourceCorrupt ? "The activity sidecar could not be read as an activity envelope (unparsable bytes, or no items array) — this leaderboard is NOT the recorded history, and an empty list does not mean nothing happened.\n" : "") + result.report
232
252
  };
233
253
  }
234
254
  };
@@ -253,8 +273,12 @@ function apply(ctx, rawConfig = {}) {
253
273
  driver.markSourceReadable();
254
274
  }
255
275
  driver.backfill(loaded.records);
276
+ }).then(void 0, (error) => {
277
+ const reason = error instanceof Error ? error.message : String(error);
278
+ driver.markSourceReadFailed(reason);
279
+ ioCtx.logger.warn(`evolution-replay: activity sidecar backfill skipped (${reason}) — this leaderboard carries no history from the sidecar, and a failed read is not "nothing was recorded"`);
256
280
  }).catch((error) => {
257
- ioCtx.logger.warn(`evolution-replay: activity sidecar backfill skipped (${error instanceof Error ? error.message : String(error)})`);
281
+ ioCtx.logger.warn(`evolution-replay: activity sidecar backfill failed after a successful read (${error instanceof Error ? error.message : String(error)}) — the source verdict is unaffected`);
258
282
  });
259
283
  });
260
284
  }
@@ -51,6 +51,12 @@ export interface ReplayResult {
51
51
  * was repaired and an io reload re-read it) clears the qualification —
52
52
  * it never outlives the condition it describes. */
53
53
  sourceCorrupt?: boolean | undefined;
54
+ /** 0.18.1 (S3): the most recent backfill READ threw (io error, or a state the
55
+ * reader could not produce at all) — no verdict was reached, which is a
56
+ * different fact from `sourceCorrupt` (the bytes WERE read and are not an
57
+ * activity envelope). Carries the failure's message; a later ANSWERED read
58
+ * clears it (see {@link EvolutionReplayDriver.markSourceReadFailed}). */
59
+ sourceReadFailure?: string | undefined;
54
60
  }
55
61
  export interface ReplayWeights {
56
62
  accepted: number;
@@ -90,6 +96,12 @@ export declare class EvolutionReplayDriver {
90
96
  * clears it on a later clean read, so the qualification reflects the present
91
97
  * instead of latching for the driver lifetime. */
92
98
  private sourceCorrupt;
99
+ /** 0.18.1 (S3): the backfill READ itself failed — held as its message. A
100
+ * thrown read used to skip both markers, so `sourceCorrupt` kept whatever
101
+ * the previous read said and a failed read presented as a clean one. Cleared
102
+ * by any ANSWERED read; never sets `sourceCorrupt` (a failed read is not
103
+ * evidence that the bytes are corrupt). */
104
+ private sourceReadFailure;
93
105
  /** V26-05 (v25): plan ids recorded live BEFORE the backfill settled. A
94
106
  * plan-applied landing inside the one-shot `loadActivityState` read window is
95
107
  * recorded live AND persisted into the sidecar the backfill is reading —
@@ -155,6 +167,14 @@ export declare class EvolutionReplayDriver {
155
167
  * `apply()` emits at mark time.
156
168
  */
157
169
  markSourceReadable(): void;
170
+ /**
171
+ * 0.18.1 (S3): the backfill read THREW (io error, or a state the reader could
172
+ * not produce at all). Neither verdict applies — the bytes were never judged.
173
+ * Before this the throw skipped both markers and `compare()` returned an
174
+ * unqualified leaderboard, presenting a failed read as an answered one.
175
+ * @param reason - the failure's message, as thrown.
176
+ */
177
+ markSourceReadFailed(reason: string): void;
158
178
  compare(weights?: ReplayWeights): ReplayResult;
159
179
  }
160
180
  export declare const name = "evolution-replay";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-replay",
3
3
  "description": "Replay/A-B evaluation primitives for evolution plans (community build)",
4
- "version": "0.18.0",
4
+ "version": "0.19.0",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -27,14 +27,14 @@
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
29
  "@deepseek-ai/schemastery": "^3.18.4",
30
- "@lmzhen/dsh-evolution-activity": "^0.18.0",
31
- "@lmzhen/dsh-evolution-core": "^0.18.0"
30
+ "@lmzhen/dsh-evolution-activity": "^0.19.0",
31
+ "@lmzhen/dsh-evolution-core": "^0.19.0"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "@deepseek-ai/cordis": "^4.0.1"
35
35
  },
36
36
  "devDependencies": {
37
- "@lmzhen/dsh-evolution-activity": "^0.18.0",
38
- "@lmzhen/dsh-evolution-core": "^0.18.0"
37
+ "@lmzhen/dsh-evolution-activity": "^0.19.0",
38
+ "@lmzhen/dsh-evolution-core": "^0.19.0"
39
39
  }
40
40
  }