dsh-continual-evolve 0.2.0 → 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.
Files changed (66) hide show
  1. package/README.md +83 -371
  2. package/README.zh.md +84 -235
  3. package/lib/apply.js +8 -2
  4. package/lib/approval.d.ts +6 -0
  5. package/lib/approval.js +9 -1
  6. package/lib/auto.d.ts +55 -4
  7. package/lib/auto.js +61 -5
  8. package/lib/benchmark-command.d.ts +9 -0
  9. package/lib/benchmark-command.js +333 -0
  10. package/lib/benchmark.d.ts +70 -0
  11. package/lib/benchmark.js +107 -1
  12. package/lib/command.d.ts +3 -0
  13. package/lib/command.js +62 -441
  14. package/lib/evaluate.d.ts +7 -0
  15. package/lib/evaluate.js +22 -7
  16. package/lib/evolve-event.d.ts +38 -0
  17. package/lib/evolve-event.js +49 -0
  18. package/lib/failures.d.ts +39 -0
  19. package/lib/failures.js +170 -0
  20. package/lib/fate.d.ts +5 -2
  21. package/lib/fate.js +13 -8
  22. package/lib/goal-command.d.ts +7 -0
  23. package/lib/goal-command.js +37 -0
  24. package/lib/index.d.ts +51 -25
  25. package/lib/index.js +33 -1
  26. package/lib/inject.d.ts +24 -1
  27. package/lib/inject.js +93 -5
  28. package/lib/llm-text.d.ts +30 -0
  29. package/lib/llm-text.js +49 -0
  30. package/lib/mount-command.d.ts +10 -0
  31. package/lib/mount-command.js +48 -0
  32. package/lib/plan.js +5 -0
  33. package/lib/planner.d.ts +1 -1
  34. package/lib/planner.js +13 -39
  35. package/lib/promotion.d.ts +62 -0
  36. package/lib/promotion.js +102 -0
  37. package/lib/render.d.ts +1 -3
  38. package/lib/render.js +0 -4
  39. package/lib/review.d.ts +4 -1
  40. package/lib/review.js +10 -38
  41. package/lib/rollback.d.ts +1 -3
  42. package/lib/rollback.js +0 -8
  43. package/lib/score.d.ts +15 -0
  44. package/lib/score.js +74 -5
  45. package/lib/service.d.ts +2 -2
  46. package/lib/service.js +7 -3
  47. package/lib/skill-render.d.ts +23 -0
  48. package/lib/skill-render.js +68 -0
  49. package/lib/skill.d.ts +2 -5
  50. package/lib/skill.js +2 -29
  51. package/lib/skillquality.d.ts +1 -2
  52. package/lib/skillquality.js +2 -2
  53. package/lib/state.js +6 -1
  54. package/lib/store.d.ts +1 -3
  55. package/lib/store.js +0 -7
  56. package/lib/tool.js +22 -1
  57. package/lib/types.d.ts +8 -0
  58. package/lib/usage.d.ts +45 -0
  59. package/lib/usage.js +115 -0
  60. package/lib/validate.d.ts +12 -2
  61. package/lib/validate.js +26 -1
  62. package/lib/wrapup-command.d.ts +9 -0
  63. package/lib/wrapup-command.js +212 -0
  64. package/lib/wrapup.d.ts +29 -15
  65. package/lib/wrapup.js +69 -42
  66. package/package.json +10 -8
package/lib/benchmark.js CHANGED
@@ -106,6 +106,8 @@ export function addCase(baseDir, bid, title, statement, rubric, rubricKey) {
106
106
  // resolveRubricKey) and the dev key here is only a defensive last resort.
107
107
  const stored = rubricKey ? encryptRubric(rubric, rubricKey) : encryptRubric(rubric, deriveKey(DEV_RUBRIC_KEY));
108
108
  writeFileSync(join(caseDir, "rubric.json"), `${JSON.stringify(stored, null, 2)}\n`, "utf8");
109
+ // A5: initialize case metadata for quality gate.
110
+ saveCaseMeta(baseDir, bid, id, defaultCaseMeta());
109
111
  return { id, title: title.trim(), statement, rubric };
110
112
  }
111
113
  export function listCases(baseDir, bid) {
@@ -121,7 +123,8 @@ export function listCases(baseDir, bid) {
121
123
  try {
122
124
  const statement = readFileSync(statementPath, "utf8");
123
125
  const rubric = JSON.parse(readFileSync(rubricPath, "utf8"));
124
- return { id, title: id, statement, rubric };
126
+ const meta = loadCaseMeta(baseDir, bid, id);
127
+ return { id, title: id, statement, rubric, ...(meta ? { status: meta.status } : {}) };
125
128
  }
126
129
  catch {
127
130
  return undefined;
@@ -149,6 +152,109 @@ export function loadScoreboard(baseDir, bid) {
149
152
  export function saveScoreboard(baseDir, bid, board) {
150
153
  writeFileSync(join(benchmarkDir(baseDir, bid), "scoreboard.json"), `${JSON.stringify(board, null, 2)}\n`, "utf8");
151
154
  }
155
+ // ── Case meta (gap A5: quality gate + calibration history) ────────────
156
+ export function loadCaseMeta(baseDir, bid, cid) {
157
+ const path = join(benchmarkDir(baseDir, bid), "cases", cid, "meta.json");
158
+ if (!existsSync(path))
159
+ return undefined;
160
+ try {
161
+ return JSON.parse(readFileSync(path, "utf8"));
162
+ }
163
+ catch {
164
+ return undefined;
165
+ }
166
+ }
167
+ export function saveCaseMeta(baseDir, bid, cid, meta) {
168
+ const metaDir = join(benchmarkDir(baseDir, bid), "cases", cid);
169
+ mkdirSync(metaDir, { recursive: true });
170
+ writeFileSync(join(metaDir, "meta.json"), `${JSON.stringify(meta, null, 2)}\n`, "utf8");
171
+ }
172
+ /** List case metas for all cases in a benchmark (missing meta → defaults). */
173
+ export function listCaseMetas(baseDir, bid) {
174
+ const result = new Map();
175
+ const cases = listCases(baseDir, bid);
176
+ for (const c of cases) {
177
+ const meta = loadCaseMeta(baseDir, bid, c.id);
178
+ if (meta) {
179
+ result.set(c.id, meta);
180
+ }
181
+ }
182
+ return result;
183
+ }
184
+ /** Check whether a case is frozen (immutable). */
185
+ export function isCaseFrozen(baseDir, bid, cid) {
186
+ const meta = loadCaseMeta(baseDir, bid, cid);
187
+ return meta?.status === "frozen";
188
+ }
189
+ /**
190
+ * Transition a case's lifecycle state. Throws on illegal transitions.
191
+ * draft → calibrating (start pilot)
192
+ * calibrating → draft (abandon calibration)
193
+ * calibrating → frozen (lock baseline)
194
+ */
195
+ export function transitionCaseStatus(baseDir, bid, cid, to) {
196
+ const meta = loadCaseMeta(baseDir, bid, cid) ?? defaultCaseMeta();
197
+ const from = meta.status;
198
+ const valid = (from === "draft" && to === "calibrating") ||
199
+ (from === "calibrating" && to === "draft") ||
200
+ (from === "calibrating" && to === "frozen");
201
+ if (!valid) {
202
+ throw new Error(`illegal case status transition: ${from} → ${to}`);
203
+ }
204
+ meta.status = to;
205
+ saveCaseMeta(baseDir, bid, cid, meta);
206
+ return meta;
207
+ }
208
+ function defaultCaseMeta() {
209
+ return {
210
+ status: "draft",
211
+ capability: "",
212
+ distinguisher: "",
213
+ shortcuts: "",
214
+ calibrationHistory: [],
215
+ };
216
+ }
217
+ /**
218
+ * Quality-check a case: mechanical validation without LLM calls.
219
+ * Returns human-readable problems; empty array means the case passes.
220
+ */
221
+ export function caseCheckProblems(baseDir, bid, cid) {
222
+ const problems = [];
223
+ const statementPath = join(benchmarkDir(baseDir, bid), "cases", cid, "statement.md");
224
+ if (!existsSync(statementPath)) {
225
+ problems.push("statement.md missing");
226
+ return problems;
227
+ }
228
+ const statement = readFileSync(statementPath, "utf8").trim();
229
+ if (statement.length < 20) {
230
+ problems.push(`statement too short (${statement.length} chars, minimum 20)`);
231
+ }
232
+ const rubricPath = join(benchmarkDir(baseDir, bid), "cases", cid, "rubric.json");
233
+ if (!existsSync(rubricPath)) {
234
+ problems.push("rubric.json missing");
235
+ return problems;
236
+ }
237
+ try {
238
+ const raw = readFileSync(rubricPath, "utf8");
239
+ JSON.parse(raw);
240
+ }
241
+ catch {
242
+ problems.push("rubric.json is not valid JSON");
243
+ }
244
+ const meta = loadCaseMeta(baseDir, bid, cid);
245
+ if (!meta) {
246
+ problems.push("meta.json missing (run /evolve benchmark casecheck to initialize)");
247
+ }
248
+ else {
249
+ if (!meta.capability)
250
+ problems.push("capability contract is empty");
251
+ if (!meta.distinguisher)
252
+ problems.push("distinguisher is empty");
253
+ if (!meta.shortcuts)
254
+ problems.push("shortcuts annotation is empty");
255
+ }
256
+ return problems;
257
+ }
152
258
  export function removeBenchmark(baseDir, bid) {
153
259
  const dir = benchmarkDir(baseDir, bid);
154
260
  if (existsSync(dir)) {
package/lib/command.d.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  import type { Context } from "@deepseek-ai/cordis";
6
6
  import type { HarnessEntry, HarnessState, RefinementKind } from "./types.js";
7
7
  import type { EvolutionEngine } from "./service.js";
8
+ import type { PromotionPolicy } from "./promotion.js";
8
9
  export interface CommandGateOptions {
9
10
  requireGlobalApproval: boolean;
10
11
  }
@@ -12,6 +13,8 @@ export interface CommandRuntimeOptions {
12
13
  rubricKey: Buffer;
13
14
  /** When a benchmark decision rejects a candidate, roll the refinement back automatically. */
14
15
  autoRollbackOnReject: boolean;
16
+ /** Mechanical promotion guards for wrapup/fate (2026-08-22 policy). */
17
+ promotionPolicy: PromotionPolicy;
15
18
  }
16
19
  export declare function registerEvolveCommand(ctx: Context, engine: EvolutionEngine, opts: CommandGateOptions, runtime: CommandRuntimeOptions): void;
17
20
  /**