dsh-continual-evolve 0.1.1 → 0.3.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.
Files changed (64) hide show
  1. package/README.md +172 -17
  2. package/README.zh.md +87 -10
  3. package/lib/apply.js +3 -1
  4. package/lib/approval.d.ts +25 -0
  5. package/lib/approval.js +9 -1
  6. package/lib/auto.d.ts +99 -5
  7. package/lib/auto.js +165 -6
  8. package/lib/benchmark-command.d.ts +9 -0
  9. package/lib/benchmark-command.js +331 -0
  10. package/lib/benchmark.d.ts +84 -0
  11. package/lib/benchmark.js +107 -1
  12. package/lib/command.js +33 -221
  13. package/lib/evaluate.d.ts +43 -7
  14. package/lib/evaluate.js +172 -43
  15. package/lib/evolve-event.d.ts +38 -0
  16. package/lib/evolve-event.js +49 -0
  17. package/lib/failures.d.ts +39 -0
  18. package/lib/failures.js +170 -0
  19. package/lib/fate.d.ts +128 -0
  20. package/lib/fate.js +342 -0
  21. package/lib/goal-command.d.ts +7 -0
  22. package/lib/goal-command.js +37 -0
  23. package/lib/index.d.ts +51 -21
  24. package/lib/index.js +32 -2
  25. package/lib/inject.d.ts +8 -0
  26. package/lib/inject.js +51 -4
  27. package/lib/llm-text.d.ts +30 -0
  28. package/lib/llm-text.js +49 -0
  29. package/lib/mount-command.d.ts +10 -0
  30. package/lib/mount-command.js +48 -0
  31. package/lib/mount.js +5 -0
  32. package/lib/plan.js +5 -0
  33. package/lib/planner.d.ts +8 -1
  34. package/lib/planner.js +40 -39
  35. package/lib/render.d.ts +1 -3
  36. package/lib/render.js +2 -5
  37. package/lib/review.d.ts +5 -2
  38. package/lib/review.js +27 -38
  39. package/lib/rollback.d.ts +1 -3
  40. package/lib/rollback.js +0 -8
  41. package/lib/score.d.ts +37 -4
  42. package/lib/score.js +120 -10
  43. package/lib/service.d.ts +2 -2
  44. package/lib/service.js +5 -2
  45. package/lib/skill-render.d.ts +15 -0
  46. package/lib/skill-render.js +30 -0
  47. package/lib/skill.d.ts +12 -7
  48. package/lib/skill.js +36 -31
  49. package/lib/skillquality.d.ts +80 -0
  50. package/lib/skillquality.js +311 -0
  51. package/lib/store.d.ts +1 -3
  52. package/lib/store.js +0 -7
  53. package/lib/tool.js +28 -4
  54. package/lib/types.d.ts +39 -0
  55. package/lib/types.js +19 -0
  56. package/lib/usage.d.ts +32 -0
  57. package/lib/usage.js +84 -0
  58. package/lib/validate.d.ts +12 -2
  59. package/lib/validate.js +51 -2
  60. package/lib/wrapup-command.d.ts +8 -0
  61. package/lib/wrapup-command.js +211 -0
  62. package/lib/wrapup.d.ts +215 -0
  63. package/lib/wrapup.js +427 -0
  64. package/package.json +8 -8
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![CI](https://github.com/ZK-Andy/dsh-continual-evolve/actions/workflows/ci.yml/badge.svg)](https://github.com/ZK-Andy/dsh-continual-evolve/actions/workflows/ci.yml)
8
8
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
9
  [![Node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-339933)](package.json)
10
- [![Tests](https://img.shields.io/badge/tests-184%20passing-brightgreen)]()
10
+ [![Tests](https://img.shields.io/badge/tests-401%20passing-brightgreen)]()
11
11
  [![Status](https://img.shields.io/badge/status-all%20phases%20complete%20%C2%B7%20maintenance-ff69b4)]()
12
12
 
13
13
  Continual self-evolution for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): a versioned, auditable, rollback-safe layer of harness state — prompt notes, memories, skills, and subagent specs — refined from session trajectories.
@@ -21,8 +21,11 @@ Continual self-evolution for [DeepSeek Harness](https://github.com/deepseek-ai/d
21
21
  > scoring, non-regressive acceptance, rubric ACL). Since then the plugin
22
22
  > keeps growing with usage-driven enhancements — the memory layer (ranked
23
23
  > injection, trajectory citations, archive), per-installation rubric keys,
24
- > and plugin-owned file logging. See the Roadmap for the full shipped and
25
- > candidate lists.
24
+ > plugin-owned file logging, the session wrap-up (`/evolve wrapup`), and
25
+ > the gate's automatic local-fate dimension (local entries get a promoted
26
+ > or archived exit on the gate's own cadence — consulted first, never
27
+ > written silently). See the Roadmap for the full shipped and candidate
28
+ > lists.
26
29
 
27
30
  ## Background
28
31
 
@@ -98,27 +101,38 @@ dsh-continual-evolve/
98
101
  │ ├── rollback.ts # deterministic inverse-op rollback
99
102
  │ ├── plan.ts # proposal JSON parsing (truncation-aware)
100
103
  │ ├── tool.ts # evolve_* model-facing tools (5)
101
- │ ├── command.ts # /evolve command (incl. benchmark subcommands)
104
+ │ ├── command.ts # /evolve command dispatcher + shared utilities
105
+ │ ├── goal-command.ts # /evolve goal subcommand handler
106
+ │ ├── mount-command.ts # /evolve mount + unmount subcommand handlers
107
+ │ ├── benchmark-command.ts # /evolve benchmark subcommand handler
108
+ │ ├── wrapup-command.ts # /evolve wrapup subcommand handler
102
109
  │ ├── planner.ts # ctx.llm planner
110
+ │ ├── llm-text.ts # unified streaming-text helper (BlockAssembler + finish check)
103
111
  │ ├── render.ts # bounded prompt rendering
104
112
  │ ├── inject.ts # dynamic system-prompt section (prompt notes + delegation specs, ranked injection)
105
113
  │ ├── source.ts # trajectory citations (sessionId + event seqs of distilled entries)
106
- │ ├── auto.ts # auto-review gate (turn/compaction triggers + audit, global-aware view)
114
+ │ ├── auto.ts # auto-review gate (turn/compaction triggers + audit, global-aware view, local-fate phase)
115
+ │ ├── fate.ts # gate local-fate dimension — auto promote/archive of local entries (consulted first, cooldown)
107
116
  │ ├── notify.ts # gate visibility — follow-up notice after an approved auto-refine
108
117
  │ ├── goal.ts # goal-driven evolution rounds (/evolve goal)
109
118
  │ ├── review.ts # gate LLM judgment (declines local duplicates of globally covered topics)
110
119
  │ ├── approval.ts # human approval for global edits
111
120
  │ ├── skill.ts # skill materialization ($DSH_HOME/skills/)
121
+ │ ├── skill-render.ts # shared skill rendering (skillNameOf + renderSkillMarkdown, breaks circular dependency)
122
+ │ ├── skillquality.ts # skill standard in the loop (skill-creator template reading + frontmatter code checks)
112
123
  │ ├── mount.ts # hot-mounted skill plugins (loader.create + boot restore)
113
- │ ├── benchmark.ts # benchmark store
124
+ │ ├── benchmark.ts # benchmark store + CellScore types (with runtime evidence fields)
114
125
  │ ├── rubric.ts # rubric ACL (AES-256-GCM envelopes, auto-generated local key)
115
126
  │ ├── logfile.ts # plugin-owned file logging (JSONL exporter + rotation)
116
127
  │ ├── score.ts # code-owned aggregation + acceptance rule
117
- │ ├── evaluate.ts # evaluation matrix runner (structured-output subagents)
128
+ │ ├── evaluate.ts # two-stage evaluation runner (executor evidence → independent reviewer) + failure-cell protocol + runtime verification
118
129
  │ ├── pool.ts # bounded-concurrency worker pool for evaluation runs
119
130
  │ ├── store.ts # store layout + snapshots + result history
120
- └── service.ts # evolution engine (onApplied hook)
121
- └── test/ # 20 files, 184 tests
131
+ ├── service.ts # evolution engine (onApplied hook)
132
+ │ ├── usage.ts # entry injection usage tracking (durable counts, staleness detection)
133
+ │ ├── failures.ts # failure-signature aggregation (gate + benchmark failures by class, /evolve failures)
134
+ │ └── wrapup.ts # session wrap-up lifecycle (promote / split-promote → global, guarded archive; shared proposal builders; staleness signal)
135
+ └── test/ # 28 files, 401 tests
122
136
  ```
123
137
 
124
138
  ## Install
@@ -141,9 +155,12 @@ Swap `web` for your profile name (`headless`, or a custom profile).
141
155
  /evolve history applied refinements (ids for rollback)
142
156
  /evolve rollback <id> deterministically revert a refinement
143
157
  /evolve plan [msg] LLM planner against the current store
158
+ /evolve wrapup assess this session's local entries: promote reusable ones to the
159
+ global store (approval required), archive session-specific ones
144
160
  /evolve archive <id> hide an entry from injection (data kept, restorable)
145
161
  /evolve unarchive <id> restore an archived entry
146
162
  /evolve log [tail N] [session <id>] show the recent plugin log (default 50 lines; optional per-session filter)
163
+ /evolve failures aggregated failure counts (review-gate + benchmark, by class — D1 observation layer)
147
164
  /evolve export <path> backup the local store to JSON
148
165
  /evolve import <path> restore a store from an export file
149
166
  /evolve mount <skillId> hot-mount a skill entry as a live cordis plugin (tool: skill_<name>)
@@ -179,11 +196,82 @@ LangMem; no external services — everything is pure functions):
179
196
  `/evolve unarchive <id>` restores it. Archived entries are marked
180
197
  `[archived]` in `evolve_list` and skipped by injection; the overflow count
181
198
  excludes them.
199
+ - **Session wrap-up** — a session's local entries otherwise become orphans when
200
+ it ends (later sessions never see them). `/evolve wrapup` gives them an exit.
201
+ Each entry is audited mechanically — global-coverage is judged by **title
202
+ similarity only** (a bare id collision with a different title is intentionally
203
+ NOT coverage; the actual matching global titles are shown to the assessor so
204
+ it judges against real content) — then classified as `promote` / `archive` /
205
+ `keep`. Promotions move reusable entries into the global store **through the
206
+ human approval gate**, keeping their trajectory citation and adding a
207
+ `sourcedFromLocal=<session>:<id>` back-link; the local copy is stamped
208
+ `promotedTo` and retired from injection so it is never offered again.
209
+ **Split promotion** (A-form): a mixed entry (durable facts + session snapshot)
210
+ can be archived while carrying a cleaned `promote` sub-object — only the
211
+ durable part lands globally, the snapshot stays in the archive. A **symmetric
212
+ archive guard** requires user confirmation before an archive that is NOT
213
+ globally covered AND was distilled from real user messages hides that content
214
+ from future sessions (over-archiving gets the same protection as
215
+ over-writing); operational entries still archive silently. Everything stays
216
+ snapshot/versioned/rollbackable.
217
+ - **Gate local-fate (automatic wrap-up)** — the same wrap-up machinery now
218
+ runs inside the auto-review gate on its own cadence (`fateIntervalTurns`),
219
+ so local entries get their exit while the session is still running instead
220
+ of waiting for a manual `/evolve wrapup`. On each due gate run the audited
221
+ candidates are classified by the assessor and partitioned by the same
222
+ deterministic guards; the user is consulted FIRST before anything governed
223
+ lands (one dialog covering promotes, split promotions and review-required
224
+ archives — the consultSkillEdits pattern, with a decline cooldown). Covered
225
+ or operational entries still archive silently, and at compaction the gate
226
+ never opens a dialog: only silent archives apply, governed actions are
227
+ deferred with an audit record pointing at `/evolve wrapup`. Every fate
228
+ decision lands in `reviews.jsonl` (`approved` / `declined` / `deferred` /
229
+ `assessed` / `failed`) and applied actions are visible via a follow-up
230
+ notice. Apply writes are byte-identical to the wrap-up command (shared
231
+ proposal builders).
182
232
  - **Global-aware gate** — the auto-review gate and planner judge the merged
183
233
  global + local state with every entry's real scope labeled, so a topic
184
234
  already covered by a global entry is declined instead of being re-sedimented
185
235
  as a local duplicate.
186
236
 
237
+ ## Skill standard in the loop
238
+
239
+ The planner and the auto-review gate are raw `ctx.llm` calls — they do not
240
+ live in an agent session, so they cannot load skills through the `skill`
241
+ tool. To keep self-evolved skills on the quality bar, the plugin references
242
+ the **skill-creator** / **skill-audit** skills (user-level skills distilled
243
+ by the author from the official deepseek-harness 11 skills; template facts
244
+ verified against deepseek-harness `47f9438`) at runtime — they stay the
245
+ single source of truth on disk, nothing is copied:
246
+
247
+ - Every planning call receives a `<skill_quality_standard>` block: the
248
+ `skill-creator/references/template.md` facts when those skills are
249
+ installed (`<dshHome>/skills/`), or a builtin distilled guide otherwise
250
+ (~1KB, low-frequency calls). The planner must ground skill proposals in a
251
+ REAL trigger scenario from the trajectory, must not duplicate the
252
+ official 11 skills or existing entries, and self-checks every proposed
253
+ skill against the 7 structural features.
254
+ - The gate judges skill-related trajectories against the skill-audit
255
+ dimensions (frontmatter routing, structural features, paragraph skeleton,
256
+ duplication) and declines proposals that would not meet the standard.
257
+ - The mechanical frontmatter rules of `validate-frontmatter.mjs` are
258
+ code-enforced at apply time: skill bodies must not open with a second
259
+ `---` block (it would shadow the generated frontmatter), and resource
260
+ references may not escape the skill directory. After materialization the
261
+ rendered SKILL.md is re-checked and dangling `references/`/`scripts/`
262
+ links are logged as warnings.
263
+ - **Two skill forms** — `executable` skills keep the python reference
264
+ contract (hot-mountable as tools); `guidance` skills are SKILL.md
265
+ documents with no reference, the form for recurring multi-step workflows
266
+ (session start/end routines, handoff procedures). Code enforces the
267
+ split: a guidance skill must NOT carry a reference or arguments contract.
268
+ - **User-governed skill creation** — the gate never writes a skill
269
+ silently: when the planner proposes skill edits, the user is asked
270
+ (固化/不固化) before they land; a rejected candidate is not offered
271
+ again within a cooldown window. The rest of a proposal proceeds
272
+ regardless, so memory/prompt distillation is never blocked by a skill
273
+ decision.
274
+
187
275
  ## Logging
188
276
 
189
277
  Plugin-owned file logging: every cordis log message (from this plugin or any
@@ -211,16 +299,47 @@ profile (optional; the file log remains the baseline that always exists).
211
299
  /evolve benchmark status <bid> scoreboard + decisions
212
300
  /evolve benchmark run <bid> evaluate current state → reference
213
301
  /evolve benchmark run <bid> candidate <refinementId> evaluate post-refinement state → decide
302
+ /evolve benchmark casecheck <bid> quality-gate check all cases
303
+ /evolve benchmark pilot <bid> <cid> single pilot run for calibration
304
+ /evolve benchmark freeze <bid> <cid> freeze a case as formal baseline
305
+ /evolve benchmark meta <bid> <cid> <field> <value> set case metadata (capability/distinguisher/shortcuts)
214
306
  ```
215
307
 
216
308
  The loop: freeze a reference score → evolve a candidate (`/evolve plan`) →
217
309
  run the same case × run matrix against the post-refinement state → the
218
310
  **code-owned** acceptance rule keeps the candidate only if the overall mean
219
- strictly improves with no case regressing (Self-Harness style). The model
220
- produces raw per-cell scores only; aggregation and decisions live in
221
- `src/score.ts`. Rubric isolation is by construction (the planner never sees
222
- rubric files); a rejection is recorded in the scoreboard and the refinement
223
- is rolled back automatically (`autoRollbackOnReject`, on by default).
311
+ strictly improves with no case regressing (Self-Harness style).
312
+
313
+ **Evaluator/scorer separation (two-stage, gap A1)** each case × run unit
314
+ is a PAIR of fresh subagents:
315
+
316
+ 1. the **executor** performs the task with its tools and records **concrete
317
+ evidence** of what it did and found — it NEVER sees the rubric, so the
318
+ agent under test cannot optimize toward or self-grade against the grading
319
+ criteria;
320
+ 2. an **independent reviewer** grades that evidence strictly against the
321
+ rubric (the only branch that receives the decrypted rubric), eliminating
322
+ the "self-produced and self-scored" bias.
323
+
324
+ Each cell records the executor's session id, so a score can be drilled back
325
+ to the exact transcript that produced it (trace evidence pointer, gap A4).
326
+
327
+ **Failure-cell protocol (gap A2)** — a unit that cannot produce a score
328
+ (rubric decrypt error, executor/reviewer crash, protocol error) is recorded
329
+ as a **failed** cell, NEVER a zero: aggregation excludes failed cells from
330
+ every mean and counts them (`/evolve benchmark status` shows `(N failed)`),
331
+ and the acceptance rule rejects a round with more failed cells than
332
+ `maxFailedCells` (0 by default) instead of silently averaging a 0 into the
333
+ mean.
334
+
335
+ Aggregation and decisions live in `src/score.ts`. Rubric isolation is by
336
+ construction (the planner never sees rubric files, and the executor branch
337
+ never decrypts); a rejection is recorded in the scoreboard and the
338
+ refinement is rolled back automatically (`autoRollbackOnReject`, on by
339
+ default).
340
+
341
+ Ready-to-use seed cases are in [`examples/`](examples/) — copy-paste the
342
+ statement and rubric to get started in under a minute.
224
343
 
225
344
  ### Real recorded run (ACCEPT)
226
345
 
@@ -234,9 +353,10 @@ acceptance:
234
353
  | re-evaluate | `/evolve benchmark run lint_convention candidate <id>` | **100** — evaluator ran `evolve_list`, hit the memory, quoted it verbatim |
235
354
  | decision | — | `overall: 90 → 100` · `lint_knowledge: 90 → 100` · **DECISION: ACCEPTED** |
236
355
 
237
- The evaluator does not grade model common sense — it inspects the actual
238
- harness state under test (grep, `evolve_list`) and scores against it, so a
239
- harness change measurably moves the score. Earlier runs in the same session
356
+ The executor does not grade model common sense — it inspects the actual
357
+ harness state under test (grep, `evolve_list`) and records what it found;
358
+ the independent reviewer grades that record. A harness change measurably
359
+ moves the score. Earlier runs in the same session
240
360
  produced honest `REJECTED` decisions (0 → 0 stub cases, and 100 → 100
241
361
  where the baseline was already perfect).
242
362
 
@@ -258,6 +378,10 @@ where the baseline was already perfect).
258
378
  | `logLevel` | `1` | file log level: 0=error, 1=info, 2=warn, 3=debug |
259
379
  | `logMaxBytes` | 5 MiB | rotate the log to `plugin.log.1` when it exceeds this size |
260
380
  | `autoRollbackOnReject` | `true` | after a benchmark decision rejects a candidate, roll the refinement back automatically (same engine path as `/evolve rollback` — deterministic, snapshotted, audited) |
381
+ | `localFate` | `true` | gate local-fate dimension: the gate audits the session's local entries on its own cadence and proposes promote/archive — consulted first, never written silently (only meaningful with `autoReview`) |
382
+ | `fateIntervalTurns` | follows `reviewIntervalTurns` | minimum turns between local-fate assessments on the turn-interval path (compaction is unconditional) |
383
+ | `goalBlockedWrapupTurns` | `3` | D3: after this many consecutive gate runs observing the goal phase `blocked`, run one local-fate assessment (`0` disables) |
384
+ | `reviewModel` | (agent's own) | optional model override for the review gate (cheaper model); format: `"provider/model"` or just `"model"` |
261
385
 
262
386
  Example (profile `cordis.patch.yml`):
263
387
 
@@ -282,6 +406,8 @@ pnpm lint # oxlint src test
282
406
 
283
407
  Hit a wall? See [`docs/FAQ.md`](docs/FAQ.md) — real failure/fix records (service planes, schema DSL, structured output, gate counting, verifying prompt injection).
284
408
 
409
+ Where we still lag behind prime-agent `/refine` and penguin-harness — and what to build next: [`docs/gap-analysis.md`](docs/gap-analysis.md) (P0+P1+P2+P3 shipped: evaluator/scorer separation, failure-cell protocol, runtime provenance verification + material-drift detection, usage statistics, auto-decay, case lifecycle + quality gate, entry directory view, review model separation, blast-radius annotations, duration tracking, evolve_complete events, seed benchmark; D1 observation layer + D3 goal-blocked trigger shipped; remaining: cross-process sync on demand + D1/D2 full engineering pending experiment data).
410
+
285
411
  ## Roadmap
286
412
 
287
413
  **Shipped**
@@ -295,6 +421,35 @@ Hit a wall? See [`docs/FAQ.md`](docs/FAQ.md) — real failure/fix records (servi
295
421
  - **gate-proposed archiving** — stale entries are a first-class refine target: the planner can emit `action: "archive"` (kind + id only), which stamps `metadata.archivedAt` through the normal apply path — snapshot, version bump, audit event, and a deterministic rollback inverse that restores the pre-archive state. Archive hides from injection but never deletes; re-archiving an archived entry is rejected, and the base system prompt stays immutable
296
422
  - **automatic rollback on benchmark rejection** — the acceptance loop is closed: when the code-owned decision rejects a candidate, the refinement is reverted automatically through the same engine path as `/evolve rollback` (deterministic inverse edits, snapshotted and audited; configurable via `autoRollbackOnReject`, on by default). Failures report the manual fallback instead of throwing
297
423
  - **per-session log filtering** — `/evolve log [tail N] [session <id>]` keeps only the lines mentioning a given session id (exact token match, drawn from the rendered message and raw args); gate records now carry the session id in their log line
424
+ - **skill standard in the loop** — the planner and gate now author and judge skill entries against the skill-creator/skill-audit standard (author-distilled from the official deepseek-harness 11 skills): every plan call injects the `template.md` facts (builtin distilled guide as fallback) as `<skill_quality_standard>`; apply code-enforces the frontmatter mechanics (no shadowing `---`, no escaping resource refs); materialized SKILL.md files are re-checked and dangling resource references are logged;
425
+ - **guidance skills + user-governed creation** — a second skill form (SKILL.md documents without a python reference) lets recurring workflows be proposed as skills; the gate offers every auto-created skill to the user (固化/不固化) before it lands, with a rejection cooldown — skills grow under governance, never silently
426
+ - **2026-08-17 wrap-up wave (done)**:
427
+ - **`/evolve wrapup`** — a session's local entries get a real exit at session end: mechanical audit (local candidates + global-coverage detection; coverage judges **title similarity only** — a bare id collision with a different title is deliberately NOT coverage, and the real matching global titles are shown to the assessor) → LLM classification (`promote` / `archive` / `keep` + A-form split promotion: archive a mixed entry while promoting a cleaned durable sub-object) → deterministic guards re-checked at apply time (promote can never write a global duplicate; the symmetric archive guard requires user confirmation before an uncovered, user-sourced archive hides content; splits that duplicate a global topic drop to plain archive) → one human approval gate for every global create
428
+ - **gate local-fate dimension** — the wrap-up machinery now runs inside the auto-review gate on its own cadence (`fateIntervalTurns`, compaction unconditional): local entries are audited, classified and partitioned while the session is still running; governed actions are consulted first (one dialog, decline cooldown), covered/operational entries archive silently, compaction applies only silent archives and defers governed actions with an audit record; every decision lands in `reviews.jsonl` and applied actions get a follow-up notice. Apply writes are shared with the wrap-up command (byte-identical proposals)
429
+ - **2026-08-19 research-wave precursors (done)**:
430
+ - **goal-blocked wrap-up (D3)** — a goal stuck in `blocked` for `goalBlockedWrapupTurns` consecutive gate runs (default 3) triggers one local-fate assessment, so the blocked encounter is distilled before the session moves on; the streak resets on any non-blocked run and after each assessment, and declined proposals follow the normal fate cooldown (never nagged). Disable with `goalBlockedWrapupTurns: 0`
431
+ - **failure-signature aggregation (D1 observation layer)** — `/evolve failures` counts every failed review-gate record and benchmark failed cell by deterministic failure class (`rubric-decrypt` / `executor` / `reviewer` / `material-drift` / `gate` / `max-tokens` / …), the data layer a future failure-signature Refiner would route on
432
+ - **bootstrap-update experiment scaffold (D2)** — [`docs/experiment-bootstrap.md`](docs/experiment-bootstrap.md) designs a ≤3-round controlled experiment (fixed reference → evolve harness → candidate) to test whether a refined harness accelerates the next run; `scripts/benchmark-trend.sh` extracts the per-run trend table (overall / totalDurationMs / failed / case-hash consistency) from scoreboards
433
+ - **2026-08-17 gap P0 (done)**:
434
+ - **evaluator/scorer separation** — benchmark evaluation is now two-stage (gap A1): the executor performs the task and records concrete evidence without ever seeing the rubric; an independent reviewer grades that evidence against the rubric (the only branch that decrypts it). The assessed agent can no longer optimize toward or self-grade against the criteria.
435
+ - **failure-cell protocol** — cells carry `status: ok|failed` (gap A2): failed units are excluded from every mean and counted, and the acceptance rule rejects rounds with failures beyond `maxFailedCells` (0 default) instead of averaging a zero into the mean. Scoreboard status/run surfaces failed counts and per-cell reasons.
436
+ - **trace evidence pointer** — each cell records the executor's session id (gap A4), so a score drills back to the exact transcript that earned it
437
+ - **2026-08-18 gap P1 (done)**:
438
+ - **runtime evidence verification (A3)** — cells now record actual `provider`, `model`, and `caseHash` (SHA-256 prefix of statement + rubric) written by the host, not the model; material changes between reference and candidate runs are detected and re-mark the affected candidate cells as failed (version_changed semantics, `score.flagMaterialDrift`), so a drifted round can never be accepted
439
+ - **entry usage statistics (B1)** — injection counts are durably tracked per entry in `<baseDir>/evolve/usage.json`; `evolve_list` shows usage counts; `zeroUsageEntries()` surfaces never-injected local entries as archive candidates
440
+ - **automatic staleness detection (B2)** — entries with zero injection usage AND old recency are flagged `stale` in wrap-up candidates; the LLM assessor is instructed to prefer "archive" for stale entries
441
+ - **2026-08-18 gap P2 (done)**:
442
+ - **case lifecycle + quality gate (A5)** — cases follow a `draft → calibrating → frozen` state machine; `casecheck` runs mechanical quality validation (capability contract, distinguisher, shortcuts); `pilot` performs a single-run calibration; `freeze` locks a case as a formal baseline (requires quality gate pass); `meta` sets case metadata fields
443
+ - **entry directory view (B3)** — the injection block now includes a lightweight directory of ALL non-archived entries (id + title, one line each) when entries exceed the curated cap, giving the model a zero-cost overview
444
+ - **review model separation (C1)** — `reviewModel` config option lets the review gate use a cheaper model than the main agent
445
+ - **blast-radius annotations (C2)** — every edit now carries a `blastRadius` field (`general` / `project` / `session`); the planner is instructed to annotate it and the parser validates values
446
+ - **duration tracking (C3)** — each evaluation cell records `durationMs` (wall-clock time); aggregate totals and decision reports show timing comparison
447
+ - **2026-08-18 code refactoring (done)**:
448
+ - **circular dependency break (P1-1)** — extracted `skill-render.ts` to decouple `skill.ts ↔ skillquality.ts`
449
+ - **LLM call deduplication (P1-2)** — extracted `llm-text.ts` with shared `streamText()` (~107 lines removed from review/planner/wrapup)
450
+ - **config type derivation (P2-1)** — `EvolveConfig` now derived from schemastery schema via `Schemastery.TypeT` (eliminated 20-line handwritten interface)
451
+ - **command.ts split (P2-2)** — 860-line god file split into `goal-command.ts`, `mount-command.ts`, `benchmark-command.ts`, `wrapup-command.ts`
452
+ - **P3 cleanups** — `questionServiceOf()` cast dedup (4 sites), dead exports removed, contradictory comments fixed
298
453
 
299
454
  The upcoming/candidates list is empty for now — future work is driven by real usage.
300
455
 
package/README.zh.md CHANGED
@@ -7,12 +7,12 @@
7
7
  [![CI](https://github.com/ZK-Andy/dsh-continual-evolve/actions/workflows/ci.yml/badge.svg)](https://github.com/ZK-Andy/dsh-continual-evolve/actions/workflows/ci.yml)
8
8
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
9
  [![Node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-339933)](package.json)
10
- [![Tests](https://img.shields.io/badge/tests-184%20passing-brightgreen)]()
10
+ [![Tests](https://img.shields.io/badge/tests-401%20passing-brightgreen)]()
11
11
  [![Status](https://img.shields.io/badge/status-all%20phases%20complete%20%C2%B7%20maintenance-ff69b4)]()
12
12
 
13
13
  [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`)的持续自进化插件:一套**版本化、可审计、可回滚**的 harness 状态层——提示词补充、记忆、技能、子代理规格——从会话轨迹中沉淀而来。
14
14
 
15
- > **状态:全部阶段完成,进入长期维护。** Phase 1–3 交付了完整进化闭环:纯核心引擎、模型工具与 `/evolve` 命令、自动 review 门禁(回合间隔 + 压缩检查点、全局写入人工审批)、真实系统提示词注入(prompt 补充 + 委派规格,空 store 零 token 成本)、benchmark 驱动验证闭环(代码所有计分、非退化接受、rubric ACL)。此后插件随真实使用持续增强——记忆层(排序注入、轨迹引用、归档)、每安装实例独立的 rubric 密钥、插件自带文件日志。已交付与候选清单见"路线图"。
15
+ > **状态:全部阶段完成,进入长期维护。** Phase 1–3 交付了完整进化闭环:纯核心引擎、模型工具与 `/evolve` 命令、自动 review 门禁(回合间隔 + 压缩检查点、全局写入人工审批)、真实系统提示词注入(prompt 补充 + 委派规格,空 store 零 token 成本)、benchmark 驱动验证闭环(代码所有计分、非退化接受、rubric ACL)。此后插件随真实使用持续增强——记忆层(排序注入、轨迹引用、归档)、每安装实例独立的 rubric 密钥、插件自带文件日志、会话收尾(`/evolve wrapup`)、以及门禁的自动 local 归宿维度(local 条目在门禁自有节奏下获得提升或归档的归宿——先征询、绝不静默写入)。已交付与候选清单见"路线图"。
16
16
 
17
17
  ## 背景
18
18
 
@@ -75,27 +75,38 @@ dsh-continual-evolve/
75
75
  │ ├── rollback.ts # 确定性逆操作回滚
76
76
  │ ├── plan.ts # 提案 JSON 解析(截断诊断)
77
77
  │ ├── tool.ts # evolve_* 模型工具(5 个)
78
- │ ├── command.ts # /evolve 命令(含 benchmark 子命令)
78
+ │ ├── command.ts # /evolve 命令分发器 + 共享工具
79
+ │ ├── goal-command.ts # /evolve goal 子命令处理
80
+ │ ├── mount-command.ts # /evolve mount + unmount 子命令处理
81
+ │ ├── benchmark-command.ts # /evolve benchmark 子命令处理
82
+ │ ├── wrapup-command.ts # /evolve wrapup 子命令处理
79
83
  │ ├── planner.ts # ctx.llm 规划器
84
+ │ ├── llm-text.ts # 统一流式文本助手(BlockAssembler + finish 检查)
80
85
  │ ├── render.ts # 有界提示词渲染
81
86
  │ ├── inject.ts # 动态系统提示词段(prompt 补充 + 委派规格,打分排序注入)
82
87
  │ ├── source.ts # 轨迹引用(沉淀条目的 sessionId + 事件 seq)
83
- │ ├── auto.ts # 自动 review 门禁(回合/压缩触发 + 审计,global 感知视图)
88
+ │ ├── auto.ts # 自动 review 门禁(回合/压缩触发 + 审计,global 感知视图,local 归宿阶段)
89
+ │ ├── fate.ts # 门禁 local 归宿维度——自动提议 local 条目提升/归档(先征询、带冷却)
84
90
  │ ├── notify.ts # 门禁可见性——approved 自动沉淀后发送可见通知
85
91
  │ ├── goal.ts # goal 驱动的进化轮次(/evolve goal)
86
92
  │ ├── review.ts # 门禁 LLM 判断(拒绝 global 已覆盖主题的 local 重复沉淀)
87
93
  │ ├── approval.ts # 全局写入人工审批
88
94
  │ ├── skill.ts # 技能物化($DSH_HOME/skills/)
95
+ │ ├── skill-render.ts # 共享技能渲染(skillNameOf + renderSkillMarkdown,打破循环依赖)
96
+ │ ├── skillquality.ts # 自进化环中的技能标准(skill-creator 模板读取 + frontmatter 代码校验)
89
97
  │ ├── mount.ts # 技能热挂载插件(loader.create + 启动恢复)
90
- │ ├── benchmark.ts # benchmark 存储
98
+ │ ├── benchmark.ts # benchmark 存储 + CellScore 类型(含运行时实证字段)
91
99
  │ ├── rubric.ts # rubric ACL(AES-256-GCM 密文信封,自动生成本地密钥)
92
100
  │ ├── logfile.ts # 插件自带文件日志(JSONL exporter + 轮转)
93
101
  │ ├── score.ts # 代码所有聚合 + 接受规则
94
- │ ├── evaluate.ts # 评估矩阵执行器(结构化输出子代理)
102
+ │ ├── evaluate.ts # 两段式评估执行器(执行者产证据 → 独立评审者评分)+ 失败格协议 + 运行时实证校验
95
103
  │ ├── pool.ts # 评估运行的有界并发工作池
96
104
  │ ├── store.ts # store 布局 + 快照 + 结果历史
97
- └── service.ts # 进化引擎(onApplied 钩子)
98
- └── test/ # 20 个文件,184 个测试
105
+ ├── service.ts # 进化引擎(onApplied 钩子)
106
+ │ ├── usage.ts # 条目注入使用率追踪(持久计数、陈旧检测)
107
+ │ ├── failures.ts # 失败签名聚合(门禁 + benchmark 失败按类统计,/evolve failures)
108
+ │ └── wrapup.ts # 会话收尾生命周期(提升/拆解提升到 global、带守卫的归档;共享 proposal 构造器;陈旧信号)
109
+ └── test/ # 28 个文件,401 个测试
99
110
  ```
100
111
 
101
112
  ## 安装
@@ -118,9 +129,12 @@ dsh plugin --profile web add github:ZK-Andy/dsh-continual-evolve
118
129
  /evolve history 已应用的 refinement(回滚用 id)
119
130
  /evolve rollback <id> 确定性回滚某个 refinement
120
131
  /evolve plan [msg] LLM 规划器
132
+ /evolve wrapup 评估本会话 local 条目:可复用的提升到 global(需审批),
133
+ 会话特有的一次性条目归档
121
134
  /evolve archive <id> 归档条目——不再注入(数据保留,可恢复)
122
135
  /evolve unarchive <id> 恢复已归档条目
123
136
  /evolve log [tail N] [session <id>] 查看最近插件日志(默认 50 行;可加会话过滤)
137
+ /evolve failures 失败聚合统计(门禁 + benchmark 失败按类计数——D1 观察层)
124
138
  /evolve export <path> 备份局部 store 为 JSON
125
139
  /evolve import <path> 从导出文件恢复 store
126
140
  /evolve mount <skillId> 热挂载 skill 条目为实时 cordis 插件(工具:skill_<name>)
@@ -140,8 +154,20 @@ dsh plugin --profile web add github:ZK-Andy/dsh-continual-evolve
140
154
  - **打分排序注入**——某类条目超过 6 条封顶时,注入块不再固定取前 6 条:先按与 agent 最近直接用户消息的相关度打分(关键词/BM25 级别,标题命中权重 2×),再按新鲜度排序(`updated_at`,30 天半衰期),让"最新 + 最相关"的条目填满封顶。空 store 零 token 行为不变。
141
155
  - **轨迹引用**——每条新沉淀条目都会记录 `metadata.sourceSession` + `metadata.sourceSeqs`,指向它蒸馏自的直接用户消息(DSH 会话是事件溯源、seq 连续,引用可展开回持久会话日志)。列表显示 `src=<sessionId>:<seqs>`;旧条目不迁移也不报错。
142
156
  - **归档**——`/evolve archive <id>` 让条目不再注入(`metadata.archivedAt`,数据保留、与快照/回滚兼容),`/evolve unarchive <id>` 恢复。归档条目在 `evolve_list` 中标记 `[archived]`,注入跳过,溢出计数不含它们。
157
+ - **会话收尾**——否则会话结束时的 local 条目会变成孤岛(后续会话永远看不到)。`/evolve wrapup` 给它们一个归宿:先机械审计——**全局覆盖只看标题相似**(裸同 id 但标题迥异**不算**覆盖;实际命中的全局标题会展示给分类器,让它对照真实内容判断)——再由模型逐条分类为 `promote` / `archive` / `keep`。提升把可复用条目写入 global store——**经人工审批门禁**,保留轨迹引用并追加 `sourcedFromLocal=<session>:<id>` 反向回引;本地副本随后盖 `promotedTo` 戳退出注入,永不再被提议。**拆解提升(A 形)**:混合条目(持久事实 + 会话快照)可整体归档、同时带一个清洗过的 `promote` 子对象——只有持久部分落进 global,快照留在归档里。**对称归档守卫**:未被全局覆盖、且源自真实用户消息的归档,先征求用户确认才隐藏内容(防过度归档与防过度写入获得同等保护);操作性条目仍静默归档。一切仍走快照/版本/可回滚。
158
+ - **门禁 local 归宿(自动收尾)**——同一套 wrap-up 机制现在以内置节奏(`fateIntervalTurns`)跑在自动 review 门禁里:local 条目在会话进行中就能获得归宿,不必等手动 `/evolve wrapup`。每次到期的门禁运行都会审计候选条目、由分类器分类、再经同一套确定性守卫划分;任何治理动作落地前**先征询用户**(一个弹窗覆盖提升、拆解提升与需确认归档——consultSkillEdits 模式,带拒绝冷却)。被全局覆盖或操作性条目仍静默归档;压缩时刻门禁绝不弹窗:只做静默归档,治理动作以审计记录推迟并指向 `/evolve wrapup`。每次 fate 决策都落进 `reviews.jsonl`(`approved` / `declined` / `deferred` / `assessed` / `failed`),已执行动作通过后续通知可见。应用写入与 wrapup 命令逐字节一致(共享 proposal 构造器)。
143
159
  - **global 感知门禁**——自动 review 门禁与规划器评审的是合并后的 global + local 状态,每条条目标注真实 scope;global 已覆盖的主题会被 declined,不再重复沉淀为 local 条目。
144
160
 
161
+ ## 自进化环中的技能标准
162
+
163
+ 规划器与自动门禁是裸 `ctx.llm` 调用——不在 agent 会话内,无法通过 `skill` 工具加载技能。为了让自进化长出的技能保持在质量线上,插件在运行时引用 **skill-creator** / **skill-audit** 技能(作者从官方 deepseek-harness 11 个技能蒸馏的自建技能,模板事实核验自官方源码 `47f9438`;留在磁盘上作为单一事实源,零复制):
164
+
165
+ - **每次规划调用注入 `<skill_quality_standard>` 块**:`skill-creator/references/template.md` 事实(技能已安装时,位于 `<dshHome>/skills/`),否则用内置精华版兜底(约 1KB,低频调用)。规划器必须把技能提案锚定在轨迹中的**真实触发场景**、不得重复官方 11 技能或已有条目,并逐条自检 7 条结构特征。
166
+ - **门禁按 skill-audit 维度评审**技能相关轨迹(frontmatter 路由、结构特征、段落骨架、防重复),不达标的提案 declined 并说明改进方向。
167
+ - **机械 frontmatter 规则代码强制**(移植 `validate-frontmatter.mjs`):技能正文不得以第二个 `---` 块开头(会遮蔽自动生成的 frontmatter)、资源引用不得越出技能目录;物化后对渲染产物复检,悬空 `references/`/`scripts/` 引用记 warn 日志。
168
+ - **两种技能形态**——`executable`(可执行,保留 python reference 契约,可热挂载为工具)与 `guidance`(指导型:SKILL.md 文档、无 reference,用于反复出现的多步流程,如会话开始/结束、交接流程)。代码强制区分:guidance 技能**不得**携带 reference 或 arguments 契约。
169
+ - **用户治理的技能创建**——门禁绝不静默写技能:规划器提议技能编辑时,先问用户(固化/不固化)再落地;被拒候选在冷却窗口内不再打扰。提案其余部分(记忆/提示词沉淀)不受影响照常进行——技能决策永不阻塞普通沉淀。
170
+
145
171
  ## 日志
146
172
 
147
173
  插件自带文件日志:所有 cordis 日志消息(本插件或其他插件)追加写入 `<dshHome>/evolve/plugin.log`(JSONL、0600,超过 `logMaxBytes` 轮转到 `plugin.log.1`)。与 `dsh web` 的启动方式无关——无需安装额外组件、不依赖启动脚本。查看方式:
@@ -163,9 +189,25 @@ tail -f ~/.dsh/evolve/plugin.log # 实时跟随
163
189
  /evolve benchmark reset <bid> 清空计分板(重跑参考线)
164
190
  /evolve benchmark run <bid> 评估当前状态 → 参考线
165
191
  /evolve benchmark run <bid> candidate <refinementId> 评估进化后状态 → 决策
192
+ /evolve benchmark casecheck <bid> 质量门禁检查所有 case
193
+ /evolve benchmark pilot <bid> <cid> 单次 pilot 运行(校准用)
194
+ /evolve benchmark freeze <bid> <cid> 冻结 case 为正式基线
195
+ /evolve benchmark meta <bid> <cid> <field> <value> 设置 case 元数据(capability/distinguisher/shortcuts)
166
196
  ```
167
197
 
168
- 闭环:冻结参考分 → 进化候选(`/evolve plan`)→ 用同一 case × run 矩阵复测进化后状态 → **代码所有**的接受规则只在总体均值严格提高且无 case 退化时保留候选(Self-Harness 风格)。模型只产出原始细胞级分数;聚合与决策都在 `src/score.ts`。rubric 隔离靠构造(规划器的提示词永远不含 rubric 文件);拒绝会记录进 scoreboard 并自动回滚该 refinement(`autoRollbackOnReject`,默认开)。
198
+ 闭环:冻结参考分 → 进化候选(`/evolve plan`)→ 用同一 case × run 矩阵复测进化后状态 → **代码所有**的接受规则只在总体均值严格提高且无 case 退化时保留候选(Self-Harness 风格)。
199
+
200
+ **评估者/评分者分离(两段式,差距 A1)**——每个 case × run 单元是一对全新子代理:
201
+ 1. **执行者**用工具完成任务并记录**具体证据**(做了什么、查到了什么)——它**永远看不到 rubric**,被测 agent 无法朝评分标准优化、也无法自评;
202
+ 2. **独立评审者**严格按 rubric 给证据打分(唯一接收解密 rubric 的分支),消除"自产自审"偏差。
203
+
204
+ 每个 cell 记录执行者会话 id——分数可下钻回产生该证据的确切会话轨迹(Trace 证据指针,差距 A4)。
205
+
206
+ **失败格协议(差距 A2)**——无法产出分数的单元(rubric 解密失败、执行者/评审者崩溃、协议错误)记为**失败格**,**绝不是 0 分**:聚合从所有均值中排除失败格并计数(`/evolve benchmark status` 显示 `(N failed)`),接受规则在失败格超过 `maxFailedCells`(默认 0)时拒绝整轮,而不是把 0 平均进均值。
207
+
208
+ 聚合与决策都在 `src/score.ts`。rubric 隔离靠构造(规划器的提示词永远不含 rubric 文件、执行者分支永不解密);拒绝会记录进 scoreboard 并自动回滚该 refinement(`autoRollbackOnReject`,默认开)。
209
+
210
+ 开箱即用的种子 case 在 [`examples/`](examples/)——复制粘贴 statement 和 rubric 即可在一分钟内上手。
169
211
 
170
212
  ### 真实运行记录(ACCEPT)
171
213
 
@@ -178,7 +220,7 @@ tail -f ~/.dsh/evolve/plugin.log # 实时跟随
178
220
  | 复测 | `/evolve benchmark run lint_convention candidate <id>` | **100**——评估器跑 `evolve_list` 命中记忆并逐字引用 |
179
221
  | 决策 | — | `overall: 90 → 100` · `lint_knowledge: 90 → 100` · **DECISION: ACCEPTED** |
180
222
 
181
- 评估器评的不是模型常识,而是**被测 harness 状态本身**(grep、`evolve_list` 检查)——所以 harness 的改动会真实地反映在分数上。同一会话早些时候还产生过诚实的 `REJECTED` 决策(0→0 占位符 case、100→100 满分基线无法超越)。
223
+ 执行者评的不是模型常识,而是**实际检查被测 harness 状态**(grep、`evolve_list`)并记录产出,再由独立评审者按 rubric 评分——所以 harness 的改动会真实地反映在分数上。同一会话早些时候还产生过诚实的 `REJECTED` 决策(0→0 占位符 case、100→100 满分基线无法超越)。
182
224
 
183
225
  ## 配置
184
226
 
@@ -198,6 +240,10 @@ tail -f ~/.dsh/evolve/plugin.log # 实时跟随
198
240
  | `logLevel` | `1` | 文件日志级别:0=error、1=info、2=warn、3=debug |
199
241
  | `logMaxBytes` | 5 MiB | 超过该大小轮转到 `plugin.log.1` |
200
242
  | `autoRollbackOnReject` | `true` | benchmark 决策拒绝候选后自动回滚该 refinement(与 `/evolve rollback` 同一引擎路径——确定性、快照、审计) |
243
+ | `localFate` | `true` | 门禁 local 归宿维度:门禁按自有节奏审计本会话 local 条目并提议提升/归档——先征询、绝不静默写入(仅 `autoReview` 开启时有效) |
244
+ | `fateIntervalTurns` | 跟随 `reviewIntervalTurns` | 回合间隔路径上两次 local 归宿评估的最小间隔(压缩时刻无条件触发) |
245
+ | `goalBlockedWrapupTurns` | `3` | D3:连续多少次门禁运行观察到 goal 处于 `blocked` 后触发一次 local 归宿评估(`0` 关闭) |
246
+ | `reviewModel` | (使用 agent 自身模型) | review 门禁的可选模型覆盖(更便宜的模型);格式:`"provider/model"` 或仅 `"model"` |
201
247
 
202
248
  示例(profile `cordis.patch.yml`):
203
249
 
@@ -222,6 +268,8 @@ pnpm lint # oxlint src test
222
268
 
223
269
  遇到问题先看 [`docs/FAQ.md`](docs/FAQ.md)(真实踩坑记录:服务平面、schema DSL、结构化输出、门禁计数、注入验证等)。
224
270
 
271
+ 对照 prime-agent `/refine` 与 penguin-harness 的差距与下一步实施项(P0+P1+P2+P3 已交付:评估者/评分者分离、失败格协议、运行时实证校验+材料漂移检测、使用率统计、自动衰减、case 生命周期+质检、条目目录视图、review 模型分离、blast-radius 标注、耗时追踪、evolve_complete 事件、种子 benchmark;D1 观察层 + D3 goal-blocked 触发已交付;剩余:跨进程同步按需实现 + D1/D2 完整工程化待实验数据):[`docs/gap-analysis.md`](docs/gap-analysis.md)。
272
+
225
273
  ## 路线图
226
274
 
227
275
  **已交付**
@@ -235,6 +283,35 @@ pnpm lint # oxlint src test
235
283
  - **门禁提议归档**——过时条目是一等 refine 目标:规划器可输出 `action: "archive"`(仅需 kind + id),代码经正常 apply 通道盖 `metadata.archivedAt` 戳——快照、版本 +1、审计事件、以及恢复归档前状态的确定性回滚逆编辑。归档隐藏于注入但绝不删除;重复归档被拒绝;基础系统提示词保持不可变
236
284
  - **benchmark 拒绝自动回滚**——接受闭环已闭合:代码所有决策拒绝候选时,refinement 经与 `/evolve rollback` 相同的引擎路径自动撤销(确定性逆编辑、快照 + 审计;`autoRollbackOnReject` 配置,默认开)。失败时给出手动回滚提示而不是抛错
237
285
  - **日志按会话过滤**——`/evolve log [tail N] [session <id>]` 只保留提及指定会话 id 的行(精确 token 匹配,取自渲染消息与原始 args);门禁记录的行现在携带会话 id
286
+ - **自进化环中的技能标准**——规划器与门禁现在按 skill-creator/skill-audit 标准(作者蒸馏自官方 deepseek-harness 11 技能)创作与评审技能条目:每次规划注入 `template.md` 事实(内置精华版兜底)为 `<skill_quality_standard>`;apply 代码强制 frontmatter 机械规则(禁止遮蔽 `---`、禁止越界资源引用);物化后的 SKILL.md 复检,悬空资源引用记日志;
287
+ - **guidance 技能形态 + 用户治理创建**——第二种技能形态(无 python reference 的 SKILL.md 文档技能)让反复出现的流程可以被提议为技能;门禁把每次自动创建的技能先交给用户决定(固化/不固化)再落地,带拒绝冷却——技能在治理下生长,绝不静默写入
288
+ - **2026-08-17 收尾 wave(完成)**:
289
+ - **`/evolve wrapup`**——会话结束时 local 条目有了真正的归宿:先机械审计(local 候选 + 全局覆盖检测;**覆盖只看标题相似**——裸同 id 但标题迥异**不算**覆盖,真正命中的全局标题会展示给分类器)→ LLM 分类(`promote` / `archive` / `keep` + A 形拆解提升:混合条目整体归档、同时提升清洗出的持久子对象)→ 应用时刻确定性守卫复检(promote 永不写出全局重复;对称归档守卫要求用户确认后才隐藏未被覆盖、源自真实对话的条目;清洗标题撞全局主题的拆解降级为普通归档)→ 所有全局 create 走一个人工审批门
290
+ - **门禁 local 归宿维度**——wrap-up 机制现在以内置节奏(`fateIntervalTurns`,压缩时刻无条件)跑在自动 review 门禁里:local 条目在会话进行中被审计、分类、划分;治理动作先征询(一个弹窗、拒绝冷却),被覆盖/操作性条目静默归档,压缩时刻只做静默归档并以审计记录推迟治理动作;每次决策落进 `reviews.jsonl`,已执行动作发后续通知。应用写入与 wrapup 命令共享构造器(逐字节一致)
291
+ - **2026-08-19 研究项先导(完成)**:
292
+ - **goal-blocked 触发收尾(D3)**——goal 连续 `goalBlockedWrapupTurns` 次门禁运行(默认 3)处于 `blocked` 时触发一次 local 归宿评估,把卡住的原因沉淀下来再继续;连胜在任意非 blocked 运行与每次触发后被重置,被拒提案走正常 fate 冷却(绝不打扰);`goalBlockedWrapupTurns: 0` 关闭
293
+ - **失败签名聚合(D1 观察层)**——`/evolve failures` 将门禁失败记录与 benchmark 失败格按确定性失败类(`rubric-decrypt` / `executor` / `reviewer` / `material-drift` / `gate` / `max-tokens` 等)统计——未来 failure-signature Refiner 的底层数据
294
+ - **bootstrap 加速实验脚手架(D2)**——[`docs/experiment-bootstrap.md`](docs/experiment-bootstrap.md) 设计 ≤3 轮对照实验(固定 reference → 沉淀 harness → 候选评估)验证"被提高的 harness 加速下一跑";`scripts/benchmark-trend.sh` 从 scoreboard 提取每轮趋势表(overall / totalDurationMs / failed / case-hash 一致性)
295
+ - **2026-08-17 差距 P0(完成)**:
296
+ - **评估者/评分者分离**——benchmark 评估改为两段式(差距 A1):执行者完成任务并记录具体证据、**永远看不到 rubric**;独立评审者按 rubric 给证据评分(唯一解密 rubric 的分支)。被测 agent 无法朝评分标准优化、也无法自评
297
+ - **失败格协议**——cell 带 `status: ok|failed`(差距 A2):失败格从所有均值中排除并计数,接受规则在失败格超过 `maxFailedCells`(默认 0)时拒绝整轮,而不是把 0 平均进均值。scoreboard status/run 展示失败数与逐格原因
298
+ - **Trace 证据指针**——每个 cell 记录执行者会话 id(差距 A4),分数可下钻回产生它的确切会话轨迹
299
+ - **2026-08-18 差距 P1(完成)**:
300
+ - **运行时实证校验(A3)**——cell 现在记录宿主写入的实际 `provider`、`model` 和 `caseHash`(statement + rubric 的 SHA-256 前缀);参考线与候选运行之间的材料变化会被检出并把受影响候选格重标为失败(version_changed 语义,`score.flagMaterialDrift`),材料漂移的轮次绝不可能被接受
301
+ - **条目使用率统计(B1)**——注入计数持久追踪(`<baseDir>/evolve/usage.json`);`evolve_list` 展示使用次数;`zeroUsageEntries()` 筛选从未注入的 local 条目作为归档候选
302
+ - **自动陈旧检测(B2)**——零注入且低新鲜度的条目标记为 `stale`;LLM 分类器被指示优先归档陈旧条目
303
+ - **2026-08-18 差距 P2(完成)**:
304
+ - **case 生命周期 + 质量门禁(A5)**——case 遵循 `draft → calibrating → frozen` 状态机;`casecheck` 运行机械质量校验(能力合约、区分点、快捷方式);`pilot` 执行单次校准运行;`freeze` 将 case 锁定为正式基线(需通过质量门禁);`meta` 设置 case 元数据字段
305
+ - **条目目录视图(B3)**——注入块现在包含所有非归档条目的轻量目录(id + title,每条一行),在条目超出精选封顶时自动展示,为模型提供零成本全局概览
306
+ - **review 模型分离(C1)**——`reviewModel` 配置项让 review 门禁可使用比主 agent 更便宜的模型
307
+ - **blast-radius 标注(C2)**——每条编辑现在携带 `blastRadius` 字段(`general` / `project` / `session`);规划器被要求标注该字段,解析器验证取值
308
+ - **耗时追踪(C3)**——每个评估单元格记录 `durationMs`(墙钟时间);聚合总计和决策报告展示耗时对比
309
+ - **2026-08-18 代码重构(完成)**:
310
+ - **循环依赖拆解(P1-1)**——抽出 `skill-render.ts` 解耦 `skill.ts ↔ skillquality.ts`
311
+ - **LLM 调用去重(P1-2)**——抽出 `llm-text.ts` 共享 `streamText()`(review/planner/wrapup 删除 ~107 行重复)
312
+ - **config 类型推导(P2-1)**——`EvolveConfig` 改为 `Schemastery.TypeT` 推导(消除 20 行手写接口)
313
+ - **command.ts 拆分(P2-2)**——860 行 god file 拆为 `goal-command.ts`、`mount-command.ts`、`benchmark-command.ts`、`wrapup-command.ts`
314
+ - **P3 清理**——`questionServiceOf()` cast 去重(4 处)、死导出清理、矛盾注释修复
238
315
 
239
316
  候选/待办清单暂时为空——后续工作随真实使用驱动。
240
317
 
package/lib/apply.js CHANGED
@@ -8,7 +8,7 @@ export function applyRefinementProposal(state, proposal, options) {
8
8
  for (const edit of proposal.edits) {
9
9
  const computedId = edit.id ?? (edit.action === "create" ? slug(edit.title ?? edit.kind, edit.kind) : undefined);
10
10
  const id = computedId ?? "";
11
- const validationError = validateEdit(edit, computedId);
11
+ const validationError = validateEdit(edit, computedId, options.scope);
12
12
  if (validationError) {
13
13
  appliedEdits.push({ ...edit, id, applied: false, error: validationError });
14
14
  continue;
@@ -82,6 +82,7 @@ export function applyRefinementProposal(state, proposal, options) {
82
82
  : {}),
83
83
  }
84
84
  : {};
85
+ const skillKind = edit.skill_kind ?? before?.skill_kind;
85
86
  const after = {
86
87
  id,
87
88
  kind: edit.kind,
@@ -91,6 +92,7 @@ export function applyRefinementProposal(state, proposal, options) {
91
92
  scope: before?.scope ?? options.scope ?? "local",
92
93
  reference: edit.reference ?? before?.reference ?? {},
93
94
  arguments: edit.arguments ?? before?.arguments ?? {},
95
+ ...(skillKind !== undefined ? { skill_kind: skillKind } : {}),
94
96
  metadata: { ...sourceMetadata, ...(edit.metadata ?? before?.metadata ?? {}) },
95
97
  source: "evolve",
96
98
  created_at: before?.created_at ?? now,
package/lib/approval.d.ts CHANGED
@@ -6,6 +6,31 @@
6
6
  */
7
7
  import type { Context } from "@deepseek-ai/cordis";
8
8
  import type { Agent } from "@deepseek-ai/dsh-agent";
9
+ export interface QuestionService {
10
+ ask(request: {
11
+ questions: {
12
+ id: string;
13
+ question: string;
14
+ options?: {
15
+ label: string;
16
+ description?: string;
17
+ }[];
18
+ }[];
19
+ agent?: Agent;
20
+ signal?: AbortSignal;
21
+ }): Promise<{
22
+ answers?: {
23
+ id: string;
24
+ selected?: string[];
25
+ }[];
26
+ }>;
27
+ }
28
+ /**
29
+ * Lazily resolve the userQuestions service from the context.
30
+ * Returns undefined when the service is not loaded — callers decide
31
+ * whether that is an error or a fallback.
32
+ */
33
+ export declare function questionServiceOf(ctx: Context): QuestionService | undefined;
9
34
  /**
10
35
  * Ask the user to approve a global edit. Throws when the service is missing,
11
36
  * the user declines, or the question cannot be answered.
package/lib/approval.js CHANGED
@@ -1,9 +1,17 @@
1
+ /**
2
+ * Lazily resolve the userQuestions service from the context.
3
+ * Returns undefined when the service is not loaded — callers decide
4
+ * whether that is an error or a fallback.
5
+ */
6
+ export function questionServiceOf(ctx) {
7
+ return ctx.userQuestions;
8
+ }
1
9
  /**
2
10
  * Ask the user to approve a global edit. Throws when the service is missing,
3
11
  * the user declines, or the question cannot be answered.
4
12
  */
5
13
  export async function requireGlobalApproval(ctx, agent, signal, what) {
6
- const userQuestions = ctx.userQuestions;
14
+ const userQuestions = questionServiceOf(ctx);
7
15
  if (!userQuestions) {
8
16
  throw new Error("global evolution edits require the userQuestions service (load @deepseek-ai/dsh-user-questions)");
9
17
  }