@lmzhen/dsh-evolution-activity 0.3.81 → 0.3.83

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
@@ -1,4 +1,4 @@
1
- # @deepseek-ai/dsh-evolution-activity
1
+ # @lmzhen/dsh-evolution-activity
2
2
 
3
3
  Durable activity store for self-evolution plan outcomes
4
4
 
@@ -12,7 +12,7 @@ Subscribes to the process event `evolution/plan-applied` (payload v2, with sessi
12
12
 
13
13
  #### What the model sees
14
14
 
15
- `@deepseek-ai/dsh-evolution-activity` registers no direct prompt or tool schema itself. Model-visible effects are owned by the packages that consume this service.
15
+ `@lmzhen/dsh-evolution-activity` registers no direct prompt or tool schema itself. Model-visible effects are owned by the packages that consume this service.
16
16
 
17
17
  #### Token effect
18
18
 
package/lib/index.js CHANGED
@@ -69,24 +69,58 @@ function parseActivityContent(raw) {
69
69
  }
70
70
  }
71
71
  /**
72
- * H-06: the read barrier over the sidecar. V24-13 (v24): `loadActivity` now
73
- * HAS a production consumer — `evolution-replay` backfills its `/evolution
74
- * replay` leaderboard from this store at mount (the two packages'
75
- * "persistence is the activity store's job" contract is actually wired
76
- * through this call). The single-writer rule is unchanged: `apply()`'s
77
- * transact listener remains the only WRITE path.
72
+ * H-06: the read barrier over the sidecar, with the corruption verdict.
73
+ *
74
+ * @param root - the evolution state root (the sidecar lives under it).
75
+ * @param io - the IO provider to read through.
76
+ * @returns the parsed records and whether the bytes were readable.
77
+ */
78
+ async function loadActivityState(root, io) {
79
+ const raw = await io.readText(activityFile(root));
80
+ return {
81
+ records: parseActivityContent(raw),
82
+ corrupt: isCorruptActivity(raw)
83
+ };
84
+ }
85
+ /**
86
+ * H-06: the read barrier over the sidecar — records only, no corruption
87
+ * verdict. @internal Test-only: no production caller (verified by grep
88
+ * 2026-09-16); replay consumes {@link loadActivityState} — the V24-13 claim
89
+ * that `loadActivity` "HAS a production consumer — evolution-replay" was
90
+ * inaccurate (PLAN S5.2). Kept exported so the published surface does not
91
+ * break. The single-writer rule is unchanged: `apply()`'s transact listener
92
+ * remains the only WRITE path. Consumers that must not read an unreadable
93
+ * sidecar as "no history" use {@link loadActivityState}.
78
94
  */
79
95
  async function loadActivity(root, io) {
80
- return parseActivityContent(await io.readText(activityFile(root)));
96
+ return (await loadActivityState(root, io)).records;
81
97
  }
82
98
  /** True when bytes exist but are not a readable activity envelope: unparsable
83
99
  * JSON, or a missing `items` array (a scalar/array/other-shaped file). A missing
84
100
  * file (null) is NOT corruption — it is a first write. */
101
+ /**
102
+ * v43 audit (H-3 / J-6 / FLOW6-6): an UNSUPPORTED version is corrupt for this
103
+ * writer. The read side has always ignored `version` and taken `items` as-is,
104
+ * so a future-version sidecar used to fold fine and then be rewritten as
105
+ * `version: ACTIVITY_FILE_VERSION` on the next append — a silent downgrade that
106
+ * destroyed whatever the newer format carried. This guard is the write side's
107
+ * half of the pair: unknown version ⇒ the original bytes are quarantined (the
108
+ * existing `.corrupt` path) before a current-version file replaces them, which
109
+ * is the posture `evolution-events` already takes for the same shape.
110
+ * Residual, closed by FLOW6-6: `parseActivityContent` still answers `[]` for
111
+ * such a file, but {@link loadActivityState} now carries the verdict beside the
112
+ * records, so a read-only consumer can distinguish "newer format" from "no
113
+ * history" instead of presenting a partial view as the recorded truth.
114
+ * @internal Exported for this package's own tests (siblings `parseActivityContent`
115
+ * and `serializeActivity` are exported for the same reason).
116
+ */
85
117
  function isCorruptActivity(raw) {
86
118
  if (raw === null) return false;
87
119
  try {
88
120
  const parsed = JSON.parse(raw);
89
- return typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.items);
121
+ if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.items)) return true;
122
+ const version = parsed.version;
123
+ return version !== void 0 && version !== 2;
90
124
  } catch {
91
125
  return true;
92
126
  }
@@ -128,4 +162,4 @@ function apply(ctx, rawConfig = {}) {
128
162
  });
129
163
  }
130
164
  //#endregion
131
- export { ACTIVITY_FILE_VERSION, Config, DEFAULT_MAX_ITEMS, activityFile, apply, applyActivityEvent, loadActivity, name, parseActivityContent, serializeActivity };
165
+ export { ACTIVITY_FILE_VERSION, Config, DEFAULT_MAX_ITEMS, activityFile, apply, applyActivityEvent, isCorruptActivity, loadActivity, loadActivityState, name, parseActivityContent, serializeActivity };
@@ -53,14 +53,58 @@ export declare function applyActivityEvent(items: EvolutionActivityRecord[], eve
53
53
  export declare function serializeActivity(items: EvolutionActivityRecord[]): string;
54
54
  export declare function parseActivityContent(raw: string | null): EvolutionActivityRecord[];
55
55
  /**
56
- * H-06: the read barrier over the sidecar. V24-13 (v24): `loadActivity` now
57
- * HAS a production consumer — `evolution-replay` backfills its `/evolution
58
- * replay` leaderboard from this store at mount (the two packages'
59
- * "persistence is the activity store's job" contract is actually wired
60
- * through this call). The single-writer rule is unchanged: `apply()`'s
61
- * transact listener remains the only WRITE path.
56
+ * FLOW6-6 (v43, the read side of H-3): the sidecar's records PLUS whether the
57
+ * bytes could be read as THIS format. `parseActivityContent` answers `[]` for
58
+ * a future-version or unparsable file, so a read-only consumer could not tell
59
+ * "newer format" from "no history" and presented a partial/empty view as the
60
+ * recorded truth. The write side already quarantines those bytes
61
+ * ({@link isCorruptActivity}); this is the channel the readers were missing.
62
+ */
63
+ export interface ActivityLoad {
64
+ records: EvolutionActivityRecord[];
65
+ /** Bytes exist but are not a readable current-version envelope. A MISSING
66
+ * file is not corruption — it is a first write. */
67
+ corrupt: boolean;
68
+ }
69
+ /**
70
+ * H-06: the read barrier over the sidecar, with the corruption verdict.
71
+ *
72
+ * @param root - the evolution state root (the sidecar lives under it).
73
+ * @param io - the IO provider to read through.
74
+ * @returns the parsed records and whether the bytes were readable.
75
+ */
76
+ export declare function loadActivityState(root: string, io: EvolutionIoLike): Promise<ActivityLoad>;
77
+ /**
78
+ * H-06: the read barrier over the sidecar — records only, no corruption
79
+ * verdict. @internal Test-only: no production caller (verified by grep
80
+ * 2026-09-16); replay consumes {@link loadActivityState} — the V24-13 claim
81
+ * that `loadActivity` "HAS a production consumer — evolution-replay" was
82
+ * inaccurate (PLAN S5.2). Kept exported so the published surface does not
83
+ * break. The single-writer rule is unchanged: `apply()`'s transact listener
84
+ * remains the only WRITE path. Consumers that must not read an unreadable
85
+ * sidecar as "no history" use {@link loadActivityState}.
62
86
  */
63
87
  export declare function loadActivity(root: string, io: EvolutionIoLike): Promise<EvolutionActivityRecord[]>;
88
+ /** True when bytes exist but are not a readable activity envelope: unparsable
89
+ * JSON, or a missing `items` array (a scalar/array/other-shaped file). A missing
90
+ * file (null) is NOT corruption — it is a first write. */
91
+ /**
92
+ * v43 audit (H-3 / J-6 / FLOW6-6): an UNSUPPORTED version is corrupt for this
93
+ * writer. The read side has always ignored `version` and taken `items` as-is,
94
+ * so a future-version sidecar used to fold fine and then be rewritten as
95
+ * `version: ACTIVITY_FILE_VERSION` on the next append — a silent downgrade that
96
+ * destroyed whatever the newer format carried. This guard is the write side's
97
+ * half of the pair: unknown version ⇒ the original bytes are quarantined (the
98
+ * existing `.corrupt` path) before a current-version file replaces them, which
99
+ * is the posture `evolution-events` already takes for the same shape.
100
+ * Residual, closed by FLOW6-6: `parseActivityContent` still answers `[]` for
101
+ * such a file, but {@link loadActivityState} now carries the verdict beside the
102
+ * records, so a read-only consumer can distinguish "newer format" from "no
103
+ * history" instead of presenting a partial view as the recorded truth.
104
+ * @internal Exported for this package's own tests (siblings `parseActivityContent`
105
+ * and `serializeActivity` are exported for the same reason).
106
+ */
107
+ export declare function isCorruptActivity(raw: string | null): boolean;
64
108
  export declare const name = "evolution-activity";
65
109
  export interface Config {
66
110
  /** Bounded sidecar: how many recent outcomes are kept. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-activity",
3
3
  "description": "Durable activity store for self-evolution plan outcomes (community build)",
4
- "version": "0.3.81",
4
+ "version": "0.3.83",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -27,15 +27,15 @@
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
29
  "@deepseek-ai/schemastery": "^3.18.1",
30
- "@lmzhen/dsh-evolution-core": "^0.3.81"
30
+ "@lmzhen/dsh-evolution-core": "^0.3.83"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@deepseek-ai/cordis": "^4.0.1"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@deepseek-ai/cordis": "^4.0.1",
37
- "@lmzhen/dsh-evolution-core": "^0.3.81",
38
- "@lmzhen/dsh-evolution-io": "^0.3.81",
39
- "@lmzhen/dsh-evolution-io-node": "^0.3.81"
37
+ "@lmzhen/dsh-evolution-core": "^0.3.83",
38
+ "@lmzhen/dsh-evolution-io": "^0.3.83",
39
+ "@lmzhen/dsh-evolution-io-node": "^0.3.83"
40
40
  }
41
41
  }