@lmzhen/dsh-evolution-replay 0.3.66 → 0.3.67

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
@@ -1,5 +1,6 @@
1
1
  import z from "@deepseek-ai/schemastery";
2
- import { clampedNumber } from "@lmzhen/dsh-evolution-core";
2
+ import { clampedNumber, evolutionHome, evolutionIoAdapter } from "@lmzhen/dsh-evolution-core";
3
+ import { loadActivity } from "@lmzhen/dsh-evolution-activity";
3
4
  //#region lib/types/index.js
4
5
  /**
5
6
  * Replay/A-B evaluation for evolution plans.
@@ -8,9 +9,16 @@ import { clampedNumber } from "@lmzhen/dsh-evolution-core";
8
9
  * driver records every `evolution/plan-applied` process event (payload v2,
9
10
  * with sessionId) into an in-memory leaderboard and exposes `/evolution
10
11
  * replay` (via the `/evolution` command family) for comparison, so a human can
11
- * A/B review policy/prompt changes against real plan outcomes. Durability
12
- * across restarts is the evolution-activity store's job; this leaderboard is
13
- * deliberately in-memory.
12
+ * A/B review policy/prompt changes against real plan outcomes.
13
+ *
14
+ * V24-13 (v24): the in-memory leaderboard is BACKFILLED at mount from the
15
+ * `evolution-activity` sidecar (`$DSH_HOME/evolution/activity.json`) — the
16
+ * header previously claimed "durability across restarts is the
17
+ * evolution-activity store's job" while nothing ever read the sidecar, so a
18
+ * restart emptied `/evolution replay` and the persisted history sat unread.
19
+ * Records are fed in sidecar order (oldest first); the driver's `maxPlans`
20
+ * window keeps the newest. Best-effort: a missing/unreadable sidecar starts
21
+ * the leaderboard empty, exactly as before.
14
22
  * @module @lmzhen/dsh-evolution-replay
15
23
  */
16
24
  const DEFAULT_WEIGHTS = {
@@ -83,6 +91,13 @@ function comparePlans(plans, weights = DEFAULT_WEIGHTS) {
83
91
  var EvolutionReplayDriver = class {
84
92
  plans = [];
85
93
  maxPlans;
94
+ /** V25-01 (v25): backfill-once latch — see {@link EvolutionReplayDriver.backfill}. */
95
+ backfilled = false;
96
+ /** V26-05 (v25): plan ids recorded live BEFORE the backfill settled. A
97
+ * plan-applied landing inside the one-shot `loadActivity` read window is
98
+ * recorded live AND persisted into the sidecar the backfill is reading —
99
+ * `backfill()` drops those ids so the event is not counted twice. */
100
+ preBackfillIds = /* @__PURE__ */ new Set();
86
101
  weights;
87
102
  constructor(config = {}, warn = () => {}) {
88
103
  const maxPlans = clampedNumber(config.maxPlans ?? 50, 50, { min: 1 });
@@ -96,6 +111,7 @@ var EvolutionReplayDriver = class {
96
111
  }
97
112
  }
98
113
  record(plan) {
114
+ if (!this.backfilled) this.preBackfillIds.add(plan.planId);
99
115
  const count = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
100
116
  const countOr = (value, fallback) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : fallback;
101
117
  const memoryApplied = count(plan.memoryApplied);
@@ -114,6 +130,22 @@ var EvolutionReplayDriver = class {
114
130
  if (this.plans.length > this.maxPlans) this.plans.shift();
115
131
  }
116
132
  /**
133
+ * V25-01 (v25): backfill the leaderboard from the activity sidecar, ONCE
134
+ * per driver instance. The inject callback that loads the sidecar re-runs
135
+ * whenever the `evolutionIo` dependency is replaced (cordis derived-fiber
136
+ * reload — plugin restart / HMR) while THIS driver survives at the apply
137
+ * scope, so the dedupe guard must live here, not in the callback: without
138
+ * it, every io reload doubled the leaderboard entries.
139
+ * @param items - activity records in sidecar order (oldest first).
140
+ */
141
+ backfill(items) {
142
+ if (this.backfilled) return;
143
+ this.backfilled = true;
144
+ const fresh = items.filter((item) => !this.preBackfillIds.has(item.planId));
145
+ this.preBackfillIds.clear();
146
+ for (const item of fresh) this.record(item);
147
+ }
148
+ /**
117
149
  * F-11: test-support API — no production consumer reads the raw
118
150
  * plan list (the leaderboard path goes through `compare()`); tests use this
119
151
  * as a read/inspection window. Kept by declaration (same posture as the
@@ -137,6 +169,15 @@ function apply(ctx, rawConfig = {}) {
137
169
  ctx.on("evolution/plan-applied", (event) => {
138
170
  driver.record(event);
139
171
  });
172
+ ctx.inject(["evolutionIo"], (ioCtx) => {
173
+ const ioRegistry = ioCtx.evolutionIo;
174
+ const io = evolutionIoAdapter(() => ioRegistry.provider());
175
+ loadActivity(evolutionHome(), io).then((items) => {
176
+ driver.backfill(items);
177
+ }).catch((error) => {
178
+ ioCtx.logger.warn(`evolution-replay: activity sidecar backfill skipped (${error instanceof Error ? error.message : String(error)})`);
179
+ });
180
+ });
140
181
  }
141
182
  //#endregion
142
183
  export { Config, DEFAULT_WEIGHTS, EvolutionReplayDriver, apply, clampReplayWeights, comparePlans, name };
@@ -5,9 +5,16 @@
5
5
  * driver records every `evolution/plan-applied` process event (payload v2,
6
6
  * with sessionId) into an in-memory leaderboard and exposes `/evolution
7
7
  * replay` (via the `/evolution` command family) for comparison, so a human can
8
- * A/B review policy/prompt changes against real plan outcomes. Durability
9
- * across restarts is the evolution-activity store's job; this leaderboard is
10
- * deliberately in-memory.
8
+ * A/B review policy/prompt changes against real plan outcomes.
9
+ *
10
+ * V24-13 (v24): the in-memory leaderboard is BACKFILLED at mount from the
11
+ * `evolution-activity` sidecar (`$DSH_HOME/evolution/activity.json`) — the
12
+ * header previously claimed "durability across restarts is the
13
+ * evolution-activity store's job" while nothing ever read the sidecar, so a
14
+ * restart emptied `/evolution replay` and the persisted history sat unread.
15
+ * Records are fed in sidecar order (oldest first); the driver's `maxPlans`
16
+ * window keeps the newest. Best-effort: a missing/unreadable sidecar starts
17
+ * the leaderboard empty, exactly as before.
11
18
  * @module @lmzhen/dsh-evolution-replay
12
19
  */
13
20
  import type { Context } from '@deepseek-ai/cordis';
@@ -61,9 +68,26 @@ declare module '@deepseek-ai/cordis' {
61
68
  export declare class EvolutionReplayDriver {
62
69
  private readonly plans;
63
70
  private readonly maxPlans;
71
+ /** V25-01 (v25): backfill-once latch — see {@link EvolutionReplayDriver.backfill}. */
72
+ private backfilled;
73
+ /** V26-05 (v25): plan ids recorded live BEFORE the backfill settled. A
74
+ * plan-applied landing inside the one-shot `loadActivity` read window is
75
+ * recorded live AND persisted into the sidecar the backfill is reading —
76
+ * `backfill()` drops those ids so the event is not counted twice. */
77
+ private readonly preBackfillIds;
64
78
  private readonly weights;
65
79
  constructor(config?: Config, warn?: (message: string) => void);
66
80
  record(plan: EvolutionPlanAppliedEvent): void;
81
+ /**
82
+ * V25-01 (v25): backfill the leaderboard from the activity sidecar, ONCE
83
+ * per driver instance. The inject callback that loads the sidecar re-runs
84
+ * whenever the `evolutionIo` dependency is replaced (cordis derived-fiber
85
+ * reload — plugin restart / HMR) while THIS driver survives at the apply
86
+ * scope, so the dedupe guard must live here, not in the callback: without
87
+ * it, every io reload doubled the leaderboard entries.
88
+ * @param items - activity records in sidecar order (oldest first).
89
+ */
90
+ backfill(items: ReadonlyArray<import('@lmzhen/dsh-evolution-activity').EvolutionActivityRecord>): void;
67
91
  /**
68
92
  * F-11: test-support API — no production consumer reads the raw
69
93
  * plan list (the leaderboard path goes through `compare()`); tests use this
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.3.66",
4
+ "version": "0.3.67",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -31,7 +31,8 @@
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
33
  "@deepseek-ai/schemastery": "^3.18.1",
34
- "@lmzhen/dsh-evolution-core": "^0.3.66"
34
+ "@lmzhen/dsh-evolution-activity": "^0.3.67",
35
+ "@lmzhen/dsh-evolution-core": "^0.3.67"
35
36
  },
36
37
  "peerDependencies": {
37
38
  "@deepseek-ai/cordis": "^4.0.1",
@@ -39,6 +40,7 @@
39
40
  },
40
41
  "devDependencies": {
41
42
  "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
42
- "@lmzhen/dsh-evolution-core": "^0.3.66"
43
+ "@lmzhen/dsh-evolution-activity": "^0.3.67",
44
+ "@lmzhen/dsh-evolution-core": "^0.3.67"
43
45
  }
44
46
  }