@lmzhen/dsh-evolution-feedback 0.3.70 → 0.3.72
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 +38 -8
- package/lib/types/index.d.ts +9 -1
- package/package.json +7 -7
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
|
-
import { EVENT_LOG_VERSION, appendEvolutionEvent, clampedNumber, eventsFile, evolutionIoAdapter, evolutionRoot, listEventArchives, parseEvolutionEvents, readEvolutionTimeline, transactIo } from "@lmzhen/dsh-evolution-core";
|
|
2
|
+
import { EVENT_LOG_VERSION, appendEvolutionEvent, clampedNumber, eventsFile, evolutionEventPayloadIssue, evolutionIoAdapter, evolutionRoot, listEventArchives, parseEvolutionEvents, readEvolutionTimeline, transactIo } from "@lmzhen/dsh-evolution-core";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
//#region lib/types/index.js
|
|
5
5
|
/**
|
|
@@ -90,14 +90,15 @@ var EvolutionFeedback = class EvolutionFeedback {
|
|
|
90
90
|
await this.mutate(async () => {
|
|
91
91
|
const rawEvents = await io.readText(eventsPath);
|
|
92
92
|
const archiveNames = await listEventArchives(io, eventsPath);
|
|
93
|
+
const rawCache = await io.readText(path);
|
|
93
94
|
if ((rawEvents === null || rawEvents.trim() === "") && archiveNames.length === 0) {
|
|
94
|
-
const aggregate = parseAggregate(
|
|
95
|
+
const aggregate = parseAggregate(rawCache, this.warn);
|
|
95
96
|
if (aggregate) try {
|
|
96
|
-
await migrateFeedbackEvents(io, eventsPath, aggregate);
|
|
97
|
+
await migrateFeedbackEvents(io, eventsPath, aggregate, this.warn);
|
|
97
98
|
} catch {}
|
|
98
99
|
}
|
|
99
|
-
const { events } = await readEvolutionTimeline(io, eventsPath);
|
|
100
|
-
const cache = parseCache(
|
|
100
|
+
const { events } = await readEvolutionTimeline(io, eventsPath, archiveNames);
|
|
101
|
+
const cache = parseCache(rawCache, this.warn);
|
|
101
102
|
const maxSeq = events.reduce((max, event) => Math.max(max, event.seq), 0);
|
|
102
103
|
const floor = events[0]?.seq ?? 0;
|
|
103
104
|
const usableCache = cache && cache.lastSeq >= floor - 1 ? cache : null;
|
|
@@ -222,6 +223,14 @@ var EvolutionFeedback = class EvolutionFeedback {
|
|
|
222
223
|
if (total === 0) return 0;
|
|
223
224
|
return (record.positive - record.negative) / total;
|
|
224
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Deep-copy view of the live aggregate.
|
|
228
|
+
*
|
|
229
|
+
* OPT-26 (2026-09): `@internal` — test-support API, NO production consumer
|
|
230
|
+
* in the family (mirrors the explicit posture of evolution-replay's
|
|
231
|
+
* `plansSnapshot()`). External consumers should read the rendered
|
|
232
|
+
* score/warn surfaces instead of relying on this shape.
|
|
233
|
+
*/
|
|
225
234
|
snapshot() {
|
|
226
235
|
const copyRecords = (table) => {
|
|
227
236
|
const out = {};
|
|
@@ -294,8 +303,13 @@ function containsLegacySequence(existing, expected) {
|
|
|
294
303
|
* and race-safe (the search runs inside the same transact). Exported for the
|
|
295
304
|
* migration-race regression test.
|
|
296
305
|
*/
|
|
297
|
-
async function migrateFeedbackEvents(io, eventsPath, aggregate) {
|
|
306
|
+
async function migrateFeedbackEvents(io, eventsPath, aggregate, warn = () => {}) {
|
|
298
307
|
const expected = synthesizeFeedbackEvents(aggregate);
|
|
308
|
+
const refused = expected.filter((event) => evolutionEventPayloadIssue(event) !== null);
|
|
309
|
+
if (refused.length > 0) {
|
|
310
|
+
warn(`evolution-feedback: migration refused ${refused.length} synthesized event(s) that the event log's payload gate rejects (types: ${[...new Set(refused.map((event) => event.type))].join(", ")}) — history starts empty instead of writing an unreadable log`);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
299
313
|
await transactIo(io, eventsPath, (current) => {
|
|
300
314
|
const existing = parseEvolutionEvents(current);
|
|
301
315
|
if (containsLegacySequence(existing, expected)) return Promise.resolve(current);
|
|
@@ -367,12 +381,28 @@ function parseCache(raw, warn = () => {}) {
|
|
|
367
381
|
/** Per-record numeric-domain validation (S6.4): a record whose `positive` or
|
|
368
382
|
* `negative` is not a finite number >= 0, or whose `lastNote` is not a string,
|
|
369
383
|
* would fold as NaN into the usage aggregate — skip it with a warn instead of
|
|
370
|
-
* corrupting the state. A record with no valid count is dropped entirely.
|
|
384
|
+
* corrupting the state. A record with no valid count is dropped entirely.
|
|
385
|
+
* OPT-25 (2026-09): a count ABOVE `MAX_MIGRATED_EVENTS_PER_RECORD` is now
|
|
386
|
+
* CLAMPED here with a warn, symmetric with the migration reader's clamp (same
|
|
387
|
+
* file, same threat model — V24-05 covered the migration side only). Before,
|
|
388
|
+
* a poisoned boot-cache count folded as-is into every downstream decision;
|
|
389
|
+
* the clamp bounds the skew to the same ceiling migration accepts. */
|
|
371
390
|
function sanitizeCacheRecords(input, kind, warn) {
|
|
372
391
|
const out = {};
|
|
373
392
|
for (const [target, value] of Object.entries(input)) {
|
|
374
393
|
const record = sanitizeFeedbackRecord(value, target, kind, warn);
|
|
375
|
-
if (record)
|
|
394
|
+
if (record) {
|
|
395
|
+
let clamped;
|
|
396
|
+
if (record.positive > MAX_MIGRATED_EVENTS_PER_RECORD || record.negative > MAX_MIGRATED_EVENTS_PER_RECORD) {
|
|
397
|
+
warn(`evolution-feedback: cache record for ${kind} "${target}" exceeds the clamp ceiling (${MAX_MIGRATED_EVENTS_PER_RECORD}) — clamping like the migration reader`);
|
|
398
|
+
clamped = {
|
|
399
|
+
positive: Math.min(record.positive, MAX_MIGRATED_EVENTS_PER_RECORD),
|
|
400
|
+
negative: Math.min(record.negative, MAX_MIGRATED_EVENTS_PER_RECORD),
|
|
401
|
+
...record.lastNote !== void 0 ? { lastNote: record.lastNote } : {}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
out[target] = clamped ?? record;
|
|
405
|
+
}
|
|
376
406
|
}
|
|
377
407
|
return out;
|
|
378
408
|
}
|
package/lib/types/index.d.ts
CHANGED
|
@@ -98,6 +98,14 @@ export declare class EvolutionFeedback {
|
|
|
98
98
|
refold(): Promise<void>;
|
|
99
99
|
record(target: string, rating: 'positive' | 'negative', note?: string, kind?: 'skill' | 'session'): void;
|
|
100
100
|
score(target: string, kind?: 'skill' | 'session'): number;
|
|
101
|
+
/**
|
|
102
|
+
* Deep-copy view of the live aggregate.
|
|
103
|
+
*
|
|
104
|
+
* OPT-26 (2026-09): `@internal` — test-support API, NO production consumer
|
|
105
|
+
* in the family (mirrors the explicit posture of evolution-replay's
|
|
106
|
+
* `plansSnapshot()`). External consumers should read the rendered
|
|
107
|
+
* score/warn surfaces instead of relying on this shape.
|
|
108
|
+
*/
|
|
101
109
|
snapshot(): FeedbackState;
|
|
102
110
|
/** Await the pending record-task chain (unload safety; rc.66). */
|
|
103
111
|
waitIdle(): Promise<unknown>;
|
|
@@ -116,7 +124,7 @@ export declare class EvolutionFeedback {
|
|
|
116
124
|
* and race-safe (the search runs inside the same transact). Exported for the
|
|
117
125
|
* migration-race regression test.
|
|
118
126
|
*/
|
|
119
|
-
export declare function migrateFeedbackEvents(io: IoLike, eventsPath: string, aggregate: FeedbackState): Promise<void>;
|
|
127
|
+
export declare function migrateFeedbackEvents(io: IoLike, eventsPath: string, aggregate: FeedbackState, warn?: (message: string) => void): Promise<void>;
|
|
120
128
|
export declare const name = "evolution-feedback";
|
|
121
129
|
export interface Config {
|
|
122
130
|
/** Score below which the FEEDBACK pair flips to warned (P1-1, v15: written
|
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.
|
|
4
|
+
"version": "0.3.72",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -31,18 +31,18 @@
|
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
34
|
-
"@lmzhen/dsh-evolution-core": "^0.3.
|
|
34
|
+
"@lmzhen/dsh-evolution-core": "^0.3.72"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"@deepseek-ai/dsh-invariants": "^0.1.5-rc.2",
|
|
38
38
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
39
|
-
"@lmzhen/dsh-evolution-io": "^0.3.
|
|
40
|
-
"@lmzhen/dsh-skill-usage": "^0.3.
|
|
39
|
+
"@lmzhen/dsh-evolution-io": "^0.3.72",
|
|
40
|
+
"@lmzhen/dsh-skill-usage": "^0.3.72"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@deepseek-ai/dsh-invariants": "^0.1.5-rc.2",
|
|
44
|
-
"@lmzhen/dsh-evolution-io": "^0.3.
|
|
45
|
-
"@lmzhen/dsh-evolution-io-node": "^0.3.
|
|
46
|
-
"@lmzhen/dsh-skill-usage": "^0.3.
|
|
44
|
+
"@lmzhen/dsh-evolution-io": "^0.3.72",
|
|
45
|
+
"@lmzhen/dsh-evolution-io-node": "^0.3.72",
|
|
46
|
+
"@lmzhen/dsh-skill-usage": "^0.3.72"
|
|
47
47
|
}
|
|
48
48
|
}
|