@lmzhen/dsh-evolution-curator 0.3.82 → 0.4.0
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 +13 -23
- package/lib/index.js +27 -3
- package/lib/types/index.d.ts +16 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,32 +1,19 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @lmzhen/dsh-evolution-curator
|
|
2
2
|
|
|
3
3
|
Deterministic skill lifecycle and recovery
|
|
4
4
|
|
|
5
|
-
## Model
|
|
5
|
+
## Model surface
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
`@deepseek-ai/dsh-evolution-curator` registers no direct prompt or tool schema itself. Model-visible effects are owned by the packages that consume this service.
|
|
12
|
-
|
|
13
|
-
#### Token effect
|
|
14
|
-
|
|
15
|
-
Zero direct token effect from this package; consumers add any model-visible tokens.
|
|
16
|
-
|
|
17
|
-
#### KV Cache effect
|
|
18
|
-
|
|
19
|
-
Independent of request-prefix construction. This package does not alter the assembled prompt or tool list.
|
|
20
|
-
|
|
21
|
-
## Known Limitations and Deferred Work
|
|
7
|
+
- **Model-visible:** nothing of its own: it registers no prompt section and no tool schema; the packages that consume this service own the model-visible effects.
|
|
8
|
+
- **Prompt prefix / KV cache:** independent of request-prefix construction — it does not alter the assembled prompt or tool list; family-level rules: `packages/README.md` §"Model-visible prompt prefix and the KV cache".
|
|
9
|
+
- **Mount it?** yes — the `evolution-curator` row, carried by the `evolution-host`, `evolution-all` and one-click `evolution-preset` bundles; it provides the `evolutionCurator` service the `/evolution curator …` commands and doctor read.
|
|
22
10
|
|
|
11
|
+
## Known limitations
|
|
23
12
|
|
|
24
13
|
- LLM nomination pass is advisory and disabled by default; deterministic lifecycle remains authoritative.
|
|
25
14
|
- Consolidation is a REAL merger path: the LLM nomination (when enabled / via `recommend()`) proposes
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
<target> <source...>` remains the direct manual path (P2-8, v11: the earlier "no LLM pass proposes
|
|
29
|
-
merge groups" wording contradicted the tested nomination chain).
|
|
15
|
+
merge groups, the control plane gates them and applies the mutation — merged source bodies are appended
|
|
16
|
+
verbatim rather than rewritten into a synthesized skill.
|
|
30
17
|
|
|
31
18
|
## Recovery and consolidation
|
|
32
19
|
|
|
@@ -35,9 +22,12 @@ Independent of request-prefix construction. This package does not alter the asse
|
|
|
35
22
|
- `consolidate(target, sources)` (service) / `/evolution consolidate` merges source bodies into the target, archives the sources with an absorbed-into marker, and folds their usage records into `archived` state. Both operations snapshot the full state first (`pre-consolidate` / `pre-restore`).
|
|
36
23
|
- `restoreSnapshot` (service) / `/evolution restore` rolls the state back to the latest snapshot: active tree, usage/suppression sidecars, `.archive/` and the curator state carried in the snapshot (`curator-state.json`), so the interval gate does not immediately re-fire after a rollback. Skills that were skipped at snapshot time (a live writer held their lock; recorded in the manifest's `skipped` list) are NOT restored — the restore result names them, and `.backups` may hold a copy. The restore itself is undoable — the pre-rollback safety snapshot preserves the current tree plus its state.
|
|
37
24
|
|
|
38
|
-
##
|
|
25
|
+
## Configuration
|
|
39
26
|
|
|
40
|
-
- `autoStart` (default true) arms an
|
|
27
|
+
- `autoStart` (default true) arms an HOURLY tick that only asks whether the due-ness interval (`intervalHours`, default 168 h) has elapsed — the tick is not the interval — plus a deferred catch-up check `bootGraceSeconds` (default 10) after host boot. Both decide due-ness from the **persisted** `lastRunAt`, so a restart with an overdue schedule runs the first pass within the boot grace instead of waiting a full interval. `bootGraceSeconds: 0` disables the deferral (not recommended: the check may run against a half-mounted host). All scheduling gates — interval, idle, first-run deferral, and the reentrancy guard — remain inside `run()`.
|
|
41
28
|
- `autoStart: false` disables both automatic checks; `/evolution curator run` (manual, gate-skipping) still works.
|
|
42
29
|
|
|
43
30
|
**Runtime invariant:** No companion is published. The platform auto-assembles nothing and the family mounts no `<pkg>/invariant` cordis row, so a companion here would never execute (v37 S2.1 / I-3).
|
|
31
|
+
## Notes and history
|
|
32
|
+
|
|
33
|
+
- The one-click `/evolution consolidate <target> <source...>` remains the direct manual path (P2-8, v11: the earlier "no LLM pass proposes merge groups" wording contradicted the tested nomination chain).
|
package/lib/index.js
CHANGED
|
@@ -355,6 +355,7 @@ var EvolutionCurator = class extends Service {
|
|
|
355
355
|
async restoreSnapshot() {
|
|
356
356
|
const release = await this.acquireMutex();
|
|
357
357
|
try {
|
|
358
|
+
if (!this.holdsInstance) return this.instanceHeldRefusal("restoreSnapshot");
|
|
358
359
|
return await this.restoreSnapshotCore();
|
|
359
360
|
} finally {
|
|
360
361
|
release();
|
|
@@ -468,6 +469,27 @@ var EvolutionCurator = class extends Service {
|
|
|
468
469
|
};
|
|
469
470
|
return prev.then(() => release);
|
|
470
471
|
}
|
|
472
|
+
/**
|
|
473
|
+
* PLAN S4.5 (2026-09-16) (audit P2-16): the manual control-plane entries
|
|
474
|
+
* (restore/consolidate/restoreSnapshot) honor the same per-home instance
|
|
475
|
+
* claim `run()` checks before every pass. The claim is taken at MOUNT and
|
|
476
|
+
* released at DISPOSE (core/instance-scope.ts), so `holdsInstance` is
|
|
477
|
+
* stable for this instance's lifetime: a single-instance deployment always
|
|
478
|
+
* holds it and every manual entry behaves exactly as before. The refusal
|
|
479
|
+
* fires only on a YIELDING second row of one process, whose manual mutation
|
|
480
|
+
* would otherwise race the holder row's in-flight run over the same tree —
|
|
481
|
+
* this instance's control-plane mutex cannot see the holder's chain. The
|
|
482
|
+
* shape is the plain `SkillActionResult` failure the command face already
|
|
483
|
+
* renders through `err(result.message)` — no new error type — and the
|
|
484
|
+
* leading `instance-held` token names the same outcome run() reports via
|
|
485
|
+
* its `skipped` field.
|
|
486
|
+
*/
|
|
487
|
+
instanceHeldRefusal(operation) {
|
|
488
|
+
return {
|
|
489
|
+
ok: false,
|
|
490
|
+
message: `instance-held: this curator row yielded the per-home claim to another curator row of this process — ${operation} is refused alongside run() so the holder row stays the only control-plane writer (run it on the holder row)`
|
|
491
|
+
};
|
|
492
|
+
}
|
|
471
493
|
async runCore(options = {}) {
|
|
472
494
|
const { ignoreGates = false, dryRun = false } = options;
|
|
473
495
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -498,13 +520,13 @@ var EvolutionCurator = class extends Service {
|
|
|
498
520
|
};
|
|
499
521
|
if (!ignoreGates && persisted === null && (stateService !== void 0 || !this.statelessFirstRunDeferred)) {
|
|
500
522
|
if (stateService) try {
|
|
501
|
-
await stateService.
|
|
523
|
+
await stateService.transactCuratorState((current) => current === null ? {
|
|
502
524
|
schemaVersion: 1,
|
|
503
525
|
lastRunAt: Date.now(),
|
|
504
526
|
runCount: 0,
|
|
505
527
|
lastSummary: "first-run-deferred",
|
|
506
528
|
paused: false
|
|
507
|
-
});
|
|
529
|
+
} : null);
|
|
508
530
|
} catch (error) {
|
|
509
531
|
this.ctx.logger.warn(`evolution-curator: failed to persist the first-run baseline: ${error instanceof Error ? error.message : String(error)}`);
|
|
510
532
|
}
|
|
@@ -547,7 +569,7 @@ var EvolutionCurator = class extends Service {
|
|
|
547
569
|
if (text) contents.set(name, text);
|
|
548
570
|
}
|
|
549
571
|
const protectedNames = await this.protectedNameMap();
|
|
550
|
-
const dedupMembers = [...new Set(computeDedupGroups({ contents }).filter((group) => group.length >= 2).flat())].filter((name) => !protectedNames.has(name));
|
|
572
|
+
const dedupMembers = [...new Set(computeDedupGroups({ contents }).groups.filter((group) => group.length >= 2).flat())].filter((name) => !protectedNames.has(name));
|
|
551
573
|
await this.scoreTree(usage, treeNames, contents);
|
|
552
574
|
const result = computeLifecycleTransitions(usage, {
|
|
553
575
|
staleAfterDays: lifecycle.staleAfterDays,
|
|
@@ -1111,6 +1133,7 @@ var EvolutionCurator = class extends Service {
|
|
|
1111
1133
|
async consolidate(target, sources) {
|
|
1112
1134
|
const release = await this.acquireMutex();
|
|
1113
1135
|
try {
|
|
1136
|
+
if (!this.holdsInstance) return this.instanceHeldRefusal("consolidate");
|
|
1114
1137
|
return await this.consolidateMutate(target, sources);
|
|
1115
1138
|
} finally {
|
|
1116
1139
|
release();
|
|
@@ -1155,6 +1178,7 @@ var EvolutionCurator = class extends Service {
|
|
|
1155
1178
|
async restore(name) {
|
|
1156
1179
|
const release = await this.acquireMutex();
|
|
1157
1180
|
try {
|
|
1181
|
+
if (!this.holdsInstance) return this.instanceHeldRefusal("restore");
|
|
1158
1182
|
return await this.restoreMutate(name);
|
|
1159
1183
|
} finally {
|
|
1160
1184
|
release();
|
package/lib/types/index.d.ts
CHANGED
|
@@ -243,6 +243,22 @@ export declare class EvolutionCurator extends Service {
|
|
|
243
243
|
private mutexDepth;
|
|
244
244
|
private mutexTail;
|
|
245
245
|
private acquireMutex;
|
|
246
|
+
/**
|
|
247
|
+
* PLAN S4.5 (2026-09-16) (audit P2-16): the manual control-plane entries
|
|
248
|
+
* (restore/consolidate/restoreSnapshot) honor the same per-home instance
|
|
249
|
+
* claim `run()` checks before every pass. The claim is taken at MOUNT and
|
|
250
|
+
* released at DISPOSE (core/instance-scope.ts), so `holdsInstance` is
|
|
251
|
+
* stable for this instance's lifetime: a single-instance deployment always
|
|
252
|
+
* holds it and every manual entry behaves exactly as before. The refusal
|
|
253
|
+
* fires only on a YIELDING second row of one process, whose manual mutation
|
|
254
|
+
* would otherwise race the holder row's in-flight run over the same tree —
|
|
255
|
+
* this instance's control-plane mutex cannot see the holder's chain. The
|
|
256
|
+
* shape is the plain `SkillActionResult` failure the command face already
|
|
257
|
+
* renders through `err(result.message)` — no new error type — and the
|
|
258
|
+
* leading `instance-held` token names the same outcome run() reports via
|
|
259
|
+
* its `skipped` field.
|
|
260
|
+
*/
|
|
261
|
+
private instanceHeldRefusal;
|
|
246
262
|
private runCore;
|
|
247
263
|
/**
|
|
248
264
|
* Seed baseline records for tree skills the sidecar has not seen yet, so
|
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.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -27,20 +27,20 @@
|
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
30
|
-
"@lmzhen/dsh-evolution-core": "^0.
|
|
30
|
+
"@lmzhen/dsh-evolution-core": "^0.4.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
34
34
|
"@deepseek-ai/dsh-llm": "^0.1.5-rc.2",
|
|
35
35
|
"@deepseek-ai/dsh-session": "^0.1.5-rc.2",
|
|
36
|
-
"@lmzhen/dsh-evolution-io": "^0.
|
|
37
|
-
"@lmzhen/dsh-evolution-state": "^0.
|
|
36
|
+
"@lmzhen/dsh-evolution-io": "^0.4.0",
|
|
37
|
+
"@lmzhen/dsh-evolution-state": "^0.4.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@deepseek-ai/dsh-llm": "^0.1.5-rc.2",
|
|
41
41
|
"@deepseek-ai/dsh-session": "^0.1.5-rc.2",
|
|
42
|
-
"@lmzhen/dsh-evolution-core": "^0.
|
|
43
|
-
"@lmzhen/dsh-evolution-io": "^0.
|
|
44
|
-
"@lmzhen/dsh-evolution-state": "^0.
|
|
42
|
+
"@lmzhen/dsh-evolution-core": "^0.4.0",
|
|
43
|
+
"@lmzhen/dsh-evolution-io": "^0.4.0",
|
|
44
|
+
"@lmzhen/dsh-evolution-state": "^0.4.0"
|
|
45
45
|
}
|
|
46
46
|
}
|