@lmzhen/dsh-evolution-curator 0.3.27 → 0.3.28

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
@@ -185,6 +185,7 @@ var EvolutionCurator = class extends Service {
185
185
  } catch (persistenceError) {
186
186
  this.ctx.logger.warn(`evolution-curator: failed to persist auto-check error report: ${persistenceError instanceof Error ? persistenceError.message : String(persistenceError)}`);
187
187
  }
188
+ await this.retainReports();
188
189
  }
189
190
  }
190
191
  /**
@@ -319,6 +320,11 @@ var EvolutionCurator = class extends Service {
319
320
  return await this.runCore(options);
320
321
  } finally {
321
322
  this.running = false;
323
+ try {
324
+ await this.retainReports();
325
+ } catch (error) {
326
+ this.ctx.logger.warn(`evolution-curator: failed to retain reports: ${error instanceof Error ? error.message : String(error)}`);
327
+ }
322
328
  }
323
329
  }
324
330
  async runCore(options = {}) {
@@ -447,7 +453,6 @@ var EvolutionCurator = class extends Service {
447
453
  try {
448
454
  await this.io.writeText(join(reportsRoot, `curator-${runId}.json`), JSON.stringify(report, null, 2));
449
455
  await this.io.writeText(join(reportsRoot, `curator-${runId}.md`), renderCuratorReportMarkdown(report));
450
- await this.retainReports(20);
451
456
  } catch (error) {
452
457
  this.ctx.logger.warn(`evolution-curator: failed to persist report ${runId}`);
453
458
  this.ctx.logger.warn(error);
@@ -668,7 +673,19 @@ var EvolutionCurator = class extends Service {
668
673
  * like `retainSnapshots`: a failed removal must not fail the run that just
669
674
  * persisted its report. The paired `.md` digest is pruned with its JSON.
670
675
  */
671
- async retainReports(keep) {
676
+ /**
677
+ * Keep only the newest N curator run reports plus at most `errorKeep` error
678
+ * reports, ordered by the report's own `startedAt` (the runId is a UUID and
679
+ * cannot order history). Best-effort like `retainSnapshots`: a failed removal
680
+ * must not fail the run that just persisted its report. The paired `.md`
681
+ * digest is pruned with its JSON.
682
+ *
683
+ * V4-22: real reports (`curator-*.json`) and error reports
684
+ * (`curator-error-*.json`) are BUDGETED INDEPENDENTLY. Before this they shared
685
+ * one keep-20 window, so after 25 consecutive failures the next successful
686
+ * run's window held only a few real reports alongside the errors.
687
+ */
688
+ async retainReports(keep = 20, errorKeep = 10) {
672
689
  const reportsRoot = join(evolutionHome(), "reports");
673
690
  let entries;
674
691
  try {
@@ -676,7 +693,8 @@ var EvolutionCurator = class extends Service {
676
693
  } catch {
677
694
  return;
678
695
  }
679
- const dated = [];
696
+ const real = [];
697
+ const errors = [];
680
698
  for (const name of entries.filter((entry) => entry.startsWith("curator-") && entry.endsWith(".json"))) try {
681
699
  const raw = await this.io.readText(join(reportsRoot, name));
682
700
  if (raw === null) continue;
@@ -686,12 +704,18 @@ var EvolutionCurator = class extends Service {
686
704
  const mtime = await this.io.mtime?.(join(reportsRoot, name)) ?? null;
687
705
  if (mtime !== null) startedAt = mtime;
688
706
  }
689
- if (Number.isFinite(startedAt)) dated.push({
707
+ if (Number.isFinite(startedAt)) (name.startsWith("curator-error-") ? errors : real).push({
690
708
  name,
691
709
  startedAt
692
710
  });
693
711
  } catch {}
694
- dated.sort((a, b) => b.startedAt - a.startedAt);
712
+ real.sort((a, b) => b.startedAt - a.startedAt);
713
+ errors.sort((a, b) => b.startedAt - a.startedAt);
714
+ await this.pruneReportList(reportsRoot, real, keep);
715
+ await this.pruneReportList(reportsRoot, errors, errorKeep);
716
+ }
717
+ /** Remove the oldest reports beyond `keep` (each with its `.md` digest, best-effort). */
718
+ async pruneReportList(reportsRoot, dated, keep) {
695
719
  for (const oldReport of dated.slice(keep)) {
696
720
  const stem = oldReport.name.replace(/\.json$/, "");
697
721
  try {
@@ -212,7 +212,21 @@ export declare class EvolutionCurator extends Service {
212
212
  * like `retainSnapshots`: a failed removal must not fail the run that just
213
213
  * persisted its report. The paired `.md` digest is pruned with its JSON.
214
214
  */
215
+ /**
216
+ * Keep only the newest N curator run reports plus at most `errorKeep` error
217
+ * reports, ordered by the report's own `startedAt` (the runId is a UUID and
218
+ * cannot order history). Best-effort like `retainSnapshots`: a failed removal
219
+ * must not fail the run that just persisted its report. The paired `.md`
220
+ * digest is pruned with its JSON.
221
+ *
222
+ * V4-22: real reports (`curator-*.json`) and error reports
223
+ * (`curator-error-*.json`) are BUDGETED INDEPENDENTLY. Before this they shared
224
+ * one keep-20 window, so after 25 consecutive failures the next successful
225
+ * run's window held only a few real reports alongside the errors.
226
+ */
215
227
  private retainReports;
228
+ /** Remove the oldest reports beyond `keep` (each with its `.md` digest, best-effort). */
229
+ private pruneReportList;
216
230
  latestReport(): Promise<CuratorRunReport | null>;
217
231
  /**
218
232
  * Read-only lifecycle scope classification: which skills are in scope,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-curator",
3
3
  "description": "Deterministic skill lifecycle and recovery (community build)",
4
- "version": "0.3.27",
4
+ "version": "0.3.28",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -31,20 +31,20 @@
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
33
  "@deepseek-ai/schemastery": "^3.18.1",
34
- "@lmzhen/dsh-evolution-core": "^0.3.27"
34
+ "@lmzhen/dsh-evolution-core": "^0.3.28"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "@deepseek-ai/cordis": "^4.0.1",
38
38
  "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
39
39
  "@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
40
- "@lmzhen/dsh-evolution-io": "^0.3.27",
41
- "@lmzhen/dsh-evolution-state": "^0.3.27"
40
+ "@lmzhen/dsh-evolution-io": "^0.3.28",
41
+ "@lmzhen/dsh-evolution-state": "^0.3.28"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
45
45
  "@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
46
- "@lmzhen/dsh-evolution-core": "^0.3.27",
47
- "@lmzhen/dsh-evolution-io": "^0.3.27",
48
- "@lmzhen/dsh-evolution-state": "^0.3.27"
46
+ "@lmzhen/dsh-evolution-core": "^0.3.28",
47
+ "@lmzhen/dsh-evolution-io": "^0.3.28",
48
+ "@lmzhen/dsh-evolution-state": "^0.3.28"
49
49
  }
50
50
  }