conductor-board 2.2.0 → 2.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.
package/cli/writer.js CHANGED
@@ -338,72 +338,95 @@ export async function runStatusInit(args) {
338
338
  const runId =
339
339
  (typeof flag(args, ["--run-id"]) === "string" && flag(args, ["--run-id"])) ||
340
340
  now().replace(/\.\d+Z$/, "").replace(/:/g, "-");
341
- const steps = {};
341
+ const wfName = doc.name || "workflow";
342
342
 
343
- // Phase 0 (§10.2): auto-inject improvement cards from PROVEN this-conductor
344
- // knowledge BEFORE the workflow steps. Entries with current/proposed apply
345
- // automatically; structural ones (new_step/remove_step/reorder) are flagged
346
- // for human approval. A _validate card closes the phase.
347
- const STRUCTURAL = new Set(["new_step", "remove_step", "reorder"]);
348
- const knowledge = (Array.isArray(doc.knowledge) ? doc.knowledge : []).filter(
349
- (k) => k && typeof k === "object" && k.title,
350
- );
351
- const slug = (t) => String(t).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
352
- const seen = new Set();
353
-
354
- // _improve::read-knowledge leads the phase: read + categorize the knowledge.
355
- if (knowledge.length > 0) {
356
- const cat = (s) => knowledge.filter((k) => (k.status || "emerging") === s).length;
357
- const cross = knowledge.filter((k) => (k.scope || "this-conductor") !== "this-conductor").length;
358
- steps["_improve::read-knowledge"] = {
359
- status: "pending",
360
- gate: "pending",
361
- attempt: 1,
362
- improve: {
363
- title: "Read knowledge",
364
- kind: "read-knowledge",
365
- note:
366
- `${cat("proven")} proven · ${cat("emerging")} emerging · ${cat("applied")} applied · ` +
367
- `${cross} cross-cutting`,
368
- },
369
- };
343
+ // §6.2 every run gets a human name: {workflow}-run-{N}-{timestamp}. N is the
344
+ // count of archived runs + 1; the timestamp is the run id trimmed to minutes.
345
+ const nameSlug = String(wfName).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
346
+ const historyDir = path.join(path.dirname(sp), "history");
347
+ let priorRuns = 0;
348
+ try {
349
+ priorRuns = fs.readdirSync(historyDir).filter((f) => f.endsWith(".json")).length;
350
+ } catch {
351
+ /* no history yet */
370
352
  }
353
+ const tsShort = runId.replace(/-\d{2}$/, ""); // 2026-06-04T12-30-00 → 2026-06-04T12-30
354
+ const runName =
355
+ (typeof flag(args, ["--run-name"]) === "string" && flag(args, ["--run-name"])) ||
356
+ `${nameSlug}-run-${priorRuns + 1}-${tsShort}`;
357
+
358
+ // §6.1 — auto_improve (default on). When off, the Phase 0 self-improvement
359
+ // pass is fully disabled: no improvement cards injected at all.
360
+ const autoImprove = doc.auto_improve !== false;
371
361
 
362
+ const steps = {};
372
363
  let improvements = 0;
373
- for (const k of knowledge) {
374
- if ((k.status || "emerging") !== "proven") continue;
375
- if ((k.scope || "this-conductor") !== "this-conductor") continue;
376
- const structural = STRUCTURAL.has(k.type);
377
- const textChange = k.current && k.proposed;
378
- if (!structural && !textChange) continue; // proven but nothing actionable
379
- let id = `_improve::${slug(k.title)}`;
380
- while (seen.has(id)) id += "-x";
381
- seen.add(id);
382
- steps[id] = {
383
- status: "pending",
384
- gate: "pending",
385
- attempt: 1,
386
- improve: {
387
- step: k.step,
388
- title: k.title,
389
- current: k.current,
390
- proposed: k.proposed,
391
- note: k.note,
392
- observed: k.observed || 1,
393
- scope: k.scope || "this-conductor",
394
- structural,
395
- kind: k.type || "instruction",
396
- },
397
- };
398
- improvements += 1;
399
- }
400
- if (improvements > 0) {
401
- steps["_improve::validate"] = {
402
- status: "pending",
403
- gate: "pending",
404
- attempt: 1,
405
- improve: { title: "Validate conductor", kind: "validate" },
406
- };
364
+
365
+ if (autoImprove) {
366
+ // Phase 0 (§10.2): auto-inject improvement cards from PROVEN this-conductor
367
+ // knowledge BEFORE the workflow steps. Entries with current/proposed apply
368
+ // automatically; structural ones (new_step/remove_step/reorder) are flagged
369
+ // for human approval. A _validate card closes the phase.
370
+ const STRUCTURAL = new Set(["new_step", "remove_step", "reorder"]);
371
+ const knowledge = (Array.isArray(doc.knowledge) ? doc.knowledge : []).filter(
372
+ (k) => k && typeof k === "object" && k.title,
373
+ );
374
+ const slug = (t) => String(t).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
375
+ const seen = new Set();
376
+
377
+ // _improve::read-knowledge leads the phase: read + categorize the knowledge.
378
+ if (knowledge.length > 0) {
379
+ const cat = (s) => knowledge.filter((k) => (k.status || "emerging") === s).length;
380
+ const cross = knowledge.filter((k) => (k.scope || "this-conductor") !== "this-conductor").length;
381
+ steps["_improve::read-knowledge"] = {
382
+ status: "pending",
383
+ gate: "pending",
384
+ attempt: 1,
385
+ improve: {
386
+ title: "Read knowledge",
387
+ kind: "read-knowledge",
388
+ note:
389
+ `${cat("proven")} proven · ${cat("emerging")} emerging · ${cat("applied")} applied · ` +
390
+ `${cross} cross-cutting`,
391
+ },
392
+ };
393
+ }
394
+
395
+ for (const k of knowledge) {
396
+ if ((k.status || "emerging") !== "proven") continue;
397
+ if ((k.scope || "this-conductor") !== "this-conductor") continue;
398
+ const structural = STRUCTURAL.has(k.type);
399
+ const textChange = k.current && k.proposed;
400
+ if (!structural && !textChange) continue; // proven but nothing actionable
401
+ let id = `_improve::${slug(k.title)}`;
402
+ while (seen.has(id)) id += "-x";
403
+ seen.add(id);
404
+ steps[id] = {
405
+ status: "pending",
406
+ gate: "pending",
407
+ attempt: 1,
408
+ improve: {
409
+ step: k.step,
410
+ title: k.title,
411
+ current: k.current,
412
+ proposed: k.proposed,
413
+ note: k.note,
414
+ observed: k.observed || 1,
415
+ scope: k.scope || "this-conductor",
416
+ structural,
417
+ kind: k.type || "instruction",
418
+ },
419
+ };
420
+ improvements += 1;
421
+ }
422
+ if (improvements > 0) {
423
+ steps["_improve::validate"] = {
424
+ status: "pending",
425
+ gate: "pending",
426
+ attempt: 1,
427
+ improve: { title: "Validate conductor", kind: "validate" },
428
+ };
429
+ }
407
430
  }
408
431
 
409
432
  for (const st of doc.steps || []) {
@@ -414,8 +437,10 @@ export async function runStatusInit(args) {
414
437
  : { status: "pending", gate: "pending", attempt: 1 };
415
438
  }
416
439
  const status = {
417
- workflow: doc.name || "workflow",
440
+ workflow: wfName,
418
441
  run_id: runId,
442
+ run_name: runName,
443
+ auto_improve: autoImprove,
419
444
  status: "running",
420
445
  goal: (doc.description || "").trim().replace(/\s+/g, " "),
421
446
  current_step: null,