@lmzhen/dsh-evolution-feedback 0.1.0-rc.65 → 0.1.0-rc.66

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
@@ -281,8 +281,10 @@ var EvolutionFeedback = class {
281
281
  };
282
282
  chain = Promise.resolve();
283
283
  path;
284
+ io;
284
285
  constructor(io, home = process.env.DSH_HOME ?? join(homedir(), ".dsh"), pathOverride) {
285
286
  if (io) this.path = pathOverride ?? join(home, "evolution", "feedback.json");
287
+ this.io = io;
286
288
  }
287
289
  mutate(task) {
288
290
  const run = this.chain.then(task, task);
@@ -311,7 +313,8 @@ var EvolutionFeedback = class {
311
313
  });
312
314
  }
313
315
  record(target, rating, note, kind = "session", io) {
314
- const table = kind === "skill" ? this.state.skills : this.state.sessions;
316
+ const mode = kind === "skill" ? "skills" : "sessions";
317
+ const table = this.state[mode];
315
318
  const current = table[target] ?? {
316
319
  positive: 0,
317
320
  negative: 0
@@ -319,7 +322,31 @@ var EvolutionFeedback = class {
319
322
  current[rating] += 1;
320
323
  if (note !== void 0) current.lastNote = note;
321
324
  table[target] = current;
322
- if (io && this.path) this.flush(io);
325
+ const recordIo = io ?? this.io;
326
+ const path = this.path;
327
+ if (!recordIo || !path) return;
328
+ this.mutate(async () => {
329
+ try {
330
+ await transactIo(recordIo, path, (raw) => {
331
+ if (raw !== null) try {
332
+ JSON.parse(raw);
333
+ } catch {
334
+ return Promise.resolve(raw);
335
+ }
336
+ const diskState = parseState(raw);
337
+ const diskTable = diskState[mode];
338
+ const diskRec = diskTable[target] ?? {
339
+ positive: 0,
340
+ negative: 0
341
+ };
342
+ diskRec[rating] += 1;
343
+ if (note !== void 0) diskRec.lastNote = note;
344
+ diskTable[target] = diskRec;
345
+ this.state = diskState;
346
+ return Promise.resolve(JSON.stringify(diskState, null, 2));
347
+ });
348
+ } catch (error) {}
349
+ });
323
350
  }
324
351
  score(target, kind = "session") {
325
352
  const record = (kind === "skill" ? this.state.skills : this.state.sessions)[target];
@@ -334,16 +361,9 @@ var EvolutionFeedback = class {
334
361
  sessions: { ...this.state.sessions }
335
362
  };
336
363
  }
337
- async flush(io) {
338
- const path = this.path;
339
- if (!path || !io) return;
340
- await this.mutate(async () => {
341
- await transactIo(io, path, (current) => {
342
- const merged = mergeStates(parseState(current), this.state);
343
- this.state = merged;
344
- return Promise.resolve(JSON.stringify(merged, null, 2));
345
- });
346
- });
364
+ /** Await the pending record-task chain (unload safety; rc.66). */
365
+ waitIdle() {
366
+ return this.chain;
347
367
  }
348
368
  };
349
369
  /** Parse a raw feedback sidecar; malformed reads as empty (best-effort). */
@@ -365,25 +385,6 @@ function parseState(raw) {
365
385
  };
366
386
  }
367
387
  }
368
- /** Deep-merge two feedback states: union by target; in-memory values win on
369
- * conflict. NOTE (v3 audit P3 reviewed): additive counting was rejected — a
370
- * restart merge (restore+flush) would double-count the same records, and a
371
- * stateless JSON sidecar cannot distinguish "two processes incrementing the
372
- * same target" from "the same record seen twice". Cross-process same-target
373
- * increments stay a documented limitation; an append-only event log would be
374
- * the real fix. */
375
- function mergeStates(disk, memory) {
376
- return {
377
- skills: {
378
- ...disk.skills,
379
- ...memory.skills
380
- },
381
- sessions: {
382
- ...disk.sessions,
383
- ...memory.sessions
384
- }
385
- };
386
- }
387
388
  const name = "evolution-feedback";
388
389
  const Config = z.object({
389
390
  qualityWarnThreshold: z.number().default(-.25),
@@ -412,8 +413,8 @@ function apply(ctx, rawConfig = {}) {
412
413
  };
413
414
  }
414
415
  ctx.effect(() => () => {
415
- if (io) return feedback.flush(io);
416
- }, "evolution-feedback.flush");
416
+ return feedback.waitIdle();
417
+ }, "evolution-feedback.records");
417
418
  }
418
419
  //#endregion
419
420
  export { Config, EvolutionFeedback, apply, name };
@@ -28,13 +28,15 @@ export declare class EvolutionFeedback {
28
28
  private state;
29
29
  private chain;
30
30
  private readonly path?;
31
+ private readonly io;
31
32
  constructor(io?: IoLike, home?: string, pathOverride?: string);
32
33
  private mutate;
33
34
  restore(io: IoLike): Promise<void>;
34
35
  record(target: string, rating: 'positive' | 'negative', note?: string, kind?: 'skill' | 'session', io?: IoLike): void;
35
36
  score(target: string, kind?: 'skill' | 'session'): number;
36
37
  snapshot(): FeedbackState;
37
- flush(io?: IoLike): Promise<void>;
38
+ /** Await the pending record-task chain (unload safety; rc.66). */
39
+ waitIdle(): Promise<unknown>;
38
40
  }
39
41
  export declare const name = "evolution-feedback";
40
42
  export interface Config {
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.1.0-rc.65",
4
+ "version": "0.1.0-rc.66",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -37,13 +37,13 @@
37
37
  "peerDependencies": {
38
38
  "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
39
39
  "@deepseek-ai/cordis": "^4.0.1",
40
- "@lmzhen/dsh-evolution-io": "^0.1.0-rc.65",
41
- "@lmzhen/dsh-skill-usage": "^0.1.0-rc.65"
40
+ "@lmzhen/dsh-evolution-io": "^0.1.0-rc.66",
41
+ "@lmzhen/dsh-skill-usage": "^0.1.0-rc.66"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
45
- "@lmzhen/dsh-evolution-io": "^0.1.0-rc.65",
46
- "@lmzhen/dsh-evolution-io-node": "^0.1.0-rc.65",
47
- "@lmzhen/dsh-skill-usage": "^0.1.0-rc.65"
45
+ "@lmzhen/dsh-evolution-io": "^0.1.0-rc.66",
46
+ "@lmzhen/dsh-evolution-io-node": "^0.1.0-rc.66",
47
+ "@lmzhen/dsh-skill-usage": "^0.1.0-rc.66"
48
48
  }
49
49
  }