zelari-code 2.0.0-alpha.5 → 2.0.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 (51) hide show
  1. package/dist/cli/headless.js +9 -0
  2. package/dist/cli/headless.js.map +1 -1
  3. package/dist/cli/headlessE2eSession.test.js +189 -0
  4. package/dist/cli/headlessE2eSession.test.js.map +1 -0
  5. package/dist/cli/headlessSessionEvent.test.js +83 -0
  6. package/dist/cli/headlessSessionEvent.test.js.map +1 -0
  7. package/dist/cli/headlessSpine.js +25 -0
  8. package/dist/cli/headlessSpine.js.map +1 -1
  9. package/dist/cli/hooks/chatState.js.map +1 -1
  10. package/dist/cli/hooks/eventsToMessages.js.map +1 -1
  11. package/dist/cli/hooks/useChatTurn.js +40 -7
  12. package/dist/cli/hooks/useChatTurn.js.map +1 -1
  13. package/dist/cli/hooks/useSession.js +0 -2
  14. package/dist/cli/hooks/useSession.js.map +1 -1
  15. package/dist/cli/kraken/evidenceAnchoring.test.js +92 -0
  16. package/dist/cli/kraken/evidenceAnchoring.test.js.map +1 -0
  17. package/dist/cli/kraken/nativeVerification.js +113 -0
  18. package/dist/cli/kraken/nativeVerification.js.map +1 -0
  19. package/dist/cli/kraken/nativeVerification.test.js +207 -0
  20. package/dist/cli/kraken/nativeVerification.test.js.map +1 -0
  21. package/dist/cli/kraken/strictDefaults.test.js +149 -0
  22. package/dist/cli/kraken/strictDefaults.test.js.map +1 -0
  23. package/dist/cli/kraken/verificationBridge.js +121 -8
  24. package/dist/cli/kraken/verificationBridge.js.map +1 -1
  25. package/dist/cli/kraken/verificationBridge.session.test.js +104 -0
  26. package/dist/cli/kraken/verificationBridge.session.test.js.map +1 -0
  27. package/dist/cli/kraken/verificationBridge.test.js +63 -17
  28. package/dist/cli/kraken/verificationBridge.test.js.map +1 -1
  29. package/dist/cli/kraken/verifierAdvisoryLock.test.js +154 -0
  30. package/dist/cli/kraken/verifierAdvisoryLock.test.js.map +1 -0
  31. package/dist/cli/kraken/verifierResolution.js +28 -0
  32. package/dist/cli/kraken/verifierResolution.js.map +1 -0
  33. package/dist/cli/kraken/verifierRoundTrip.test.js +166 -0
  34. package/dist/cli/kraken/verifierRoundTrip.test.js.map +1 -0
  35. package/dist/cli/legacyContextIsolation.test.js +65 -0
  36. package/dist/cli/legacyContextIsolation.test.js.map +1 -0
  37. package/dist/cli/main.bundled.js +893 -317
  38. package/dist/cli/main.bundled.js.map +4 -4
  39. package/dist/cli/missionContinuation.test.js +117 -0
  40. package/dist/cli/missionContinuation.test.js.map +1 -0
  41. package/dist/cli/profileMatrix.test.js +130 -0
  42. package/dist/cli/profileMatrix.test.js.map +1 -0
  43. package/dist/cli/runHeadless.js +68 -8
  44. package/dist/cli/runHeadless.js.map +1 -1
  45. package/dist/cli/sessionManager.js +7 -0
  46. package/dist/cli/sessionManager.js.map +1 -1
  47. package/dist/cli/sessionSpine.js +43 -3
  48. package/dist/cli/sessionSpine.js.map +1 -1
  49. package/dist/cli/zelariMission.js +16 -0
  50. package/dist/cli/zelariMission.js.map +1 -1
  51. package/package.json +3 -3
@@ -3288,10 +3288,10 @@ function mergeDefs(...defs) {
3288
3288
  function cloneDef(schema) {
3289
3289
  return mergeDefs(schema._zod.def);
3290
3290
  }
3291
- function getElementAtPath(obj, path63) {
3292
- if (!path63)
3291
+ function getElementAtPath(obj, path64) {
3292
+ if (!path64)
3293
3293
  return obj;
3294
- return path63.reduce((acc, key) => acc?.[key], obj);
3294
+ return path64.reduce((acc, key) => acc?.[key], obj);
3295
3295
  }
3296
3296
  function promiseAllObject(promisesObj) {
3297
3297
  const keys = Object.keys(promisesObj);
@@ -3619,11 +3619,11 @@ function explicitlyAborted(x, startIndex = 0) {
3619
3619
  }
3620
3620
  return false;
3621
3621
  }
3622
- function prefixIssues(path63, issues) {
3622
+ function prefixIssues(path64, issues) {
3623
3623
  return issues.map((iss) => {
3624
3624
  var _a3;
3625
3625
  (_a3 = iss).path ?? (_a3.path = []);
3626
- iss.path.unshift(path63);
3626
+ iss.path.unshift(path64);
3627
3627
  return iss;
3628
3628
  });
3629
3629
  }
@@ -3841,16 +3841,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
3841
3841
  }
3842
3842
  function formatError(error51, mapper = (issue2) => issue2.message) {
3843
3843
  const fieldErrors = { _errors: [] };
3844
- const processError = (error52, path63 = []) => {
3844
+ const processError = (error52, path64 = []) => {
3845
3845
  for (const issue2 of error52.issues) {
3846
3846
  if (issue2.code === "invalid_union" && issue2.errors.length) {
3847
- issue2.errors.map((issues) => processError({ issues }, [...path63, ...issue2.path]));
3847
+ issue2.errors.map((issues) => processError({ issues }, [...path64, ...issue2.path]));
3848
3848
  } else if (issue2.code === "invalid_key") {
3849
- processError({ issues: issue2.issues }, [...path63, ...issue2.path]);
3849
+ processError({ issues: issue2.issues }, [...path64, ...issue2.path]);
3850
3850
  } else if (issue2.code === "invalid_element") {
3851
- processError({ issues: issue2.issues }, [...path63, ...issue2.path]);
3851
+ processError({ issues: issue2.issues }, [...path64, ...issue2.path]);
3852
3852
  } else {
3853
- const fullpath = [...path63, ...issue2.path];
3853
+ const fullpath = [...path64, ...issue2.path];
3854
3854
  if (fullpath.length === 0) {
3855
3855
  fieldErrors._errors.push(mapper(issue2));
3856
3856
  } else {
@@ -3877,17 +3877,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
3877
3877
  }
3878
3878
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
3879
3879
  const result = { errors: [] };
3880
- const processError = (error52, path63 = []) => {
3880
+ const processError = (error52, path64 = []) => {
3881
3881
  var _a3, _b;
3882
3882
  for (const issue2 of error52.issues) {
3883
3883
  if (issue2.code === "invalid_union" && issue2.errors.length) {
3884
- issue2.errors.map((issues) => processError({ issues }, [...path63, ...issue2.path]));
3884
+ issue2.errors.map((issues) => processError({ issues }, [...path64, ...issue2.path]));
3885
3885
  } else if (issue2.code === "invalid_key") {
3886
- processError({ issues: issue2.issues }, [...path63, ...issue2.path]);
3886
+ processError({ issues: issue2.issues }, [...path64, ...issue2.path]);
3887
3887
  } else if (issue2.code === "invalid_element") {
3888
- processError({ issues: issue2.issues }, [...path63, ...issue2.path]);
3888
+ processError({ issues: issue2.issues }, [...path64, ...issue2.path]);
3889
3889
  } else {
3890
- const fullpath = [...path63, ...issue2.path];
3890
+ const fullpath = [...path64, ...issue2.path];
3891
3891
  if (fullpath.length === 0) {
3892
3892
  result.errors.push(mapper(issue2));
3893
3893
  continue;
@@ -3919,8 +3919,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
3919
3919
  }
3920
3920
  function toDotPath(_path) {
3921
3921
  const segs = [];
3922
- const path63 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3923
- for (const seg of path63) {
3922
+ const path64 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3923
+ for (const seg of path64) {
3924
3924
  if (typeof seg === "number")
3925
3925
  segs.push(`[${seg}]`);
3926
3926
  else if (typeof seg === "symbol")
@@ -17423,13 +17423,13 @@ function resolveRef(ref, ctx) {
17423
17423
  if (!ref.startsWith("#")) {
17424
17424
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
17425
17425
  }
17426
- const path63 = ref.slice(1).split("/").filter(Boolean);
17427
- if (path63.length === 0) {
17426
+ const path64 = ref.slice(1).split("/").filter(Boolean);
17427
+ if (path64.length === 0) {
17428
17428
  return ctx.rootSchema;
17429
17429
  }
17430
17430
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
17431
- if (path63[0] === defsKey) {
17432
- const key = path63[1];
17431
+ if (path64[0] === defsKey) {
17432
+ const key = path64[1];
17433
17433
  if (!key || !ctx.defs[key]) {
17434
17434
  throw new Error(`Reference not found: ${ref}`);
17435
17435
  }
@@ -19750,11 +19750,11 @@ var init_tools = __esm({
19750
19750
  if (!ctx.addDocument)
19751
19751
  return "Knowledge vault tool not available.";
19752
19752
  const title = args["title"] || "New Document";
19753
- const path63 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
19753
+ const path64 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
19754
19754
  const content = args["content"] || "";
19755
19755
  const tags = args["tags"] || [];
19756
19756
  ctx.addDocument({
19757
- path: path63,
19757
+ path: path64,
19758
19758
  title,
19759
19759
  content,
19760
19760
  format: "markdown",
@@ -19763,7 +19763,7 @@ var init_tools = __esm({
19763
19763
  workspaceId: ctx.workspaceId
19764
19764
  });
19765
19765
  ctx.addActivity("vault", "created document", title);
19766
- return `Document "${title}" created at "${path63}".`;
19766
+ return `Document "${title}" created at "${path64}".`;
19767
19767
  }
19768
19768
  }
19769
19769
  ];
@@ -24197,11 +24197,11 @@ var init_synthesisAudit = __esm({
24197
24197
  import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
24198
24198
  import { join as join2 } from "node:path";
24199
24199
  function loadNfrSpec(zelariRoot) {
24200
- const path63 = join2(zelariRoot, "nfr-spec.json");
24201
- if (!existsSync7(path63))
24200
+ const path64 = join2(zelariRoot, "nfr-spec.json");
24201
+ if (!existsSync7(path64))
24202
24202
  return null;
24203
24203
  try {
24204
- const raw = JSON.parse(readFileSync7(path63, "utf8"));
24204
+ const raw = JSON.parse(readFileSync7(path64, "utf8"));
24205
24205
  if (raw.version !== 1 || !Array.isArray(raw.targets))
24206
24206
  return null;
24207
24207
  return raw;
@@ -26507,9 +26507,9 @@ var init_types4 = __esm({
26507
26507
  import { readFileSync as readFileSync12 } from "node:fs";
26508
26508
  import { join as join8 } from "node:path";
26509
26509
  function readLessonsDeduped(zelariRoot) {
26510
- const path63 = join8(zelariRoot, LESSONS_FILE);
26510
+ const path64 = join8(zelariRoot, LESSONS_FILE);
26511
26511
  try {
26512
- const raw = readFileSync12(path63, "utf8");
26512
+ const raw = readFileSync12(path64, "utf8");
26513
26513
  const byId = /* @__PURE__ */ new Map();
26514
26514
  for (const line of raw.split(/\r?\n/)) {
26515
26515
  if (!line.trim())
@@ -26610,8 +26610,8 @@ function keywordsFrom(check2, signature) {
26610
26610
  return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
26611
26611
  }
26612
26612
  function writeLesson(zelariRoot, lesson) {
26613
- const path63 = join9(zelariRoot, LESSONS_FILE);
26614
- appendFileSync(path63, `${JSON.stringify(lesson)}
26613
+ const path64 = join9(zelariRoot, LESSONS_FILE);
26614
+ appendFileSync(path64, `${JSON.stringify(lesson)}
26615
26615
  `, "utf8");
26616
26616
  }
26617
26617
  function findSimilar(lessons, signature) {
@@ -27225,9 +27225,9 @@ function findCycle(nodes) {
27225
27225
  if (color.get(start) !== WHITE)
27226
27226
  continue;
27227
27227
  const stack = [[start, 0]];
27228
- const path63 = [];
27228
+ const path64 = [];
27229
27229
  color.set(start, GRAY);
27230
- path63.push(start);
27230
+ path64.push(start);
27231
27231
  while (stack.length > 0) {
27232
27232
  const top = stack[stack.length - 1];
27233
27233
  const [id, idx] = top;
@@ -27240,17 +27240,17 @@ function findCycle(nodes) {
27240
27240
  continue;
27241
27241
  const c = color.get(dep);
27242
27242
  if (c === GRAY) {
27243
- const at = path63.indexOf(dep);
27244
- return [...path63.slice(at), dep];
27243
+ const at = path64.indexOf(dep);
27244
+ return [...path64.slice(at), dep];
27245
27245
  }
27246
27246
  if (c === WHITE) {
27247
27247
  color.set(dep, GRAY);
27248
- path63.push(dep);
27248
+ path64.push(dep);
27249
27249
  stack.push([dep, 0]);
27250
27250
  }
27251
27251
  } else {
27252
27252
  color.set(id, BLACK);
27253
- path63.pop();
27253
+ path64.pop();
27254
27254
  stack.pop();
27255
27255
  }
27256
27256
  }
@@ -28170,8 +28170,8 @@ var init_runner = __esm({
28170
28170
  failed: [...this.tentaclesById.values()].filter((r) => r.status === "error"),
28171
28171
  pending: []
28172
28172
  };
28173
- const path63 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
28174
- this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path63}`);
28173
+ const path64 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
28174
+ this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path64}`);
28175
28175
  return snapshot;
28176
28176
  }
28177
28177
  callLog(msg, data) {
@@ -28271,7 +28271,14 @@ var init_types8 = __esm({
28271
28271
  "council.member",
28272
28272
  "mission.phase",
28273
28273
  "mission.replan",
28274
+ // F4 (doc §6 advisory continuation): driver → host advice record. State-only
28275
+ // (never model-surface): the recommendation must not feed the model loop.
28276
+ "mission.progress",
28274
28277
  "verification.run",
28278
+ // F3 (ADR-0023 §5 evidence traceability): per-observation state event —
28279
+ // the session-log anchor EvidenceRef.seq points at (command output, fs
28280
+ // observation, digest). Not model-surface. Schema review per ADR-0021.
28281
+ "verification.evidence",
28275
28282
  "note"
28276
28283
  ];
28277
28284
  SessionEventEnvelopeSchema = external_exports.object({
@@ -28590,6 +28597,7 @@ function buildProjection(events, issues = []) {
28590
28597
  toolResults: 0,
28591
28598
  verifications: [],
28592
28599
  missionPhases: [],
28600
+ missionAdvice: [],
28593
28601
  replans: 0,
28594
28602
  issues
28595
28603
  };
@@ -28625,6 +28633,14 @@ function buildProjection(events, issues = []) {
28625
28633
  case "mission.replan":
28626
28634
  projection.replans += 1;
28627
28635
  break;
28636
+ case "mission.progress":
28637
+ projection.missionAdvice.push({
28638
+ seq: e.seq,
28639
+ recommendation: String(e.data.recommendation ?? ""),
28640
+ rationale: String(e.data.rationale ?? "")
28641
+ });
28642
+ break;
28643
+ break;
28628
28644
  }
28629
28645
  }
28630
28646
  return projection;
@@ -29365,7 +29381,12 @@ var init_runtime2 = __esm({
29365
29381
  });
29366
29382
 
29367
29383
  // packages/core/dist/verification/types.js
29368
- var CriterionSourceSchema, CommandCheckSchema, FileExistsCheckSchema, FileContainsCheckSchema, FileAbsentCheckSchema, NoneCheckSchema, DeterministicCheckSchema, CriterionSchema, EvidenceTierSchema, EvidenceRefSchema, VerificationStatusSchema, VerificationSourceSchema, VerificationResultSchema;
29384
+ function isEventBackedEvidence(ref) {
29385
+ if (!EVENT_BACKED_EVIDENCE_TIERS.includes(ref.tier))
29386
+ return true;
29387
+ return typeof ref.seq === "number" && ref.seq > 0;
29388
+ }
29389
+ var CriterionSourceSchema, CommandCheckSchema, FileExistsCheckSchema, FileContainsCheckSchema, FileAbsentCheckSchema, NoneCheckSchema, DeterministicCheckSchema, CriterionSchema, EvidenceTierSchema, EvidenceRefSchema, EVENT_BACKED_EVIDENCE_TIERS, VerificationStatusSchema, VerificationSourceSchema, VerificationResultSchema;
29369
29390
  var init_types9 = __esm({
29370
29391
  "packages/core/dist/verification/types.js"() {
29371
29392
  "use strict";
@@ -29428,7 +29449,11 @@ var init_types9 = __esm({
29428
29449
  "human"
29429
29450
  ]);
29430
29451
  EvidenceRefSchema = external_exports.object({
29431
- /** Seq of the originating session event, when the evidence is logged. */
29452
+ /**
29453
+ * Seq of the originating session event. Required (by policy) for
29454
+ * event-backed tiers on the 2.0 path: evidence without a session anchor
29455
+ * is narration, not traceable output (F3 / ADR-0023 §5).
29456
+ */
29432
29457
  seq: external_exports.number().int().positive().optional(),
29433
29458
  tier: EvidenceTierSchema,
29434
29459
  /** Human-readable pointer: command line, file path, tool call id… */
@@ -29437,6 +29462,11 @@ var init_types9 = __esm({
29437
29462
  /** sha256 of the captured output (audit chain). */
29438
29463
  digest: external_exports.string().optional()
29439
29464
  });
29465
+ EVENT_BACKED_EVIDENCE_TIERS = [
29466
+ "tool-output",
29467
+ "command-output",
29468
+ "fs-observation"
29469
+ ];
29440
29470
  VerificationStatusSchema = external_exports.enum(["pass", "fail", "unknown"]);
29441
29471
  VerificationSourceSchema = external_exports.enum([
29442
29472
  "deterministic-engine",
@@ -29482,21 +29512,52 @@ var init_engine = __esm({
29482
29512
  results.push(await this.evaluateOne(criterion));
29483
29513
  }
29484
29514
  if (this.options.emit) {
29485
- await this.options.emit({
29486
- kind: "verification.run",
29515
+ try {
29516
+ await this.options.emit({
29517
+ kind: "verification.run",
29518
+ actor: { type: "system", role: "verification" },
29519
+ data: {
29520
+ source: "deterministic-engine",
29521
+ packId: context.packId,
29522
+ results: results.map((r) => ({
29523
+ criterionId: r.criterionId,
29524
+ status: r.status,
29525
+ evidence: r.evidence.map((e) => ({
29526
+ tier: e.tier,
29527
+ ref: e.ref,
29528
+ digest: e.digest,
29529
+ ...e.seq !== void 0 ? { seq: e.seq } : {}
29530
+ }))
29531
+ }))
29532
+ }
29533
+ });
29534
+ } catch {
29535
+ }
29536
+ }
29537
+ return results;
29538
+ }
29539
+ /**
29540
+ * F3: append one `verification.evidence` state event with the raw
29541
+ * observation and resolve to its session seq when the emitter returns it.
29542
+ * Spine failures degrade to `undefined` — an unreachable log must never
29543
+ * fail verification, it only leaves the evidence unanchored.
29544
+ */
29545
+ async emitEvidence(data) {
29546
+ if (!this.options.emit)
29547
+ return void 0;
29548
+ try {
29549
+ const out = await this.options.emit({
29550
+ kind: "verification.evidence",
29487
29551
  actor: { type: "system", role: "verification" },
29488
- data: {
29489
- source: "deterministic-engine",
29490
- packId: context.packId,
29491
- results: results.map((r) => ({
29492
- criterionId: r.criterionId,
29493
- status: r.status,
29494
- evidence: r.evidence.map((e) => ({ tier: e.tier, ref: e.ref, digest: e.digest }))
29495
- }))
29496
- }
29552
+ data
29497
29553
  });
29554
+ if (out && typeof out === "object" && typeof out.seq === "number") {
29555
+ return out.seq;
29556
+ }
29557
+ return void 0;
29558
+ } catch {
29559
+ return void 0;
29498
29560
  }
29499
- return results;
29500
29561
  }
29501
29562
  async evaluateOne(criterion) {
29502
29563
  const started = this.options.now?.() ?? Date.now();
@@ -29536,12 +29597,23 @@ var init_engine = __esm({
29536
29597
  return done({ status: "unknown", evidence: [], detail: "shell provider unavailable" });
29537
29598
  }
29538
29599
  const result = await shell.exec(check2.command, { timeoutMs: check2.timeoutMs });
29600
+ const digest = sha256(result.stdout);
29601
+ const seq = await this.emitEvidence({
29602
+ observation: "command",
29603
+ command: check2.command,
29604
+ exitCode: result.exitCode,
29605
+ timedOut: result.timedOut,
29606
+ digest,
29607
+ stdoutTail: tail(result.stdout),
29608
+ stderrTail: tail(result.stderr)
29609
+ });
29539
29610
  const evidence = [
29540
29611
  {
29541
29612
  tier: "command-output",
29542
29613
  ref: `${check2.command} \u2192 exit ${result.exitCode ?? "signal"}`,
29543
29614
  capturedAt: Date.now(),
29544
- digest: sha256(result.stdout)
29615
+ digest,
29616
+ ...seq !== void 0 ? { seq } : {}
29545
29617
  }
29546
29618
  ];
29547
29619
  if (result.timedOut) {
@@ -29564,8 +29636,21 @@ var init_engine = __esm({
29564
29636
  }
29565
29637
  return done({ status: "pass", evidence });
29566
29638
  }
29567
- fsEvidence(path63, content, sha256) {
29568
- return { tier: "fs-observation", ref: path63, capturedAt: Date.now(), digest: sha256(content) };
29639
+ /**
29640
+ * F3: the fs observation is logged to the spine (path, existence, optional
29641
+ * content digest) and the returned ref carries the event seq when the
29642
+ * emitter resolved one.
29643
+ */
29644
+ async fsEvidence(observation, path64, sha256, content, extra = {}) {
29645
+ const digest = sha256 && content !== void 0 ? sha256(content) : void 0;
29646
+ const seq = await this.emitEvidence({ observation, path: path64, ...extra, ...digest ? { digest } : {} });
29647
+ return {
29648
+ tier: "fs-observation",
29649
+ ref: path64,
29650
+ capturedAt: Date.now(),
29651
+ ...digest ? { digest } : {},
29652
+ ...seq !== void 0 ? { seq } : {}
29653
+ };
29569
29654
  }
29570
29655
  async evalFileExists(check2, done) {
29571
29656
  const sha256 = this.options.sha256 ?? defaultSha256;
@@ -29573,17 +29658,29 @@ var init_engine = __esm({
29573
29658
  if (!fs37)
29574
29659
  return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
29575
29660
  const exists = await fs37.exists(check2.path);
29576
- if (!exists)
29661
+ if (!exists) {
29662
+ await this.emitEvidence({ observation: "file-exists", path: check2.path, exists: false });
29577
29663
  return done({ status: "fail", evidence: [], detail: `file not found: ${check2.path}` });
29664
+ }
29578
29665
  const content = await fs37.readFile(check2.path).catch(() => "");
29579
- return done({ status: "pass", evidence: [this.fsEvidence(check2.path, content, sha256)] });
29666
+ return done({
29667
+ status: "pass",
29668
+ evidence: [await this.fsEvidence("file-exists", check2.path, sha256, content, { exists: true })]
29669
+ });
29580
29670
  }
29581
29671
  async evalFileAbsent(check2, done) {
29582
29672
  const fs37 = this.services.fs;
29583
29673
  if (!fs37)
29584
29674
  return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
29585
29675
  const exists = await fs37.exists(check2.path);
29586
- return exists ? done({ status: "fail", evidence: [], detail: `file still present: ${check2.path}` }) : done({ status: "pass", evidence: [{ tier: "fs-observation", ref: check2.path, capturedAt: Date.now() }] });
29676
+ if (exists) {
29677
+ await this.emitEvidence({ observation: "file-absent", path: check2.path, exists: true });
29678
+ return done({ status: "fail", evidence: [], detail: `file still present: ${check2.path}` });
29679
+ }
29680
+ return done({
29681
+ status: "pass",
29682
+ evidence: [await this.fsEvidence("file-absent", check2.path, void 0, void 0, { exists: false })]
29683
+ });
29587
29684
  }
29588
29685
  async evalFileContains(check2, done) {
29589
29686
  const sha256 = this.options.sha256 ?? defaultSha256;
@@ -29594,9 +29691,10 @@ var init_engine = __esm({
29594
29691
  try {
29595
29692
  content = await fs37.readFile(check2.path);
29596
29693
  } catch {
29694
+ await this.emitEvidence({ observation: "file-contains", path: check2.path, readable: false });
29597
29695
  return done({ status: "fail", evidence: [], detail: `file not readable: ${check2.path}` });
29598
29696
  }
29599
- const evidence = [this.fsEvidence(check2.path, content, sha256)];
29697
+ const evidence = [await this.fsEvidence("file-contains", check2.path, sha256, content)];
29600
29698
  let hit;
29601
29699
  try {
29602
29700
  hit = new RegExp(check2.pattern, "m").test(content);
@@ -29622,6 +29720,7 @@ function evaluateCompletion(criteria, results, policy = STRICT_ALL_POLICY) {
29622
29720
  const order = new Map(criteria.map((c, i) => [c.id, i]));
29623
29721
  const satisfied = [];
29624
29722
  const unsatisfied = [];
29723
+ let unbackedSatisfied = false;
29625
29724
  const ids = [...requiredIds].sort((a, b) => (order.get(a) ?? 0) - (order.get(b) ?? 0));
29626
29725
  for (const id of ids) {
29627
29726
  const result = byId.get(id);
@@ -29645,7 +29744,29 @@ function evaluateCompletion(criteria, results, policy = STRICT_ALL_POLICY) {
29645
29744
  });
29646
29745
  continue;
29647
29746
  }
29747
+ const admissible = policy.admissibleTiers;
29748
+ const tierOk = (e) => !admissible || admissible.includes(e.tier);
29749
+ if (!result.evidence.some(tierOk)) {
29750
+ const tiers = [...new Set(result.evidence.map((e) => e.tier))].join(", ");
29751
+ unsatisfied.push({
29752
+ id,
29753
+ status: "unknown",
29754
+ reason: `pass with inadmissible evidence tiers only (${tiers}) \u2014 not acceptable for completion`
29755
+ });
29756
+ continue;
29757
+ }
29758
+ const backed = result.evidence.filter(tierOk).some(isEventBackedEvidence);
29759
+ if (policy.requireEventBackedEvidence && !backed) {
29760
+ unsatisfied.push({
29761
+ id,
29762
+ status: "unknown",
29763
+ reason: "pass without event-backed evidence \u2014 no EvidenceRef.seq anchored to a session event"
29764
+ });
29765
+ continue;
29766
+ }
29648
29767
  satisfied.push(id);
29768
+ if (!backed)
29769
+ unbackedSatisfied = true;
29649
29770
  }
29650
29771
  const verdict = unsatisfied.length === 0 ? "PASS" : unsatisfied.some((u) => u.status === "fail") ? "REPAIR_REQUIRED" : "BLOCKED";
29651
29772
  return {
@@ -29653,21 +29774,126 @@ function evaluateCompletion(criteria, results, policy = STRICT_ALL_POLICY) {
29653
29774
  satisfied,
29654
29775
  unsatisfied,
29655
29776
  evidenceComplete: unsatisfied.length === 0,
29777
+ eventBackedEvidenceComplete: unsatisfied.length === 0 && !unbackedSatisfied,
29656
29778
  summary: verdict === "PASS" ? `complete: ${satisfied.length}/${ids.length} required criteria pass with evidence` : `incomplete (${verdict}): ${unsatisfied.map((u) => `${u.id}=${u.status}`).join(", ")}`
29657
29779
  };
29658
29780
  }
29659
- var STRICT_ALL_POLICY, strictBuildGate;
29781
+ var STRICT_ALL_POLICY, DETERMINISTIC_EVIDENCE_TIERS, STRICT_BUILD_POLICY, strictBuildGate;
29660
29782
  var init_completionPolicy = __esm({
29661
29783
  "packages/core/dist/verification/completionPolicy.js"() {
29662
29784
  "use strict";
29785
+ init_types9();
29663
29786
  STRICT_ALL_POLICY = { mode: "strict", required: "*" };
29787
+ DETERMINISTIC_EVIDENCE_TIERS = [
29788
+ "tool-output",
29789
+ "command-output",
29790
+ "fs-observation",
29791
+ "human"
29792
+ ];
29793
+ STRICT_BUILD_POLICY = {
29794
+ mode: "strict",
29795
+ required: "*",
29796
+ admissibleTiers: DETERMINISTIC_EVIDENCE_TIERS,
29797
+ requireEventBackedEvidence: true
29798
+ };
29664
29799
  strictBuildGate = evaluateCompletion;
29665
29800
  }
29666
29801
  });
29667
29802
 
29803
+ // packages/core/dist/verification/sessionEvidence.js
29804
+ function asString2(v) {
29805
+ return typeof v === "string" ? v : null;
29806
+ }
29807
+ function asStringArray(v) {
29808
+ if (!Array.isArray(v))
29809
+ return [];
29810
+ return v.filter((x) => typeof x === "string");
29811
+ }
29812
+ function asUnsatisfied(v) {
29813
+ if (!Array.isArray(v))
29814
+ return [];
29815
+ const out = [];
29816
+ for (const item of v) {
29817
+ if (!item || typeof item !== "object")
29818
+ continue;
29819
+ const rec = item;
29820
+ const id = asString2(rec.id);
29821
+ if (id === null)
29822
+ continue;
29823
+ out.push({
29824
+ id,
29825
+ status: asString2(rec.status) ?? "unknown",
29826
+ reason: asString2(rec.reason) ?? ""
29827
+ });
29828
+ }
29829
+ return out;
29830
+ }
29831
+ function parseVerificationRunPayload(ev) {
29832
+ const data = ev.data;
29833
+ if (!data || typeof data !== "object")
29834
+ return null;
29835
+ const rec = data;
29836
+ const verdictRaw = asString2(rec.verdict);
29837
+ const verdict = verdictRaw !== null && VERDICTS.has(verdictRaw) ? verdictRaw : "unknown";
29838
+ const evidence = rec.evidence && typeof rec.evidence === "object" ? rec.evidence : null;
29839
+ const satisfied = asStringArray(evidence?.satisfied);
29840
+ const unsatisfied = asUnsatisfied(evidence?.unsatisfied);
29841
+ const completeRaw = evidence?.complete;
29842
+ return {
29843
+ seq: ev.seq,
29844
+ ts: ev.ts,
29845
+ engine: asString2(rec.engine),
29846
+ strict: rec.strict === true,
29847
+ verdict,
29848
+ satisfied,
29849
+ unsatisfied,
29850
+ evidenceComplete: typeof completeRaw === "boolean" ? completeRaw : verdict === "PASS" && satisfied.length > 0 && unsatisfied.length === 0,
29851
+ summary: asString2(rec.summary) ?? ""
29852
+ };
29853
+ }
29854
+ function lastVerificationRun(events) {
29855
+ for (let i = events.length - 1; i >= 0; i--) {
29856
+ const ev = events[i];
29857
+ if (ev.kind !== "verification.run")
29858
+ continue;
29859
+ const snap = parseVerificationRunPayload(ev);
29860
+ if (snap)
29861
+ return snap;
29862
+ }
29863
+ return null;
29864
+ }
29865
+ function snapshotToCompletionEvaluation(snap) {
29866
+ if (!snap.strict)
29867
+ return null;
29868
+ const unsatisfied = snap.unsatisfied.map((u) => ({
29869
+ id: u.id,
29870
+ status: u.status === "fail" || u.status === "missing" ? u.status : "unknown",
29871
+ reason: u.reason
29872
+ }));
29873
+ const verdict = snap.verdict === "PASS" || snap.verdict === "REPAIR_REQUIRED" || snap.verdict === "BLOCKED" ? snap.verdict : "BLOCKED";
29874
+ return {
29875
+ verdict,
29876
+ satisfied: [...snap.satisfied],
29877
+ unsatisfied,
29878
+ evidenceComplete: snap.evidenceComplete && unsatisfied.length === 0 && verdict === "PASS",
29879
+ // F3: snapshot summaries do not carry per-ref seq — event-backedness is
29880
+ // auditable from the raw spine events (verification.evidence), unknown here.
29881
+ eventBackedEvidenceComplete: false,
29882
+ summary: snap.summary
29883
+ };
29884
+ }
29885
+ var VERDICTS;
29886
+ var init_sessionEvidence = __esm({
29887
+ "packages/core/dist/verification/sessionEvidence.js"() {
29888
+ "use strict";
29889
+ VERDICTS = /* @__PURE__ */ new Set(["PASS", "REPAIR_REQUIRED", "BLOCKED"]);
29890
+ }
29891
+ });
29892
+
29668
29893
  // packages/core/dist/verification/criteriaPack.v1.js
29669
29894
  function codingCriteriaPack(options = {}) {
29670
29895
  const timeoutMs = options.commandTimeoutMs ?? 6e5;
29896
+ const withDefault = (o, d) => o === void 0 ? d : o;
29671
29897
  const command = (command2) => command2 ? { kind: "command", command: command2, timeoutMs } : void 0;
29672
29898
  const criteria = [
29673
29899
  {
@@ -29675,21 +29901,21 @@ function codingCriteriaPack(options = {}) {
29675
29901
  text: "Static checks (typecheck) pass with no new errors.",
29676
29902
  source: "criteria-pack",
29677
29903
  required: true,
29678
- check: command(options.typecheckCommand ?? "npm run typecheck")
29904
+ check: command(withDefault(options.typecheckCommand, "npm run typecheck"))
29679
29905
  },
29680
29906
  {
29681
29907
  id: "correctness.specification",
29682
29908
  text: "The test suite passes \u2014 behavior matches the specification.",
29683
29909
  source: "criteria-pack",
29684
29910
  required: true,
29685
- check: command(options.testCommand ?? "npm run test")
29911
+ check: command(withDefault(options.testCommand, "npm run test"))
29686
29912
  },
29687
29913
  {
29688
29914
  id: "correctness.observable-output",
29689
29915
  text: "The project builds successfully (observable output artifact).",
29690
29916
  source: "criteria-pack",
29691
29917
  required: true,
29692
- check: command(options.buildCommand ?? "npm run build")
29918
+ check: command(withDefault(options.buildCommand, "npm run build"))
29693
29919
  },
29694
29920
  {
29695
29921
  id: "quality.scope-discipline",
@@ -29954,6 +30180,7 @@ var init_verification2 = __esm({
29954
30180
  init_types9();
29955
30181
  init_engine();
29956
30182
  init_completionPolicy();
30183
+ init_sessionEvidence();
29957
30184
  init_criteriaPack_v1();
29958
30185
  init_metrics();
29959
30186
  init_verifier();
@@ -29986,7 +30213,8 @@ function deriveMissionState(projection) {
29986
30213
  verifications: projection.verifications.length,
29987
30214
  lastSeq: projection.lastSeq,
29988
30215
  interrupted: projection.lastSeq > 0 && projection.endedAt === void 0,
29989
- forkParent: projection.fork?.parentSessionId
30216
+ forkParent: projection.fork?.parentSessionId,
30217
+ lastAdvice: projection.missionAdvice[projection.missionAdvice.length - 1]
29990
30218
  };
29991
30219
  }
29992
30220
  function missionSnapshot(state3) {
@@ -30004,11 +30232,99 @@ var init_missionState = __esm({
30004
30232
  }
30005
30233
  });
30006
30234
 
30235
+ // packages/core/dist/mission/continuationPolicy.js
30236
+ function requiredBlockers(progress) {
30237
+ const blockers = [];
30238
+ if (progress.ratio === null) {
30239
+ blockers.push("no verification criteria recorded yet");
30240
+ return blockers;
30241
+ }
30242
+ const missing = progress.criteriaTotal - progress.criteriaPassed;
30243
+ if (missing > 0) {
30244
+ blockers.push(`${missing}/${progress.criteriaTotal} required criteria not passing`);
30245
+ }
30246
+ if (progress.criteriaPassed === progress.criteriaTotal && !progress.evidenceComplete) {
30247
+ blockers.push("all criteria pass but evidence is incomplete (not event-backed)");
30248
+ }
30249
+ return blockers;
30250
+ }
30251
+ function evaluateMissionContinuation(input) {
30252
+ const { progress, verifier, budget, userSteer } = input;
30253
+ const base = { goalRewrite: false, doneByScore: false };
30254
+ const trend = verifier ? { tier: verifier.tier, value: verifier.value } : void 0;
30255
+ const blockers = requiredBlockers(progress);
30256
+ if (userSteer === "stop") {
30257
+ return {
30258
+ ...base,
30259
+ recommendation: "wind-down",
30260
+ rationale: "user steer: explicit stop \u2014 winding down without claiming done",
30261
+ blockers,
30262
+ trend
30263
+ };
30264
+ }
30265
+ if (userSteer === "continue") {
30266
+ return {
30267
+ ...base,
30268
+ recommendation: "continue",
30269
+ rationale: "user steer: explicit continue \u2014 overrides any trend-derived advice",
30270
+ blockers,
30271
+ trend
30272
+ };
30273
+ }
30274
+ if (budget && budget.iterationsUsed >= budget.iterationsMax) {
30275
+ return {
30276
+ ...base,
30277
+ recommendation: "hold-for-user",
30278
+ rationale: `iteration budget exhausted (${budget.iterationsUsed}/${budget.iterationsMax}) \u2014 operator decides: raise budget or hand off`,
30279
+ blockers,
30280
+ trend
30281
+ };
30282
+ }
30283
+ if (blockers.length > 0) {
30284
+ const trendNote = verifier ? ` \xB7 verifier trend recorded as context only (tier ${verifier.tier}${verifier.value === null ? "" : `, ${verifier.value.toFixed(2)}`}) \u2014 trend is never authority` : "";
30285
+ return {
30286
+ ...base,
30287
+ recommendation: "continue",
30288
+ rationale: `required criteria incomplete \u2192 no early-stop${trendNote}`,
30289
+ blockers,
30290
+ trend
30291
+ };
30292
+ }
30293
+ const blended = verifier?.tier === "blended" ? verifier.value : null;
30294
+ const rejected = verifier?.verdict === "rejected" || blended !== null && blended <= REJECTED_BLEND;
30295
+ if (rejected) {
30296
+ return {
30297
+ ...base,
30298
+ recommendation: "hold-for-user",
30299
+ rationale: "deterministic evidence is complete and PASSing, but the verifier flags risk \u2014 attention requested; the deterministic verdict stands untouched",
30300
+ blockers: [],
30301
+ trend
30302
+ };
30303
+ }
30304
+ const confirmed = verifier?.verdict === "confirmed" || blended !== null && blended >= CONFIRMED_BLEND;
30305
+ return {
30306
+ ...base,
30307
+ recommendation: "wind-down",
30308
+ rationale: confirmed ? "required criteria pass with evidence and the verifier confirms \u2014 candidate done (final say: driver + CompletionPolicy)" : "required criteria pass with evidence \u2014 candidate done on deterministic evidence (final say: driver + CompletionPolicy)",
30309
+ blockers: [],
30310
+ trend
30311
+ };
30312
+ }
30313
+ var CONFIRMED_BLEND, REJECTED_BLEND;
30314
+ var init_continuationPolicy = __esm({
30315
+ "packages/core/dist/mission/continuationPolicy.js"() {
30316
+ "use strict";
30317
+ CONFIRMED_BLEND = 0.8;
30318
+ REJECTED_BLEND = 0.35;
30319
+ }
30320
+ });
30321
+
30007
30322
  // packages/core/dist/mission/index.js
30008
30323
  var init_mission2 = __esm({
30009
30324
  "packages/core/dist/mission/index.js"() {
30010
30325
  "use strict";
30011
30326
  init_missionState();
30327
+ init_continuationPolicy();
30012
30328
  }
30013
30329
  });
30014
30330
 
@@ -30045,8 +30361,10 @@ __export(dist_exports, {
30045
30361
  DESIGN_PHASE_MODE_BANNER: () => DESIGN_PHASE_MODE_BANNER,
30046
30362
  DESIGN_PHASE_REQUIREMENTS: () => DESIGN_PHASE_REQUIREMENTS,
30047
30363
  DESIGN_PHASE_REQUIREMENT_SETS: () => DESIGN_PHASE_REQUIREMENT_SETS,
30364
+ DETERMINISTIC_EVIDENCE_TIERS: () => DETERMINISTIC_EVIDENCE_TIERS,
30048
30365
  DOOM_LOOP_THRESHOLD: () => DOOM_LOOP_THRESHOLD,
30049
30366
  DeterministicCheckSchema: () => DeterministicCheckSchema,
30367
+ EVENT_BACKED_EVIDENCE_TIERS: () => EVENT_BACKED_EVIDENCE_TIERS,
30050
30368
  EXPERIMENTAL_FLAGS: () => EXPERIMENTAL_FLAGS,
30051
30369
  EventBus: () => EventBus,
30052
30370
  EvidenceRefSchema: () => EvidenceRefSchema,
@@ -30100,6 +30418,7 @@ __export(dist_exports, {
30100
30418
  SINGLE_AGENT_IDENTITY_MODULE: () => SINGLE_AGENT_IDENTITY_MODULE,
30101
30419
  SKILL_CATALOG: () => SKILL_CATALOG,
30102
30420
  STRICT_ALL_POLICY: () => STRICT_ALL_POLICY,
30421
+ STRICT_BUILD_POLICY: () => STRICT_BUILD_POLICY,
30103
30422
  STRUCTURED_REASONING_DIRECTIVE: () => STRUCTURED_REASONING_DIRECTIVE,
30104
30423
  ScriptRunner: () => ScriptRunner,
30105
30424
  SessionActorSchema: () => SessionActorSchema,
@@ -30191,6 +30510,7 @@ __export(dist_exports, {
30191
30510
  emptyContextGrowthStats: () => emptyContextGrowthStats,
30192
30511
  enforceDesignPhaseToolEmissions: () => enforceDesignPhaseToolEmissions,
30193
30512
  evaluateCompletion: () => evaluateCompletion,
30513
+ evaluateMissionContinuation: () => evaluateMissionContinuation,
30194
30514
  executeTool: () => executeTool,
30195
30515
  exportSession: () => exportSession,
30196
30516
  exportSessionJson: () => exportSessionJson,
@@ -30246,6 +30566,7 @@ __export(dist_exports, {
30246
30566
  isBrainToolExecutionStartEvent: () => isBrainToolExecutionStartEvent,
30247
30567
  isBrainToolExecutionUpdateEvent: () => isBrainToolExecutionUpdateEvent,
30248
30568
  isConverged: () => isConverged,
30569
+ isEventBackedEvidence: () => isEventBackedEvidence,
30249
30570
  isExperimentalEnabled: () => isExperimentalEnabled,
30250
30571
  isModelSurfaceEvent: () => isModelSurfaceEvent,
30251
30572
  isReviewerKind: () => isReviewerKind,
@@ -30255,6 +30576,7 @@ __export(dist_exports, {
30255
30576
  isVerifyToolCheckSkipped: () => isVerifyToolCheckSkipped,
30256
30577
  jaccardSimilarity: () => jaccardSimilarity,
30257
30578
  jsonBytes: () => jsonBytes,
30579
+ lastVerificationRun: () => lastVerificationRun,
30258
30580
  lineageOf: () => lineageOf,
30259
30581
  lintSynthesisHonesty: () => lintSynthesisHonesty,
30260
30582
  listCodingSkills: () => listCodingSkills,
@@ -30276,6 +30598,7 @@ __export(dist_exports, {
30276
30598
  parseProjectRootFromWorkspaceContext: () => parseProjectRootFromWorkspaceContext,
30277
30599
  parseTextToolCalls: () => parseTextToolCalls,
30278
30600
  parseThinking: () => parseThinking,
30601
+ parseVerificationRunPayload: () => parseVerificationRunPayload,
30279
30602
  parseVerificationTable: () => parseVerificationTable,
30280
30603
  parseVerifyVerdict: () => parseVerifyVerdict,
30281
30604
  pathsOverlap: () => pathsOverlap,
@@ -30325,6 +30648,7 @@ __export(dist_exports, {
30325
30648
  sha256Hex: () => sha256Hex,
30326
30649
  shouldRetryMember: () => shouldRetryMember,
30327
30650
  slugify: () => slugify2,
30651
+ snapshotToCompletionEvaluation: () => snapshotToCompletionEvaluation,
30328
30652
  specificityFromAssumptions: () => specificityFromAssumptions,
30329
30653
  stableStringify: () => stableStringify,
30330
30654
  strictBuildGate: () => strictBuildGate,
@@ -30694,6 +31018,7 @@ var init_sessionSpine = __esm({
30694
31018
  "use strict";
30695
31019
  init_session();
30696
31020
  init_session();
31021
+ init_verification2();
30697
31022
  MAX_STREAM_BUFFERS = 32;
30698
31023
  SessionSpineMirror = class _SessionSpineMirror {
30699
31024
  constructor(sessionId2, options) {
@@ -30702,7 +31027,7 @@ var init_sessionSpine = __esm({
30702
31027
  this.sessionsDir = resolveSessionsDir({ baseDir: options.baseDir });
30703
31028
  }
30704
31029
  writer = null;
30705
- chain = Promise.resolve();
31030
+ chain = Promise.resolve(null);
30706
31031
  streamBuffers = /* @__PURE__ */ new Map();
30707
31032
  warned = false;
30708
31033
  status = "disabled";
@@ -30781,6 +31106,19 @@ var init_sessionSpine = __esm({
30781
31106
  if (!report || report.events.length === 0) return null;
30782
31107
  return deriveMessages(report.events);
30783
31108
  }
31109
+ /**
31110
+ * E2.1 (ADR-0023 × ADR-0021): last recognizable strict verification record
31111
+ * in this session's log — the completion verdict is reconstructible from
31112
+ * the spine alone (null when degraded/disabled or no record).
31113
+ */
31114
+ async lastVerificationRun() {
31115
+ if (this.status !== "active" && this.status !== "closed") return null;
31116
+ const report = await readSessionLog(
31117
+ path21.join(this.sessionsDir, this.sessionId, "events.jsonl")
31118
+ ).catch(() => null);
31119
+ if (!report) return null;
31120
+ return lastVerificationRun(report.events);
31121
+ }
30784
31122
  /** Mirror one BrainEvent (coalescing message deltas until message_end). */
30785
31123
  mirrorBrainEvent(ev) {
30786
31124
  if (this.status !== "active" || !this.writer) return;
@@ -30825,18 +31163,44 @@ var init_sessionSpine = __esm({
30825
31163
  data: { phase: phase2, ...note ? { note } : {} }
30826
31164
  });
30827
31165
  }
31166
+ /**
31167
+ * F4 (doc §6): append an advisory continuation record. State-only — the
31168
+ * recommendation must never feed the model loop or rewrite the goal.
31169
+ */
31170
+ missionProgress(advice) {
31171
+ void this.append({
31172
+ kind: "mission.progress",
31173
+ actor: ACTOR_SYSTEM,
31174
+ data: {
31175
+ recommendation: advice.recommendation,
31176
+ rationale: advice.rationale,
31177
+ ...advice.blockers ? { blockers: advice.blockers } : {},
31178
+ ...advice.trend ? { trend: advice.trend } : {},
31179
+ ...advice.iteration !== void 0 ? { iteration: advice.iteration } : {}
31180
+ }
31181
+ });
31182
+ }
30828
31183
  note(text, data) {
30829
31184
  void this.append({ kind: "note", actor: ACTOR_SYSTEM, data: { note: text, ...data } });
30830
31185
  }
30831
31186
  append(input) {
30832
- if (!this.writer || this.status === "closed") return Promise.resolve();
30833
- this.chain = this.chain.then(() => this.writer.append(input)).then(() => void 0).catch((err) => {
31187
+ if (!this.writer || this.status === "closed") return Promise.resolve(null);
31188
+ this.chain = this.chain.then(() => this.writer.append(input)).then((envelope) => envelope.seq).catch((err) => {
30834
31189
  this.status = "degraded";
30835
31190
  this.writer = null;
30836
31191
  this.warnOnce(err);
31192
+ return null;
30837
31193
  });
30838
31194
  return this.chain;
30839
31195
  }
31196
+ /**
31197
+ * F3 (ADR-0023 §5): append one spine event and resolve to its assigned
31198
+ * seq — the anchor EvidenceRef.seq points at. Null when degraded or
31199
+ * disabled: callers must treat null as "not traceable", never as failure.
31200
+ */
31201
+ appendEvent(input) {
31202
+ return this.append(input);
31203
+ }
30840
31204
  warnOnce(err) {
30841
31205
  if (this.warned || this.options.quiet) return;
30842
31206
  this.warned = true;
@@ -32455,9 +32819,9 @@ function spillToolOutput(fullText, meta3) {
32455
32819
  const rnd = randomBytes2(3).toString("hex");
32456
32820
  const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
32457
32821
  const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
32458
- const path63 = join11(dir, file2);
32459
- writeFileSync11(path63, fullText, "utf8");
32460
- return path63;
32822
+ const path64 = join11(dir, file2);
32823
+ writeFileSync11(path64, fullText, "utf8");
32824
+ return path64;
32461
32825
  } catch {
32462
32826
  return null;
32463
32827
  }
@@ -32503,10 +32867,10 @@ function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
32503
32867
  ${tail2}`;
32504
32868
  }
32505
32869
  if (doSpill) {
32506
- const path63 = spillToolOutput(text, { toolName: opts.toolName });
32507
- if (path63) {
32870
+ const path64 = spillToolOutput(text, { toolName: opts.toolName });
32871
+ if (path64) {
32508
32872
  const spillNote = `
32509
- \u2026 [full output spilled to: ${path63} \u2014 re-read with read_file if you need the complete text] \u2026`;
32873
+ \u2026 [full output spilled to: ${path64} \u2014 re-read with read_file if you need the complete text] \u2026`;
32510
32874
  if (preview.includes("] \u2026\n")) {
32511
32875
  preview = preview.replace("] \u2026\n", `] \u2026${spillNote}
32512
32876
  `);
@@ -35169,28 +35533,28 @@ var init_storage = __esm({
35169
35533
  VALID_SCALARS = /^(true|false|null|~)$/i;
35170
35534
  Storage = class {
35171
35535
  /** Read a Markdown file with frontmatter. Throws if not found. */
35172
- read(path63) {
35173
- if (!existsSync19(path63)) {
35174
- throw new Error(`File not found: ${path63}`);
35536
+ read(path64) {
35537
+ if (!existsSync19(path64)) {
35538
+ throw new Error(`File not found: ${path64}`);
35175
35539
  }
35176
- const md = readFileSync16(path63, "utf8");
35540
+ const md = readFileSync16(path64, "utf8");
35177
35541
  return parseFrontmatter(md);
35178
35542
  }
35179
35543
  /** Read a Markdown file; returns null if not found. */
35180
- readIfExists(path63) {
35181
- if (!existsSync19(path63)) return null;
35182
- return this.read(path63);
35544
+ readIfExists(path64) {
35545
+ if (!existsSync19(path64)) return null;
35546
+ return this.read(path64);
35183
35547
  }
35184
35548
  /**
35185
35549
  * Write a Markdown file atomically (tmp + rename). Creates parent dirs.
35186
35550
  * The meta object is serialized as YAML frontmatter; body as Markdown.
35187
35551
  */
35188
- write(path63, meta3, body) {
35189
- mkdirSync11(dirname2(path63), { recursive: true });
35190
- const tmp = path63 + ".tmp-" + process.pid;
35552
+ write(path64, meta3, body) {
35553
+ mkdirSync11(dirname2(path64), { recursive: true });
35554
+ const tmp = path64 + ".tmp-" + process.pid;
35191
35555
  const md = serializeFrontmatter(meta3, body);
35192
35556
  writeFileSync13(tmp, md, "utf8");
35193
- renameSync2(tmp, path63);
35557
+ renameSync2(tmp, path64);
35194
35558
  }
35195
35559
  /** List all .md files in a directory (non-recursive). */
35196
35560
  listMarkdown(dir) {
@@ -35252,8 +35616,8 @@ function nextPlanTaskId(store6) {
35252
35616
  return `t${store6.counter}`;
35253
35617
  }
35254
35618
  function writePlanTaskArtifact(rootDir, task) {
35255
- const path63 = join15(rootDir, "plan-tasks", `${task.id}.md`);
35256
- mkdirSync12(dirname3(path63), { recursive: true });
35619
+ const path64 = join15(rootDir, "plan-tasks", `${task.id}.md`);
35620
+ mkdirSync12(dirname3(path64), { recursive: true });
35257
35621
  const meta3 = {
35258
35622
  kind: "task",
35259
35623
  id: task.id,
@@ -35274,7 +35638,7 @@ function writePlanTaskArtifact(rootDir, task) {
35274
35638
  task.notes?.trim() ? task.notes.trim() : "_(no notes)_",
35275
35639
  ""
35276
35640
  ].filter((l) => l !== null).join("\n");
35277
- new Storage().write(path63, meta3, body);
35641
+ new Storage().write(path64, meta3, body);
35278
35642
  }
35279
35643
  function loadHandle(rootDir) {
35280
35644
  const jsonPath = join15(rootDir, "plan.json");
@@ -37832,21 +38196,21 @@ function normalizeAuth(auth) {
37832
38196
  return "agent";
37833
38197
  }
37834
38198
  function readSecrets() {
37835
- const path63 = getSshSecretsPath();
37836
- if (!existsSync23(path63)) return {};
38199
+ const path64 = getSshSecretsPath();
38200
+ if (!existsSync23(path64)) return {};
37837
38201
  try {
37838
- return JSON.parse(readFileSync20(path63, "utf8"));
38202
+ return JSON.parse(readFileSync20(path64, "utf8"));
37839
38203
  } catch {
37840
38204
  return {};
37841
38205
  }
37842
38206
  }
37843
38207
  function writeSecrets(data) {
37844
- const path63 = getSshSecretsPath();
37845
- mkdirSync13(dirname4(path63), { recursive: true });
37846
- writeFileSync15(path63, `${JSON.stringify(data, null, 2)}
38208
+ const path64 = getSshSecretsPath();
38209
+ mkdirSync13(dirname4(path64), { recursive: true });
38210
+ writeFileSync15(path64, `${JSON.stringify(data, null, 2)}
37847
38211
  `, "utf8");
37848
38212
  try {
37849
- chmodSync(path63, 384);
38213
+ chmodSync(path64, 384);
37850
38214
  } catch {
37851
38215
  }
37852
38216
  }
@@ -37875,10 +38239,10 @@ function deleteSshPassword(id) {
37875
38239
  writeSecrets({ passwords });
37876
38240
  }
37877
38241
  function readStore2() {
37878
- const path63 = getSshTargetsPath();
37879
- if (!existsSync23(path63)) return [];
38242
+ const path64 = getSshTargetsPath();
38243
+ if (!existsSync23(path64)) return [];
37880
38244
  try {
37881
- const parsed = JSON.parse(readFileSync20(path63, "utf8"));
38245
+ const parsed = JSON.parse(readFileSync20(path64, "utf8"));
37882
38246
  const list = Array.isArray(parsed.targets) ? parsed.targets : [];
37883
38247
  return list.filter(
37884
38248
  (t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
@@ -37893,11 +38257,11 @@ function readStore2() {
37893
38257
  }
37894
38258
  }
37895
38259
  function writeStore2(targets) {
37896
- const path63 = getSshTargetsPath();
37897
- mkdirSync13(dirname4(path63), { recursive: true });
38260
+ const path64 = getSshTargetsPath();
38261
+ mkdirSync13(dirname4(path64), { recursive: true });
37898
38262
  const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
37899
38263
  writeFileSync15(
37900
- path63,
38264
+ path64,
37901
38265
  `${JSON.stringify({ targets: clean }, null, 2)}
37902
38266
  `,
37903
38267
  "utf8"
@@ -38143,11 +38507,11 @@ function formatSshTargetsForPrompt() {
38143
38507
  ];
38144
38508
  for (const t of targets) {
38145
38509
  const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
38146
- const path63 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
38510
+ const path64 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
38147
38511
  const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
38148
38512
  const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
38149
38513
  lines.push(
38150
- `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path63}${tags}${allow}`
38514
+ `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path64}${tags}${allow}`
38151
38515
  );
38152
38516
  }
38153
38517
  return lines.join("\n");
@@ -39857,6 +40221,9 @@ function parseHeadlessFlags(argv) {
39857
40221
  i++;
39858
40222
  } else if (arg === "--strict-done") {
39859
40223
  strictDone = true;
40224
+ } else if (arg === "--no-strict-done") {
40225
+ strictDone = false;
40226
+ process.env.ZELARI_MISSION_STRICT = "0";
39860
40227
  } else if (arg === "--kraken-graph") {
39861
40228
  krakenGraph = argv[i + 1];
39862
40229
  i++;
@@ -39959,8 +40326,16 @@ __export(headlessSpine_exports, {
39959
40326
  missionStateFromSpine: () => missionStateFromSpine,
39960
40327
  openHeadlessSpine: () => openHeadlessSpine,
39961
40328
  resolveHeadlessProfileId: () => resolveHeadlessProfileId,
39962
- seedHeadlessModelHistory: () => seedHeadlessModelHistory
40329
+ seedHeadlessModelHistory: () => seedHeadlessModelHistory,
40330
+ sessionStartedEvent: () => sessionStartedEvent
39963
40331
  });
40332
+ function sessionStartedEvent(handle) {
40333
+ return {
40334
+ type: "session_started",
40335
+ sessionId: handle.sessionId,
40336
+ spine: handle.spine.status
40337
+ };
40338
+ }
39964
40339
  function resolveHeadlessProfileId(mode, explicit) {
39965
40340
  if (explicit) return resolveProfile(explicit).id;
39966
40341
  return defaultProfileForMode(mode ?? "kraken");
@@ -40002,9 +40377,18 @@ async function openHeadlessSpine(opts) {
40002
40377
  verificationRun(payload) {
40003
40378
  spine.verificationRun(payload);
40004
40379
  },
40380
+ appendEvent(input) {
40381
+ return spine.appendEvent(input);
40382
+ },
40383
+ lastVerificationRun() {
40384
+ return spine.lastVerificationRun();
40385
+ },
40005
40386
  missionPhase(phase2, note) {
40006
40387
  spine.missionPhase(phase2, note);
40007
40388
  },
40389
+ missionProgress(advice) {
40390
+ spine.missionProgress(advice);
40391
+ },
40008
40392
  note(text, data) {
40009
40393
  spine.note(text, data);
40010
40394
  },
@@ -40114,12 +40498,12 @@ var init_headlessSpine = __esm({
40114
40498
  // src/cli/state/fileStateStore.ts
40115
40499
  import { createHash as createHash10, randomUUID as randomUUID2 } from "node:crypto";
40116
40500
  import { promises as fs21 } from "node:fs";
40117
- import * as path40 from "node:path";
40501
+ import * as path41 from "node:path";
40118
40502
  function shortId() {
40119
40503
  return randomUUID2().replace(/-/g, "").slice(0, 12);
40120
40504
  }
40121
40505
  async function writeJsonAtomic(filePath, data) {
40122
- await fs21.mkdir(path40.dirname(filePath), { recursive: true });
40506
+ await fs21.mkdir(path41.dirname(filePath), { recursive: true });
40123
40507
  const tmp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
40124
40508
  await fs21.writeFile(tmp, JSON.stringify(data, null, 2) + "\n", "utf8");
40125
40509
  await fs21.rename(tmp, filePath);
@@ -40180,11 +40564,11 @@ var init_fileStateStore = __esm({
40180
40564
  indexPath = "";
40181
40565
  async init(projectRoot) {
40182
40566
  this.root = projectRoot;
40183
- this.stateDir = path40.join(projectRoot, ".zelari", "state");
40184
- this.commitsDir = path40.join(this.stateDir, "commits");
40185
- this.artifactsDir = path40.join(this.stateDir, "artifacts");
40186
- this.headPath = path40.join(this.stateDir, "HEAD.json");
40187
- this.indexPath = path40.join(this.stateDir, "index.jsonl");
40567
+ this.stateDir = path41.join(projectRoot, ".zelari", "state");
40568
+ this.commitsDir = path41.join(this.stateDir, "commits");
40569
+ this.artifactsDir = path41.join(this.stateDir, "artifacts");
40570
+ this.headPath = path41.join(this.stateDir, "HEAD.json");
40571
+ this.indexPath = path41.join(this.stateDir, "index.jsonl");
40188
40572
  await fs21.mkdir(this.commitsDir, { recursive: true });
40189
40573
  await fs21.mkdir(this.artifactsDir, { recursive: true });
40190
40574
  }
@@ -40197,13 +40581,13 @@ var init_fileStateStore = __esm({
40197
40581
  const discoveries = input.discoveries ?? [];
40198
40582
  const parent = await this.head();
40199
40583
  const id = shortId();
40200
- const artifactRel = path40.join("artifacts", id);
40201
- const artifactAbs = path40.join(this.artifactsDir, id);
40584
+ const artifactRel = path41.join("artifacts", id);
40585
+ const artifactAbs = path41.join(this.artifactsDir, id);
40202
40586
  await fs21.mkdir(artifactAbs, { recursive: true });
40203
40587
  const summary = defaultSummary(input, discoveries);
40204
- await fs21.writeFile(path40.join(artifactAbs, "summary.md"), summary + "\n", "utf8");
40205
- await writeJsonAtomic(path40.join(artifactAbs, "discoveries.json"), discoveries);
40206
- await writeJsonAtomic(path40.join(artifactAbs, "verification.json"), input.verification);
40588
+ await fs21.writeFile(path41.join(artifactAbs, "summary.md"), summary + "\n", "utf8");
40589
+ await writeJsonAtomic(path41.join(artifactAbs, "discoveries.json"), discoveries);
40590
+ await writeJsonAtomic(path41.join(artifactAbs, "verification.json"), input.verification);
40207
40591
  const meta3 = {
40208
40592
  id,
40209
40593
  parentId: parent?.id ?? null,
@@ -40215,14 +40599,14 @@ var init_fileStateStore = __esm({
40215
40599
  workspaceCheckpointId: input.workspaceCheckpointId,
40216
40600
  verification: {
40217
40601
  ...input.verification,
40218
- reportPath: input.verification.reportPath ?? path40.join(".zelari", "state", artifactRel, "verification.json").replace(/\\/g, "/")
40602
+ reportPath: input.verification.reportPath ?? path41.join(".zelari", "state", artifactRel, "verification.json").replace(/\\/g, "/")
40219
40603
  },
40220
40604
  changedPaths: input.changedPaths ?? [],
40221
40605
  stablePromptHash: input.stablePromptHash,
40222
40606
  discoveryCount: discoveries.length,
40223
40607
  artifactDir: artifactRel.replace(/\\/g, "/")
40224
40608
  };
40225
- await writeJsonAtomic(path40.join(this.commitsDir, `${id}.json`), meta3);
40609
+ await writeJsonAtomic(path41.join(this.commitsDir, `${id}.json`), meta3);
40226
40610
  await writeJsonAtomic(this.headPath, { id, updatedAt: meta3.createdAt });
40227
40611
  await fs21.appendFile(this.indexPath, JSON.stringify({ id, createdAt: meta3.createdAt, label: meta3.label }) + "\n", "utf8");
40228
40612
  return stripStored(meta3);
@@ -40233,7 +40617,7 @@ var init_fileStateStore = __esm({
40233
40617
  return this.get(head.id);
40234
40618
  }
40235
40619
  async get(id) {
40236
- const stored = await readJsonFile(path40.join(this.commitsDir, `${id}.json`));
40620
+ const stored = await readJsonFile(path41.join(this.commitsDir, `${id}.json`));
40237
40621
  return stored ? stripStored(stored) : null;
40238
40622
  }
40239
40623
  async list(limit = 20) {
@@ -40272,9 +40656,9 @@ var init_fileStateStore = __esm({
40272
40656
  async loadDiscoveries(id) {
40273
40657
  const meta3 = id ? await this.get(id) : await this.head();
40274
40658
  if (!meta3) return [];
40275
- const stored = await readJsonFile(path40.join(this.commitsDir, `${meta3.id}.json`));
40659
+ const stored = await readJsonFile(path41.join(this.commitsDir, `${meta3.id}.json`));
40276
40660
  if (!stored?.artifactDir) return [];
40277
- const discPath = path40.join(this.stateDir, stored.artifactDir, "discoveries.json");
40661
+ const discPath = path41.join(this.stateDir, stored.artifactDir, "discoveries.json");
40278
40662
  return await readJsonFile(discPath) ?? [];
40279
40663
  }
40280
40664
  async materializeContext(id, maxChars = DEFAULT_MATERIALIZE_CHARS) {
@@ -41984,8 +42368,8 @@ function readPlan(ctx) {
41984
42368
  } catch {
41985
42369
  }
41986
42370
  }
41987
- const path63 = workspaceFile(ctx.rootDir, "plan");
41988
- const doc = ctx.storage.readIfExists(path63);
42371
+ const path64 = workspaceFile(ctx.rootDir, "plan");
42372
+ const doc = ctx.storage.readIfExists(path64);
41989
42373
  if (!doc) return { phases: [], tasks: [], milestones: [] };
41990
42374
  const meta3 = doc.meta;
41991
42375
  return {
@@ -42163,7 +42547,7 @@ function addMilestoneRecord(ctx, summary, input) {
42163
42547
  dueDate: input.dueDate,
42164
42548
  targetVersion: version2
42165
42549
  });
42166
- const path63 = join25(ctx.rootDir, "milestones", `${id}.md`);
42550
+ const path64 = join25(ctx.rootDir, "milestones", `${id}.md`);
42167
42551
  const meta3 = {
42168
42552
  kind: "milestone",
42169
42553
  id,
@@ -42180,7 +42564,7 @@ function addMilestoneRecord(ctx, summary, input) {
42180
42564
  `Target version: ${version2}`,
42181
42565
  ""
42182
42566
  ].join("\n");
42183
- ctx.storage.write(path63, meta3, body);
42567
+ ctx.storage.write(path64, meta3, body);
42184
42568
  return { id, created: true };
42185
42569
  }
42186
42570
  function readPlanSummary(ctx) {
@@ -42384,7 +42768,7 @@ function addIdeaStub(ctx) {
42384
42768
  const tags = args["tags"] ?? [];
42385
42769
  const category = args["category"] ?? "General";
42386
42770
  const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
42387
- const path63 = workspaceArtifact(ctx.rootDir, "decisions", id);
42771
+ const path64 = workspaceArtifact(ctx.rootDir, "decisions", id);
42388
42772
  const meta3 = {
42389
42773
  kind: "adr",
42390
42774
  status: "proposed",
@@ -42410,7 +42794,7 @@ function addIdeaStub(ctx) {
42410
42794
  ...consequences.map((c) => `- ${c}`),
42411
42795
  ""
42412
42796
  ].join("\n");
42413
- ctx.storage.write(path63, meta3, body);
42797
+ ctx.storage.write(path64, meta3, body);
42414
42798
  return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
42415
42799
  });
42416
42800
  }
@@ -42492,14 +42876,14 @@ function createDocumentStub(ctx) {
42492
42876
  ctx.storage.write(risksPath, riskMeta, content);
42493
42877
  return `Document "${title}" created at risks.md (workspace root).`;
42494
42878
  }
42495
- const path63 = workspaceArtifact(ctx.rootDir, "docs", slug);
42879
+ const path64 = workspaceArtifact(ctx.rootDir, "docs", slug);
42496
42880
  const meta3 = {
42497
42881
  kind: "doc",
42498
42882
  id: slug,
42499
42883
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
42500
42884
  tags
42501
42885
  };
42502
- ctx.storage.write(path63, meta3, content);
42886
+ ctx.storage.write(path64, meta3, content);
42503
42887
  return `Document "${title}" created at docs/${slug}.md.`;
42504
42888
  });
42505
42889
  }
@@ -42720,15 +43104,15 @@ __export(updater_exports, {
42720
43104
  import { createRequire as createRequire2 } from "node:module";
42721
43105
  import { spawn as spawn12 } from "node:child_process";
42722
43106
  import { existsSync as existsSync32 } from "node:fs";
42723
- import path41 from "node:path";
43107
+ import path42 from "node:path";
42724
43108
  import { fileURLToPath } from "node:url";
42725
43109
  function resolveBundledNpmCli(execPath = process.execPath) {
42726
- const dir = path41.dirname(execPath);
43110
+ const dir = path42.dirname(execPath);
42727
43111
  const candidates = [
42728
43112
  // Windows: C:\...\node.exe → C:\...\node_modules\npm\bin\npm-cli.js
42729
- path41.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
43113
+ path42.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
42730
43114
  // POSIX: <prefix>/bin/node → <prefix>/lib/node_modules/npm/bin/npm-cli.js
42731
- path41.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
43115
+ path42.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
42732
43116
  ];
42733
43117
  for (const candidate of candidates) {
42734
43118
  try {
@@ -42745,7 +43129,7 @@ function looksLikeBrokenShim(exitCode, output) {
42745
43129
  }
42746
43130
  function getCurrentVersion() {
42747
43131
  try {
42748
- const pkgPath = path41.resolve(__dirname2, "..", "..", "package.json");
43132
+ const pkgPath = path42.resolve(__dirname2, "..", "..", "package.json");
42749
43133
  const pkg = require2(pkgPath);
42750
43134
  return pkg.version;
42751
43135
  } catch {
@@ -42868,7 +43252,7 @@ var init_updater = __esm({
42868
43252
  "use strict";
42869
43253
  init_cmdline();
42870
43254
  require2 = createRequire2(import.meta.url);
42871
- __dirname2 = path41.dirname(fileURLToPath(import.meta.url));
43255
+ __dirname2 = path42.dirname(fileURLToPath(import.meta.url));
42872
43256
  REGISTRY_URL = "https://registry.npmjs.org/zelari-code/latest";
42873
43257
  }
42874
43258
  });
@@ -43056,10 +43440,10 @@ function getUserMcpPath() {
43056
43440
  function getProjectMcpPath(projectRoot) {
43057
43441
  return join26(projectRoot, ".zelari", "mcp.json");
43058
43442
  }
43059
- function readFile2(path63) {
43060
- if (!existsSync33(path63)) return {};
43443
+ function readFile3(path64) {
43444
+ if (!existsSync33(path64)) return {};
43061
43445
  try {
43062
- const parsed = JSON.parse(readFileSync28(path63, "utf8"));
43446
+ const parsed = JSON.parse(readFileSync28(path64, "utf8"));
43063
43447
  const out = {};
43064
43448
  for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
43065
43449
  if (!cfg || typeof cfg.command !== "string" || !cfg.command.trim()) continue;
@@ -43075,17 +43459,17 @@ function readFile2(path63) {
43075
43459
  return {};
43076
43460
  }
43077
43461
  }
43078
- function writeFile(path63, servers) {
43079
- mkdirSync16(dirname7(path63), { recursive: true });
43462
+ function writeFile(path64, servers) {
43463
+ mkdirSync16(dirname7(path64), { recursive: true });
43080
43464
  const body = { mcpServers: servers };
43081
- writeFileSync18(path63, `${JSON.stringify(body, null, 2)}
43465
+ writeFileSync18(path64, `${JSON.stringify(body, null, 2)}
43082
43466
  `, "utf8");
43083
43467
  }
43084
43468
  function listMcpServers(projectRoot) {
43085
43469
  const userPath = getUserMcpPath();
43086
- const userServers = readFile2(userPath);
43470
+ const userServers = readFile3(userPath);
43087
43471
  const projectPath = projectRoot && projectRoot.trim() ? getProjectMcpPath(projectRoot.trim()) : null;
43088
- const projectServers = projectPath ? readFile2(projectPath) : {};
43472
+ const projectServers = projectPath ? readFile3(projectPath) : {};
43089
43473
  const servers = [];
43090
43474
  for (const [name, cfg] of Object.entries(userServers)) {
43091
43475
  servers.push({ name, ...cfg, scope: "user", path: userPath });
@@ -43111,9 +43495,9 @@ function upsertMcpServer(opts) {
43111
43495
  if (!opts.config.command?.trim()) {
43112
43496
  return { ok: false, error: "command is required" };
43113
43497
  }
43114
- let path63;
43498
+ let path64;
43115
43499
  if (opts.scope === "user") {
43116
- path63 = getUserMcpPath();
43500
+ path64 = getUserMcpPath();
43117
43501
  } else {
43118
43502
  const root = opts.projectRoot?.trim();
43119
43503
  if (!root) {
@@ -43122,30 +43506,30 @@ function upsertMcpServer(opts) {
43122
43506
  error: "projectRoot required for project scope (Open Folder first)"
43123
43507
  };
43124
43508
  }
43125
- path63 = getProjectMcpPath(root);
43509
+ path64 = getProjectMcpPath(root);
43126
43510
  }
43127
- const current = readFile2(path63);
43511
+ const current = readFile3(path64);
43128
43512
  current[name] = {
43129
43513
  command: opts.config.command.trim(),
43130
43514
  args: opts.config.args,
43131
43515
  env: opts.config.env,
43132
43516
  enabled: opts.config.enabled !== false
43133
43517
  };
43134
- writeFile(path63, current);
43135
- return { ok: true, path: path63 };
43518
+ writeFile(path64, current);
43519
+ return { ok: true, path: path64 };
43136
43520
  }
43137
43521
  function removeMcpServer(opts) {
43138
- const path63 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
43139
- if (!path63) {
43522
+ const path64 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
43523
+ if (!path64) {
43140
43524
  return { ok: false, error: "projectRoot required for project scope" };
43141
43525
  }
43142
- const current = readFile2(path63);
43526
+ const current = readFile3(path64);
43143
43527
  if (!(opts.name in current)) {
43144
- return { ok: false, error: `Server "${opts.name}" not found in ${path63}` };
43528
+ return { ok: false, error: `Server "${opts.name}" not found in ${path64}` };
43145
43529
  }
43146
43530
  delete current[opts.name];
43147
- writeFile(path63, current);
43148
- return { ok: true, path: path63 };
43531
+ writeFile(path64, current);
43532
+ return { ok: true, path: path64 };
43149
43533
  }
43150
43534
  var init_mcpConfigIo = __esm({
43151
43535
  "src/cli/mcp/mcpConfigIo.ts"() {
@@ -43544,12 +43928,12 @@ __export(agentsMd_exports, {
43544
43928
  import { existsSync as existsSync35, readFileSync as readFileSync30, writeFileSync as writeFileSync19 } from "node:fs";
43545
43929
  import { createHash as createHash11 } from "node:crypto";
43546
43930
  import { join as join28 } from "node:path";
43547
- import { readFile as readFile3 } from "node:fs/promises";
43931
+ import { readFile as readFile4 } from "node:fs/promises";
43548
43932
  async function readPackageJson2(projectRoot) {
43549
- const path63 = join28(projectRoot, "package.json");
43550
- if (!existsSync35(path63)) return null;
43933
+ const path64 = join28(projectRoot, "package.json");
43934
+ if (!existsSync35(path64)) return null;
43551
43935
  try {
43552
- return JSON.parse(await readFile3(path63, "utf8"));
43936
+ return JSON.parse(await readFile4(path64, "utf8"));
43553
43937
  } catch {
43554
43938
  return null;
43555
43939
  }
@@ -43631,9 +44015,9 @@ async function genBuild(ctx) {
43631
44015
  ].join("\n");
43632
44016
  }
43633
44017
  async function genOpenQuestions(ctx) {
43634
- const path63 = join28(ctx.rootDir, "risks.md");
43635
- if (!existsSync35(path63)) return "_No open questions._";
43636
- const content = readFileSync30(path63, "utf8");
44018
+ const path64 = join28(ctx.rootDir, "risks.md");
44019
+ if (!existsSync35(path64)) return "_No open questions._";
44020
+ const content = readFileSync30(path64, "utf8");
43637
44021
  const lines = content.split("\n");
43638
44022
  const questions = [];
43639
44023
  let currentTitle = "";
@@ -43907,9 +44291,9 @@ function versionKey(value) {
43907
44291
  function firstString2(v) {
43908
44292
  return typeof v === "string" && v.trim().length > 0 ? v : null;
43909
44293
  }
43910
- function readFileSyncSafe(path63) {
44294
+ function readFileSyncSafe(path64) {
43911
44295
  try {
43912
- return readFileSync31(path63, "utf8");
44296
+ return readFileSync31(path64, "utf8");
43913
44297
  } catch {
43914
44298
  return null;
43915
44299
  }
@@ -44372,8 +44756,8 @@ async function runPostCouncilHook(ctx, options) {
44372
44756
  sources: scope.sources
44373
44757
  } : void 0
44374
44758
  });
44375
- const path63 = writeCouncilCompletion(ctx.rootDir, completion);
44376
- completionHook = { ran: true, path: path63, completion };
44759
+ const path64 = writeCouncilCompletion(ctx.rootDir, completion);
44760
+ completionHook = { ran: true, path: path64, completion };
44377
44761
  } catch (err) {
44378
44762
  completionHook = {
44379
44763
  ran: true,
@@ -44418,7 +44802,7 @@ import {
44418
44802
  writeFileSync as writeFileSync21,
44419
44803
  mkdirSync as mkdirSync17
44420
44804
  } from "node:fs";
44421
- import path42 from "node:path";
44805
+ import path43 from "node:path";
44422
44806
  import os10 from "node:os";
44423
44807
  var FeedbackStore;
44424
44808
  var init_councilFeedback = __esm({
@@ -44429,7 +44813,7 @@ var init_councilFeedback = __esm({
44429
44813
  now;
44430
44814
  entries = [];
44431
44815
  constructor(options = {}) {
44432
- this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path42.join(os10.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
44816
+ this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path43.join(os10.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
44433
44817
  this.now = options.now ?? Date.now;
44434
44818
  this.load();
44435
44819
  }
@@ -44535,7 +44919,7 @@ var init_councilFeedback = __esm({
44535
44919
  }
44536
44920
  }
44537
44921
  save() {
44538
- mkdirSync17(path42.dirname(this.file), { recursive: true });
44922
+ mkdirSync17(path43.dirname(this.file), { recursive: true });
44539
44923
  writeFileSync21(
44540
44924
  this.file,
44541
44925
  JSON.stringify({ entries: this.entries }, null, 2),
@@ -44603,7 +44987,7 @@ import { execFile as execFile3 } from "node:child_process";
44603
44987
  import { promisify as promisify2 } from "node:util";
44604
44988
  import { mkdtempSync, rmSync as rmSync2 } from "node:fs";
44605
44989
  import { tmpdir as tmpdir2 } from "node:os";
44606
- import path43 from "node:path";
44990
+ import path44 from "node:path";
44607
44991
  import { randomUUID as randomUUID3 } from "node:crypto";
44608
44992
  async function git3(cwd, args, env) {
44609
44993
  const { stdout } = await execFileAsync2("git", ["-C", cwd, ...args], {
@@ -44623,8 +45007,8 @@ async function isGitRepo(cwd) {
44623
45007
  return await gitSafe(cwd, ["rev-parse", "--is-inside-work-tree"]) === "true";
44624
45008
  }
44625
45009
  async function withTempIndex(fn) {
44626
- const dir = mkdtempSync(path43.join(tmpdir2(), "zelari-ckpt-"));
44627
- const indexFile = path43.join(dir, "index");
45010
+ const dir = mkdtempSync(path44.join(tmpdir2(), "zelari-ckpt-"));
45011
+ const indexFile = path44.join(dir, "index");
44628
45012
  try {
44629
45013
  return await fn(indexFile);
44630
45014
  } finally {
@@ -44715,7 +45099,7 @@ async function restoreCheckpoint(cwd, id) {
44715
45099
  const deleted = [];
44716
45100
  for (const rel2 of added) {
44717
45101
  try {
44718
- rmSync2(path43.join(cwd, rel2), { force: true });
45102
+ rmSync2(path44.join(cwd, rel2), { force: true });
44719
45103
  deleted.push(rel2);
44720
45104
  } catch {
44721
45105
  }
@@ -44816,7 +45200,7 @@ __export(fileBackend_exports, {
44816
45200
  });
44817
45201
  import { randomUUID as randomUUID4 } from "node:crypto";
44818
45202
  import { promises as fs23 } from "node:fs";
44819
- import * as path44 from "node:path";
45203
+ import * as path45 from "node:path";
44820
45204
  function tokenize(text) {
44821
45205
  return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 3);
44822
45206
  }
@@ -44860,8 +45244,8 @@ var init_fileBackend = __esm({
44860
45244
  logPath = "";
44861
45245
  memoryDir = "";
44862
45246
  async init(projectRoot) {
44863
- this.memoryDir = path44.join(projectRoot, ".zelari", "memory");
44864
- this.logPath = path44.join(this.memoryDir, "log.jsonl");
45247
+ this.memoryDir = path45.join(projectRoot, ".zelari", "memory");
45248
+ this.logPath = path45.join(this.memoryDir, "log.jsonl");
44865
45249
  await fs23.mkdir(this.memoryDir, { recursive: true });
44866
45250
  }
44867
45251
  async add(content, metadata = {}, graph) {
@@ -44934,12 +45318,12 @@ var init_fileBackend = __esm({
44934
45318
 
44935
45319
  // src/cli/traceStore.ts
44936
45320
  import { promises as fs24 } from "node:fs";
44937
- import * as path45 from "node:path";
45321
+ import * as path46 from "node:path";
44938
45322
  function traceDir(projectRoot) {
44939
- return path45.join(projectRoot, ".zelari", "trace");
45323
+ return path46.join(projectRoot, ".zelari", "trace");
44940
45324
  }
44941
45325
  function tracePath(projectRoot, missionId) {
44942
- return path45.join(traceDir(projectRoot), `${missionId}.json`);
45326
+ return path46.join(traceDir(projectRoot), `${missionId}.json`);
44943
45327
  }
44944
45328
  async function saveTrace(projectRoot, missionId, entries) {
44945
45329
  const dir = traceDir(projectRoot);
@@ -44974,7 +45358,7 @@ __export(zelariMission_exports, {
44974
45358
  });
44975
45359
  import { randomUUID as randomUUID5 } from "node:crypto";
44976
45360
  import { promises as fs25 } from "node:fs";
44977
- import * as path46 from "node:path";
45361
+ import * as path47 from "node:path";
44978
45362
  function resolveMaxIterations(env = process.env) {
44979
45363
  const raw = env.ZELARI_MISSION_MAX_ITER;
44980
45364
  const n = raw ? Number.parseInt(raw, 10) : DEFAULT_MAX_ITER;
@@ -45002,10 +45386,10 @@ function isMissionAutoStart(env = process.env) {
45002
45386
  return env.ZELARI_MISSION_AUTO === "1";
45003
45387
  }
45004
45388
  async function writeMissionState(projectRoot, state3) {
45005
- const dir = path46.join(projectRoot, ".zelari");
45389
+ const dir = path47.join(projectRoot, ".zelari");
45006
45390
  await fs25.mkdir(dir, { recursive: true });
45007
45391
  await fs25.writeFile(
45008
- path46.join(dir, "mission-state.json"),
45392
+ path47.join(dir, "mission-state.json"),
45009
45393
  JSON.stringify(state3, null, 2) + "\n",
45010
45394
  "utf8"
45011
45395
  );
@@ -45237,6 +45621,17 @@ async function runZelariMission(userMessage, brief, deps) {
45237
45621
  deps.emit(`[zelari] state commit skipped: ${commitRes.error}`);
45238
45622
  }
45239
45623
  }
45624
+ const missionProgress = {
45625
+ criteriaTotal: 1,
45626
+ criteriaPassed: completionOk ? 1 : 0,
45627
+ ratio: completionOk ? 1 : 0,
45628
+ evidenceComplete: completionOk && (typeof result.writeCount === "number" ? result.writeCount > 0 : true)
45629
+ };
45630
+ const advice = evaluateMissionContinuation({
45631
+ progress: missionProgress,
45632
+ budget: { iterationsUsed: implStep, iterationsMax: maxIter }
45633
+ });
45634
+ deps.onMissionProgress?.(advice, step);
45240
45635
  if (completionOk) {
45241
45636
  state3.status = "success";
45242
45637
  deps.onMissionPhase?.("done", "mvp-green");
@@ -45283,6 +45678,7 @@ var DEFAULT_MAX_ITER, DEFAULT_MAX_STALL;
45283
45678
  var init_zelariMission = __esm({
45284
45679
  "src/cli/zelariMission.ts"() {
45285
45680
  "use strict";
45681
+ init_mission2();
45286
45682
  init_fileBackend();
45287
45683
  init_checkpointManager();
45288
45684
  init_fileStateStore();
@@ -45615,7 +46011,7 @@ function safeSocketPath(socketPath) {
45615
46011
  return socketPath.trim();
45616
46012
  }
45617
46013
  function startPermissionBroker(socketPath, handlers, opts) {
45618
- const path63 = safeSocketPath(socketPath);
46014
+ const path64 = safeSocketPath(socketPath);
45619
46015
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
45620
46016
  const sockets = /* @__PURE__ */ new Set();
45621
46017
  const server = createServer2((socket) => {
@@ -45715,10 +46111,10 @@ function startPermissionBroker(socketPath, handlers, opts) {
45715
46111
  return new Promise((resolve3, reject) => {
45716
46112
  const onError = (err) => reject(err);
45717
46113
  server.once("error", onError);
45718
- server.listen(path63, () => {
46114
+ server.listen(path64, () => {
45719
46115
  server.removeListener("error", onError);
45720
46116
  resolve3({
45721
- socketPath: path63,
46117
+ socketPath: path64,
45722
46118
  stop: () => new Promise((res) => {
45723
46119
  for (const s of sockets) s.destroy();
45724
46120
  sockets.clear();
@@ -45729,7 +46125,7 @@ function startPermissionBroker(socketPath, handlers, opts) {
45729
46125
  if (done) return;
45730
46126
  done = true;
45731
46127
  if (process.platform !== "win32") {
45732
- unlink(path63, () => res());
46128
+ unlink(path64, () => res());
45733
46129
  } else {
45734
46130
  res();
45735
46131
  }
@@ -45742,11 +46138,11 @@ function startPermissionBroker(socketPath, handlers, opts) {
45742
46138
  });
45743
46139
  }
45744
46140
  function requestBrokerAsk(socketPath, ask, opts) {
45745
- const path63 = safeSocketPath(socketPath);
46141
+ const path64 = safeSocketPath(socketPath);
45746
46142
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
45747
46143
  const connectTimeoutMs = opts?.connectTimeoutMs ?? PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS;
45748
46144
  return new Promise((resolve3, reject) => {
45749
- const socket = connect(path63);
46145
+ const socket = connect(path64);
45750
46146
  let buffer = "";
45751
46147
  let settled = false;
45752
46148
  const settle = (fn) => {
@@ -45761,7 +46157,7 @@ function requestBrokerAsk(socketPath, ask, opts) {
45761
46157
  settle(
45762
46158
  () => reject(
45763
46159
  new Error(
45764
- `permission broker unavailable at "${path63}" (connect timed out after ${connectTimeoutMs}ms)`
46160
+ `permission broker unavailable at "${path64}" (connect timed out after ${connectTimeoutMs}ms)`
45765
46161
  )
45766
46162
  )
45767
46163
  );
@@ -46494,9 +46890,9 @@ __export(graphMemory_exports, {
46494
46890
  toGraphSnapshot: () => toGraphSnapshot
46495
46891
  });
46496
46892
  import { promises as fs26 } from "node:fs";
46497
- import path48 from "node:path";
46893
+ import path49 from "node:path";
46498
46894
  function snapshotPath(cwd) {
46499
- return path48.join(cwd, SNAPSHOT_DIR, SNAPSHOT_FILE);
46895
+ return path49.join(cwd, SNAPSHOT_DIR, SNAPSHOT_FILE);
46500
46896
  }
46501
46897
  function toGraphSnapshot(graph, opts) {
46502
46898
  const unresolved = (opts.unresolvedFindings ?? []).map((u) => ({
@@ -46523,7 +46919,7 @@ async function saveGraphSnapshot(cwd, snapshot) {
46523
46919
  try {
46524
46920
  await fs26.access(cwd);
46525
46921
  const file2 = snapshotPath(cwd);
46526
- await fs26.mkdir(path48.dirname(file2), { recursive: true });
46922
+ await fs26.mkdir(path49.dirname(file2), { recursive: true });
46527
46923
  await fs26.writeFile(file2, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
46528
46924
  } catch {
46529
46925
  }
@@ -46590,7 +46986,7 @@ var SNAPSHOT_DIR, SNAPSHOT_FILE, MAX_SNAPSHOT_FINDINGS_CHARS;
46590
46986
  var init_graphMemory = __esm({
46591
46987
  "src/cli/kraken/graphMemory.ts"() {
46592
46988
  "use strict";
46593
- SNAPSHOT_DIR = path48.join(".zelari", "kraken");
46989
+ SNAPSHOT_DIR = path49.join(".zelari", "kraken");
46594
46990
  SNAPSHOT_FILE = "last-graph.json";
46595
46991
  MAX_SNAPSHOT_FINDINGS_CHARS = 400;
46596
46992
  }
@@ -46606,14 +47002,14 @@ var init_tentacle = __esm({
46606
47002
 
46607
47003
  // src/cli/kraken/workbench.ts
46608
47004
  import { promises as fs27 } from "node:fs";
46609
- import path49 from "node:path";
47005
+ import path50 from "node:path";
46610
47006
  function isWorkbenchEnabled(env = process.env) {
46611
47007
  const v = (env.ZELARI_KRAKEN_WORKBENCH ?? "1").trim().toLowerCase();
46612
47008
  if (v === "0" || v === "false" || v === "no" || v === "off") return false;
46613
47009
  return true;
46614
47010
  }
46615
47011
  function workbenchPath(cwd, graphId) {
46616
- return path49.join(cwd, ".zelari", "radio", `workbench-${graphId}.md`);
47012
+ return path50.join(cwd, ".zelari", "radio", `workbench-${graphId}.md`);
46617
47013
  }
46618
47014
  function countByStatus2(nodes) {
46619
47015
  const out = { pending: 0, running: 0, done: 0, error: 0, skipped: 0 };
@@ -46776,7 +47172,7 @@ var init_workbench = __esm({
46776
47172
  if (!this.enabled) return null;
46777
47173
  if (!this.dirty && this.lastWrite) return this.lastWrite;
46778
47174
  const out = workbenchPath(this.cwd, this.graphId);
46779
- await fs27.mkdir(path49.dirname(out), { recursive: true });
47175
+ await fs27.mkdir(path50.dirname(out), { recursive: true });
46780
47176
  const body = this.render();
46781
47177
  const tmp = `${out}.${process.pid}.${Date.now()}.tmp`;
46782
47178
  await fs27.writeFile(tmp, body, "utf8");
@@ -46973,7 +47369,7 @@ __export(executor_exports, {
46973
47369
  thoroughnessForKind: () => thoroughnessForKind
46974
47370
  });
46975
47371
  import { existsSync as existsSync40 } from "node:fs";
46976
- import path50 from "node:path";
47372
+ import path51 from "node:path";
46977
47373
  function resolveMaxParallel(env = process.env) {
46978
47374
  const raw = env.ZELARI_KRAKEN_MAX_PARALLEL;
46979
47375
  if (raw === void 0 || raw === "") return DEFAULT_MAX_PARALLEL;
@@ -47031,7 +47427,7 @@ function isWorldModelGateEnabled(cwd, env = process.env, checksExists = defaultC
47031
47427
  }
47032
47428
  function defaultChecksExists(cwd) {
47033
47429
  try {
47034
- return existsSync40(path50.join(cwd, ".zelari", "world", "checks.json"));
47430
+ return existsSync40(path51.join(cwd, ".zelari", "world", "checks.json"));
47035
47431
  } catch {
47036
47432
  return false;
47037
47433
  }
@@ -48370,10 +48766,10 @@ var init_prereqChecks = __esm({
48370
48766
 
48371
48767
  // src/cli/plugins/prefs.ts
48372
48768
  import { existsSync as existsSync42, readFileSync as readFileSync35, writeFileSync as writeFileSync22, mkdirSync as mkdirSync18 } from "node:fs";
48373
- import path53 from "node:path";
48769
+ import path54 from "node:path";
48374
48770
  import os11 from "node:os";
48375
48771
  function getPluginPrefsPath() {
48376
- return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path53.join(os11.homedir(), ".tmp", "zelari-code", "plugins.json");
48772
+ return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path54.join(os11.homedir(), ".tmp", "zelari-code", "plugins.json");
48377
48773
  }
48378
48774
  function getPluginPrefs() {
48379
48775
  const file2 = getPluginPrefsPath();
@@ -48394,7 +48790,7 @@ function getPluginPrefs() {
48394
48790
  }
48395
48791
  function writePluginPrefs(prefs) {
48396
48792
  const file2 = getPluginPrefsPath();
48397
- mkdirSync18(path53.dirname(file2), { recursive: true });
48793
+ mkdirSync18(path54.dirname(file2), { recursive: true });
48398
48794
  writeFileSync22(file2, JSON.stringify(prefs, null, 2), {
48399
48795
  encoding: "utf-8",
48400
48796
  mode: 384
@@ -48431,7 +48827,7 @@ __export(registry_exports, {
48431
48827
  isBinaryOnPath: () => isBinaryOnPath
48432
48828
  });
48433
48829
  import { existsSync as existsSync43 } from "node:fs";
48434
- import path54 from "node:path";
48830
+ import path55 from "node:path";
48435
48831
  function detectLocalBin(bin) {
48436
48832
  return (cwd) => {
48437
48833
  try {
@@ -48449,7 +48845,7 @@ function isBinaryOnPath(bin, opts = {}) {
48449
48845
  const platform = opts.platform ?? process.platform;
48450
48846
  const exists = opts.exists ?? existsSync43;
48451
48847
  const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
48452
- const pathMod = platform === "win32" ? path54.win32 : path54.posix;
48848
+ const pathMod = platform === "win32" ? path55.win32 : path55.posix;
48453
48849
  const sep2 = platform === "win32" ? ";" : ":";
48454
48850
  const dirs = pathEnv.split(sep2).filter((d) => d.length > 0);
48455
48851
  const candidates = [bin];
@@ -49396,9 +49792,9 @@ __export(triggerLock_exports, {
49396
49792
  releaseLock: () => releaseLock
49397
49793
  });
49398
49794
  import { promises as fs35 } from "node:fs";
49399
- import * as path59 from "node:path";
49795
+ import * as path60 from "node:path";
49400
49796
  function lockPath(projectRoot) {
49401
- return path59.join(projectRoot, ".zelari", "trigger.lock");
49797
+ return path60.join(projectRoot, ".zelari", "trigger.lock");
49402
49798
  }
49403
49799
  function isPidAlive(pid) {
49404
49800
  try {
@@ -49411,7 +49807,7 @@ function isPidAlive(pid) {
49411
49807
  }
49412
49808
  async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date()) {
49413
49809
  const lp = lockPath(projectRoot);
49414
- const dir = path59.dirname(lp);
49810
+ const dir = path60.dirname(lp);
49415
49811
  await fs35.mkdir(dir, { recursive: true });
49416
49812
  try {
49417
49813
  const raw = await fs35.readFile(lp, "utf8");
@@ -50005,7 +50401,7 @@ function upsertSkill(opts) {
50005
50401
  }
50006
50402
  dir = getProjectSkillsDir(root);
50007
50403
  }
50008
- const path63 = skillFilePath(dir, name);
50404
+ const path64 = skillFilePath(dir, name);
50009
50405
  const content = serializeSkillMd({
50010
50406
  name,
50011
50407
  description,
@@ -50014,13 +50410,13 @@ function upsertSkill(opts) {
50014
50410
  tools: opts.tools,
50015
50411
  cost: opts.cost
50016
50412
  });
50017
- const parsed = parseSkillMd(content, path63);
50413
+ const parsed = parseSkillMd(content, path64);
50018
50414
  if (!parsed) {
50019
50415
  return { ok: false, error: "Generated SKILL.md failed validation" };
50020
50416
  }
50021
- mkdirSync21(dirname10(path63), { recursive: true });
50022
- writeFileSync24(path63, content, "utf8");
50023
- return { ok: true, path: path63 };
50417
+ mkdirSync21(dirname10(path64), { recursive: true });
50418
+ writeFileSync24(path64, content, "utf8");
50419
+ return { ok: true, path: path64 };
50024
50420
  }
50025
50421
  function removeSkill(opts) {
50026
50422
  const name = opts.name.trim().toLowerCase();
@@ -50038,8 +50434,8 @@ function removeSkill(opts) {
50038
50434
  dir = getProjectSkillsDir(root);
50039
50435
  }
50040
50436
  const skillDir = join37(dir, name);
50041
- const path63 = skillFilePath(dir, name);
50042
- if (!existsSync48(path63) && !existsSync48(skillDir)) {
50437
+ const path64 = skillFilePath(dir, name);
50438
+ if (!existsSync48(path64) && !existsSync48(skillDir)) {
50043
50439
  return { ok: false, error: `Skill "${name}" not found in ${dir}` };
50044
50440
  }
50045
50441
  try {
@@ -50050,7 +50446,7 @@ function removeSkill(opts) {
50050
50446
  error: err instanceof Error ? err.message : String(err)
50051
50447
  };
50052
50448
  }
50053
- return { ok: true, path: path63 };
50449
+ return { ok: true, path: path64 };
50054
50450
  }
50055
50451
  var NAME_RE, BUILTIN_SKILL_MODULES, builtinsLoaded;
50056
50452
  var init_skillConfigIo = __esm({
@@ -50368,12 +50764,12 @@ function ensureHome() {
50368
50764
  }
50369
50765
  }
50370
50766
  function loadCompanionConfig() {
50371
- const path63 = getCompanionConfigPath();
50372
- if (!existsSync49(path63)) {
50767
+ const path64 = getCompanionConfigPath();
50768
+ if (!existsSync49(path64)) {
50373
50769
  return { projects: [] };
50374
50770
  }
50375
50771
  try {
50376
- const raw = JSON.parse(readFileSync39(path63, "utf8"));
50772
+ const raw = JSON.parse(readFileSync39(path64, "utf8"));
50377
50773
  const projects = Array.isArray(raw.projects) ? raw.projects.filter(
50378
50774
  (p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
50379
50775
  ).map((p3) => ({
@@ -50411,16 +50807,16 @@ function loadOrCreateToken(explicit) {
50411
50807
  return { token: explicit.trim(), created: false };
50412
50808
  }
50413
50809
  ensureHome();
50414
- const path63 = getCompanionTokenPath();
50415
- if (existsSync49(path63)) {
50416
- const t = readFileSync39(path63, "utf8").trim();
50810
+ const path64 = getCompanionTokenPath();
50811
+ if (existsSync49(path64)) {
50812
+ const t = readFileSync39(path64, "utf8").trim();
50417
50813
  if (t) return { token: t, created: false };
50418
50814
  }
50419
50815
  const token = randomBytes5(24).toString("base64url");
50420
- writeFileSync25(path63, token + "\n", "utf8");
50816
+ writeFileSync25(path64, token + "\n", "utf8");
50421
50817
  try {
50422
50818
  const fs37 = __require("node:fs");
50423
- fs37.chmodSync?.(path63, 384);
50819
+ fs37.chmodSync?.(path64, 384);
50424
50820
  } catch {
50425
50821
  }
50426
50822
  return { token, created: true };
@@ -50445,17 +50841,17 @@ function mergeProjects(cfg, extraPaths) {
50445
50841
  byId.set(p3.id, p3);
50446
50842
  }
50447
50843
  for (const raw of extraPaths) {
50448
- const path63 = raw.trim();
50449
- if (!path63) continue;
50450
- let id = slugFromPath(path63);
50844
+ const path64 = raw.trim();
50845
+ if (!path64) continue;
50846
+ let id = slugFromPath(path64);
50451
50847
  let n = 2;
50452
- while (byId.has(id) && byId.get(id).path !== path63) {
50453
- id = `${slugFromPath(path63)}-${n++}`;
50848
+ while (byId.has(id) && byId.get(id).path !== path64) {
50849
+ id = `${slugFromPath(path64)}-${n++}`;
50454
50850
  }
50455
50851
  byId.set(id, {
50456
50852
  id,
50457
- name: slugFromPath(path63),
50458
- path: path63
50853
+ name: slugFromPath(path64),
50854
+ path: path64
50459
50855
  });
50460
50856
  }
50461
50857
  return [...byId.values()];
@@ -50832,9 +51228,9 @@ async function runCompanionServe(opts = {}) {
50832
51228
  return;
50833
51229
  }
50834
51230
  const url2 = parseUrl(req);
50835
- const path63 = url2.pathname.replace(/\/+$/, "") || "/";
51231
+ const path64 = url2.pathname.replace(/\/+$/, "") || "/";
50836
51232
  try {
50837
- if (req.method === "GET" && (path63 === "/health" || path63 === "/v1/health")) {
51233
+ if (req.method === "GET" && (path64 === "/health" || path64 === "/v1/health")) {
50838
51234
  sendJson2(res, 200, {
50839
51235
  ok: true,
50840
51236
  service: "zelari-companion",
@@ -50846,18 +51242,18 @@ async function runCompanionServe(opts = {}) {
50846
51242
  });
50847
51243
  return;
50848
51244
  }
50849
- if (path63.startsWith("/v1")) {
51245
+ if (path64.startsWith("/v1")) {
50850
51246
  if (!tokenMatches(token, getBearer(req))) {
50851
51247
  sendJson2(res, 401, { ok: false, error: "unauthorized" });
50852
51248
  return;
50853
51249
  }
50854
51250
  }
50855
- if (req.method === "GET" && path63 === "/v1/config") {
51251
+ if (req.method === "GET" && path64 === "/v1/config") {
50856
51252
  const snap = buildDesktopConfigSnapshot();
50857
51253
  sendJson2(res, 200, { ok: true, ...snap });
50858
51254
  return;
50859
51255
  }
50860
- if (req.method === "GET" && path63 === "/v1/projects") {
51256
+ if (req.method === "GET" && path64 === "/v1/projects") {
50861
51257
  sendJson2(res, 200, {
50862
51258
  ok: true,
50863
51259
  projects: projects.map((p3) => ({
@@ -50868,7 +51264,7 @@ async function runCompanionServe(opts = {}) {
50868
51264
  });
50869
51265
  return;
50870
51266
  }
50871
- if (req.method === "GET" && path63 === "/v1/runs") {
51267
+ if (req.method === "GET" && path64 === "/v1/runs") {
50872
51268
  sendJson2(res, 200, {
50873
51269
  ok: true,
50874
51270
  active: runs.getActive(),
@@ -50886,7 +51282,7 @@ async function runCompanionServe(opts = {}) {
50886
51282
  });
50887
51283
  return;
50888
51284
  }
50889
- if (req.method === "POST" && path63 === "/v1/runs") {
51285
+ if (req.method === "POST" && path64 === "/v1/runs") {
50890
51286
  const raw = await readBody(req);
50891
51287
  let body = {};
50892
51288
  try {
@@ -50933,7 +51329,7 @@ async function runCompanionServe(opts = {}) {
50933
51329
  });
50934
51330
  return;
50935
51331
  }
50936
- const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path63);
51332
+ const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path64);
50937
51333
  if (req.method === "GET" && eventsMatch) {
50938
51334
  const runId = eventsMatch[1];
50939
51335
  const run = runs.getRun(runId);
@@ -50998,7 +51394,7 @@ async function runCompanionServe(opts = {}) {
50998
51394
  }, 500);
50999
51395
  return;
51000
51396
  }
51001
- const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path63);
51397
+ const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path64);
51002
51398
  if (req.method === "POST" && cancelMatch) {
51003
51399
  const runId = cancelMatch[1];
51004
51400
  const result = runs.cancel(runId);
@@ -51151,11 +51547,11 @@ import { execSync as execSync2 } from "node:child_process";
51151
51547
  import { existsSync as existsSync51, readFileSync as readFileSync40, readlinkSync, statSync as statSync10 } from "node:fs";
51152
51548
  import { createRequire as createRequire3 } from "node:module";
51153
51549
  import { fileURLToPath as fileURLToPath2 } from "node:url";
51154
- import path61 from "node:path";
51550
+ import path62 from "node:path";
51155
51551
  function findPackageRoot(start) {
51156
51552
  let dir = start;
51157
51553
  for (let i = 0; i < 6; i += 1) {
51158
- const candidate = path61.join(dir, "package.json");
51554
+ const candidate = path62.join(dir, "package.json");
51159
51555
  if (existsSync51(candidate)) {
51160
51556
  try {
51161
51557
  const pkg = JSON.parse(readFileSync40(candidate, "utf8"));
@@ -51163,11 +51559,11 @@ function findPackageRoot(start) {
51163
51559
  } catch {
51164
51560
  }
51165
51561
  }
51166
- const parent = path61.dirname(dir);
51562
+ const parent = path62.dirname(dir);
51167
51563
  if (parent === dir) break;
51168
51564
  dir = parent;
51169
51565
  }
51170
- return path61.resolve(__dirname3, "..", "..", "..");
51566
+ return path62.resolve(__dirname3, "..", "..", "..");
51171
51567
  }
51172
51568
  function tryExec(cmd) {
51173
51569
  try {
@@ -51181,7 +51577,7 @@ function tryExec(cmd) {
51181
51577
  }
51182
51578
  function readPackageJson3() {
51183
51579
  try {
51184
- const pkgPath = path61.join(packageRoot, "package.json");
51580
+ const pkgPath = path62.join(packageRoot, "package.json");
51185
51581
  return JSON.parse(readFileSync40(pkgPath, "utf8"));
51186
51582
  } catch {
51187
51583
  return null;
@@ -51197,7 +51593,7 @@ function checkShim(pkgName) {
51197
51593
  }
51198
51594
  const isWin = process.platform === "win32";
51199
51595
  const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
51200
- const shimPath = path61.join(prefix, shimName);
51596
+ const shimPath = path62.join(prefix, shimName);
51201
51597
  if (!existsSync51(shimPath)) {
51202
51598
  return FAIL(
51203
51599
  `shim not found at ${shimPath}
@@ -51225,8 +51621,8 @@ function checkShim(pkgName) {
51225
51621
  fix: npm install -g ${pkgName}@latest --force`
51226
51622
  );
51227
51623
  }
51228
- const resolved = path61.resolve(path61.dirname(shimPath), target);
51229
- const expected = path61.join(
51624
+ const resolved = path62.resolve(path62.dirname(shimPath), target);
51625
+ const expected = path62.join(
51230
51626
  prefix,
51231
51627
  "node_modules",
51232
51628
  pkgName,
@@ -51265,7 +51661,7 @@ function checkNode(pkg) {
51265
51661
  return OK(`node ${raw}`);
51266
51662
  }
51267
51663
  function checkBundle() {
51268
- const bundle = path61.join(packageRoot, "dist", "cli", "main.bundled.js");
51664
+ const bundle = path62.join(packageRoot, "dist", "cli", "main.bundled.js");
51269
51665
  if (!existsSync51(bundle)) {
51270
51666
  return FAIL(
51271
51667
  `dist/cli/main.bundled.js missing at ${bundle}
@@ -51286,7 +51682,7 @@ function checkRuntimeDeps() {
51286
51682
  const missing = [];
51287
51683
  for (const dep of required2) {
51288
51684
  try {
51289
- const localReq = createRequire3(path61.join(packageRoot, "package.json"));
51685
+ const localReq = createRequire3(path62.join(packageRoot, "package.json"));
51290
51686
  localReq.resolve(dep);
51291
51687
  } catch {
51292
51688
  missing.push(dep);
@@ -51486,7 +51882,7 @@ var init_doctor = __esm({
51486
51882
  init_metrics2();
51487
51883
  init_contextGrowthSummary();
51488
51884
  require3 = createRequire3(import.meta.url);
51489
- __dirname3 = path61.dirname(fileURLToPath2(import.meta.url));
51885
+ __dirname3 = path62.dirname(fileURLToPath2(import.meta.url));
51490
51886
  packageRoot = findPackageRoot(__dirname3);
51491
51887
  OK = (message) => ({
51492
51888
  ok: true,
@@ -51670,15 +52066,15 @@ __export(inspect_exports, {
51670
52066
  collectInspectReport: () => collectInspectReport,
51671
52067
  runInspect: () => runInspect
51672
52068
  });
51673
- import path62 from "node:path";
52069
+ import path63 from "node:path";
51674
52070
  import { existsSync as existsSync52, readFileSync as readFileSync41, readdirSync as readdirSync11 } from "node:fs";
51675
52071
  import { homedir as homedir14 } from "node:os";
51676
52072
  async function collectInspectReport(cwd = process.cwd()) {
51677
52073
  ensureBuiltinSkillsLoadedSync();
51678
52074
  const snap = listSkillsSnapshot(cwd);
51679
52075
  const mcp = listMcpServers(cwd);
51680
- const userMcpPath = path62.join(homedir14(), ".zelari-code", "mcp.json");
51681
- const projectMcpPath = path62.join(cwd, ".zelari", "mcp.json");
52076
+ const userMcpPath = path63.join(homedir14(), ".zelari-code", "mcp.json");
52077
+ const projectMcpPath = path63.join(cwd, ".zelari", "mcp.json");
51682
52078
  const globalHooks = globalHooksDir();
51683
52079
  const projectHooks = projectHooksDir(cwd);
51684
52080
  const projectTrusted = isFolderTrusted(cwd);
@@ -51706,9 +52102,9 @@ async function collectInspectReport(cwd = process.cwd()) {
51706
52102
  configSources: [
51707
52103
  { path: userMcpPath, exists: existsSync52(userMcpPath) },
51708
52104
  { path: projectMcpPath, exists: existsSync52(projectMcpPath) },
51709
- { path: path62.join(homedir14(), ".zelari-code", "provider.json"), exists: existsSync52(path62.join(homedir14(), ".zelari-code", "provider.json")) },
51710
- { path: path62.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync52(path62.join(cwd, ".zelari", "AGENTS.md")) },
51711
- { path: path62.join(cwd, "AGENTS.md"), exists: existsSync52(path62.join(cwd, "AGENTS.md")) }
52105
+ { path: path63.join(homedir14(), ".zelari-code", "provider.json"), exists: existsSync52(path63.join(homedir14(), ".zelari-code", "provider.json")) },
52106
+ { path: path63.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync52(path63.join(cwd, ".zelari", "AGENTS.md")) },
52107
+ { path: path63.join(cwd, "AGENTS.md"), exists: existsSync52(path63.join(cwd, "AGENTS.md")) }
51712
52108
  ],
51713
52109
  skills: {
51714
52110
  total: snap.skills.length,
@@ -51748,8 +52144,8 @@ function listJsonFiles(dir) {
51748
52144
  }
51749
52145
  function findAgentsMd(cwd) {
51750
52146
  const candidates = [
51751
- path62.join(cwd, "AGENTS.md"),
51752
- path62.join(cwd, ".zelari", "AGENTS.md")
52147
+ path63.join(cwd, "AGENTS.md"),
52148
+ path63.join(cwd, ".zelari", "AGENTS.md")
51753
52149
  ];
51754
52150
  const found = [];
51755
52151
  for (const c of candidates) {
@@ -55024,8 +55420,76 @@ init_completionGate();
55024
55420
  // src/cli/kraken/verificationBridge.ts
55025
55421
  init_candidateRegistry();
55026
55422
  init_completionGate();
55423
+
55424
+ // src/cli/kraken/nativeVerification.ts
55425
+ init_runtime2();
55426
+ init_verification2();
55427
+ import { readFile as readFile2 } from "node:fs/promises";
55428
+ import path40 from "node:path";
55429
+ function nativePackEnabled(env = process.env) {
55430
+ const v = env.ZELARI_VERIFY_PACK?.toLowerCase();
55431
+ return v === "1" || v === "on" || v === "true";
55432
+ }
55433
+ function resolvePackCommands(env, scripts) {
55434
+ const pick2 = (override, scriptName) => {
55435
+ if (override !== void 0) {
55436
+ const trimmed = override.trim();
55437
+ return trimmed === "" ? null : trimmed;
55438
+ }
55439
+ const script = scripts[scriptName];
55440
+ return typeof script === "string" && script.length > 0 ? `npm run ${scriptName}` : null;
55441
+ };
55442
+ return {
55443
+ typecheckCommand: pick2(env.ZELARI_VERIFY_TYPECHECK_CMD, "typecheck"),
55444
+ testCommand: pick2(env.ZELARI_VERIFY_TEST_CMD, "test"),
55445
+ buildCommand: pick2(env.ZELARI_VERIFY_BUILD_CMD, "build")
55446
+ };
55447
+ }
55448
+ function packTimeoutMs(env = process.env) {
55449
+ const raw = Number(env.ZELARI_VERIFY_TIMEOUT_MS);
55450
+ if (!Number.isFinite(raw) || raw <= 0) return 6e5;
55451
+ return Math.min(Math.floor(raw), 36e5);
55452
+ }
55453
+ async function readPackageScripts(cwd = process.cwd()) {
55454
+ try {
55455
+ const raw = await readFile2(path40.join(cwd, "package.json"), "utf-8");
55456
+ const parsed = JSON.parse(raw);
55457
+ if (parsed && typeof parsed === "object" && typeof parsed.scripts === "object") {
55458
+ return parsed.scripts;
55459
+ }
55460
+ return {};
55461
+ } catch {
55462
+ return {};
55463
+ }
55464
+ }
55465
+ function buildNativeCriteria(commands, timeoutMs) {
55466
+ const pack = codingCriteriaPack({ ...commands, commandTimeoutMs: timeoutMs });
55467
+ return pack.criteria.filter(
55468
+ (c) => !(c.required && (!c.check || c.check.kind === "none"))
55469
+ );
55470
+ }
55471
+ async function evaluateNativePack(deps = {}) {
55472
+ const env = deps.env ?? process.env;
55473
+ if (!nativePackEnabled(env)) return null;
55474
+ const cwd = deps.cwd ?? process.cwd();
55475
+ const commands = resolvePackCommands(env, await readPackageScripts(cwd));
55476
+ if (!commands.typecheckCommand && !commands.testCommand && !commands.buildCommand) return null;
55477
+ const criteria = buildNativeCriteria(commands, packTimeoutMs(env));
55478
+ if (criteria.length === 0) return null;
55479
+ const shell = deps.shell ?? new NodeShellProvider(new LocalWorkspace(cwd));
55480
+ const engine = deps.emit ? new VerificationEngine({ shell }, { emit: deps.emit }) : new VerificationEngine({ shell });
55481
+ const results = await engine.evaluate(criteria, { packId: ZELARI_CODING_PACK_ID });
55482
+ return { packId: ZELARI_CODING_PACK_ID, criteria, results };
55483
+ }
55484
+
55485
+ // src/cli/kraken/verificationBridge.ts
55027
55486
  init_verification2();
55028
- function strictDoneEnabled() {
55487
+ function strictDoneEnabled(surface = "kraken") {
55488
+ if (surface === "mission") {
55489
+ const v2 = process.env.ZELARI_MISSION_STRICT;
55490
+ if (v2 === "0" || v2 === "false") return false;
55491
+ return true;
55492
+ }
55029
55493
  const v = process.env.ZELARI_STRICT_DONE;
55030
55494
  return v === "1" || v === "true";
55031
55495
  }
@@ -55081,32 +55545,71 @@ function krakenResultsToContract(requiredChecks, results, now = Date.now()) {
55081
55545
  });
55082
55546
  return { criteria, results: verifications };
55083
55547
  }
55084
- function evaluateStrictBuildGate(mode) {
55548
+ async function anchorSelectionEvidence(results, emit) {
55549
+ if (!emit) return;
55550
+ for (const r of results) {
55551
+ for (const ev of r.evidence) {
55552
+ if (ev.seq !== void 0) continue;
55553
+ if (ev.tier === "verifier-llm" || ev.tier === "human") continue;
55554
+ try {
55555
+ const appended = await emit({
55556
+ kind: "verification.evidence",
55557
+ actor: { type: "system", role: "verification" },
55558
+ data: {
55559
+ observation: "verify-report-note",
55560
+ criterionId: r.criterionId,
55561
+ ref: ev.ref,
55562
+ tier: ev.tier
55563
+ }
55564
+ });
55565
+ const seq = appended && typeof appended === "object" && "seq" in appended ? Number(appended.seq) : NaN;
55566
+ if (Number.isFinite(seq) && seq > 0) ev.seq = seq;
55567
+ } catch {
55568
+ }
55569
+ }
55570
+ }
55571
+ }
55572
+ async function evaluateStrictBuildGate(mode, options = {}) {
55085
55573
  const gate = evaluateKrakenCompletionGate(mode);
55086
- if (!gate.selectionUsed || gate.total === 0 || !strictDoneEnabled()) {
55574
+ if (!gate.selectionUsed || gate.total === 0 || !strictDoneEnabled(options.surface ?? "kraken")) {
55087
55575
  return {
55088
55576
  gate,
55089
55577
  strict: false,
55090
55578
  evaluation: null,
55579
+ native: null,
55091
55580
  blocked: gate.blocked,
55092
55581
  summary: gate.blocked ? `blocked: ${gate.failedChecks.length} failed, ${gate.unknownChecks.length} unknown` : "open"
55093
55582
  };
55094
55583
  }
55095
55584
  const checks = krakenRequiredChecks();
55096
55585
  const contract = krakenResultsToContract(checks, getKrakenCheckResults());
55097
- const evaluation = evaluateCompletion(contract.criteria, contract.results, STRICT_ALL_POLICY);
55586
+ await anchorSelectionEvidence(contract.results, options.emit);
55587
+ const native = await evaluateNativePack({
55588
+ cwd: options.cwd,
55589
+ env: options.env,
55590
+ shell: options.shell,
55591
+ emit: options.emit
55592
+ }).catch(() => null);
55593
+ const allCriteria = [...contract.criteria, ...native?.criteria ?? []];
55594
+ const allResults = [...contract.results, ...native?.results ?? []];
55595
+ const evaluation = evaluateCompletion(allCriteria, allResults, STRICT_BUILD_POLICY);
55098
55596
  const blocked = gate.blocked || evaluation.verdict !== "PASS";
55099
55597
  return {
55100
55598
  gate,
55101
55599
  strict: true,
55102
55600
  evaluation,
55601
+ native,
55103
55602
  blocked,
55104
- summary: blocked ? `blocked (strict ${evaluation?.verdict ?? "n/a"}): ${gate.passed}/${gate.total} legacy-pass, evidence ${evaluation?.evidenceComplete ? "complete" : "incomplete"}` : `open (strict PASS): ${evaluation?.satisfied.length ?? 0}/${gate.total} criteria pass with evidence`
55603
+ summary: blocked ? `blocked (strict ${evaluation?.verdict ?? "n/a"}): ${gate.passed}/${gate.total} legacy-pass, evidence ${evaluation?.evidenceComplete ? "complete" : "incomplete"}` : `open (strict PASS): ${evaluation?.satisfied.length ?? 0}/${allCriteria.length} criteria pass with evidence`
55105
55604
  };
55106
55605
  }
55606
+ var STRICT_DONE_EXIT_CODE = 4;
55607
+ function strictGateExitCode(evaluation) {
55608
+ return evaluation.strict && evaluation.blocked ? STRICT_DONE_EXIT_CODE : 0;
55609
+ }
55107
55610
  function strictGateEventPayload(evaluation) {
55108
55611
  return {
55109
- engine: "kraken-legacy+completion-policy",
55612
+ engine: evaluation.native ? "kraken-legacy+completion-policy+criteria-pack" : "kraken-legacy+completion-policy",
55110
55613
  strict: evaluation.strict,
55111
55614
  verdict: evaluation.evaluation?.verdict ?? (evaluation.blocked ? "BLOCKED" : "PASS"),
55112
55615
  legacy: {
@@ -55120,6 +55623,17 @@ function strictGateEventPayload(evaluation) {
55120
55623
  unsatisfied: evaluation.evaluation.unsatisfied,
55121
55624
  complete: evaluation.evaluation.evidenceComplete
55122
55625
  } : null,
55626
+ // F2: deterministic pack results — real command evidence, replayable.
55627
+ native: evaluation.native ? {
55628
+ packId: evaluation.native.packId,
55629
+ criteria: evaluation.native.criteria.map((c) => ({ id: c.id, required: c.required })),
55630
+ results: evaluation.native.results.map((r) => ({
55631
+ criterionId: r.criterionId,
55632
+ status: r.status,
55633
+ evidence: r.evidence.map((e) => ({ tier: e.tier, ref: e.ref, digest: e.digest, ...e.seq !== void 0 ? { seq: e.seq } : {} })),
55634
+ detail: r.detail
55635
+ }))
55636
+ } : null,
55123
55637
  summary: evaluation.summary
55124
55638
  };
55125
55639
  }
@@ -55631,7 +56145,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
55631
56145
  });
55632
56146
  }
55633
56147
  const cwd = process.cwd();
55634
- const budget = await applyBudgetPolicyAsync(getHistory(), getPhase(), {
56148
+ const budget = await applyBudgetPolicyAsync(historyForModel, getPhase(), {
55635
56149
  model: getActiveModel(),
55636
56150
  sessionId: sessionId2,
55637
56151
  // v1.36.0: envelope for full-request metering + cache-aware
@@ -55657,6 +56171,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
55657
56171
  });
55658
56172
  void writerRef.current?.append(compactionEvent);
55659
56173
  }
56174
+ historyForModel = budget.history;
55660
56175
  historySeedLen = historyForModel.length;
55661
56176
  let composedWorkspace = "";
55662
56177
  let composedInstructions = "";
@@ -55873,8 +56388,9 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
55873
56388
  progressRuntime.observe(event);
55874
56389
  if (event.type === "agent_end") {
55875
56390
  let krakenSuppressFinish = false;
56391
+ const krakenSpineEmit = (input) => writerRef.current?.spine?.appendEvent(input) ?? Promise.resolve(null);
55876
56392
  if (event.reason === "completed" && !krakenRepairEnqueued && isKrakenSelectionEnabled() && workPhase === "build") {
55877
- const strictGate = evaluateStrictBuildGate("build");
56393
+ const strictGate = await evaluateStrictBuildGate("build", { emit: krakenSpineEmit });
55878
56394
  const krakenGate = strictGate.gate;
55879
56395
  writerRef.current?.spine?.verificationRun(strictGateEventPayload(strictGate));
55880
56396
  if (krakenGate.blocked) {
@@ -55890,8 +56406,17 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
55890
56406
  krakenSuppressFinish = true;
55891
56407
  }
55892
56408
  }
55893
- if (event.reason === "completed" && krakenRepairEnqueued && !evaluateStrictBuildGate("build").blocked) {
56409
+ if (event.reason === "completed" && krakenRepairEnqueued && !(await evaluateStrictBuildGate("build", { emit: krakenSpineEmit })).blocked) {
55894
56410
  markRepairSucceeded();
56411
+ } else if (event.reason === "completed" && krakenRepairEnqueued) {
56412
+ const still = await evaluateStrictBuildGate("build", { emit: krakenSpineEmit });
56413
+ if (still.blocked) {
56414
+ appendSystem(
56415
+ setMessages,
56416
+ `[kraken] strict done: evidence still incomplete after repair (${still.evaluation?.unsatisfied.length ?? "?"} unresolved) \u2014 turn is NOT verified-complete`,
56417
+ Date.now()
56418
+ );
56419
+ }
55895
56420
  }
55896
56421
  if (!krakenSuppressFinish) progressRuntime.finish(event.reason);
55897
56422
  }
@@ -56285,14 +56810,31 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
56285
56810
  return { completionOk: false, ran: false };
56286
56811
  }
56287
56812
  setBusy(true);
56288
- compactInPlace();
56289
- const councilBudget = await applyBudgetPolicyAsync(getHistory(), getPhase(), {
56813
+ let councilHistory = getHistory();
56814
+ {
56815
+ const mirror = writerRef.current?.spine ?? null;
56816
+ if (mirror && mirror.status === "active") {
56817
+ const derived = await mirror.derivedPriorTurns();
56818
+ if (derived && derived.length > 0) {
56819
+ councilHistory = derivedModelSeed(derived);
56820
+ }
56821
+ }
56822
+ }
56823
+ const councilBudget = await applyBudgetPolicyAsync(councilHistory, getPhase(), {
56290
56824
  model: envConfig.model
56291
56825
  });
56292
56826
  setHistory(councilBudget.history);
56293
56827
  for (const w of councilBudget.warnings) {
56294
56828
  appendSystem(setMessages, w, Date.now());
56295
56829
  }
56830
+ if ((councilBudget.messagesRemoved ?? 0) > 0) {
56831
+ void writerRef.current?.append(
56832
+ createBrainEvent("session_compacted", sessionId2, {
56833
+ summary: councilBudget.compactSummary ?? "",
56834
+ messagesRemoved: councilBudget.messagesRemoved ?? 0
56835
+ })
56836
+ );
56837
+ }
56296
56838
  const anchored = maybeAnchorShortAnswer(text);
56297
56839
  const effectiveText = anchored ?? text;
56298
56840
  appendSystem(
@@ -57894,7 +58436,7 @@ init_sessionManager();
57894
58436
  // src/cli/gitOps.ts
57895
58437
  import { execFile as execFile4 } from "node:child_process";
57896
58438
  import { promisify as promisify3 } from "node:util";
57897
- import path47 from "node:path";
58439
+ import path48 from "node:path";
57898
58440
  var execFileAsync3 = promisify3(execFile4);
57899
58441
  async function git4(cwd, args) {
57900
58442
  try {
@@ -57940,7 +58482,7 @@ async function undoWorkingChanges(opts = {}) {
57940
58482
  };
57941
58483
  }
57942
58484
  function defaultProjectRoot() {
57943
- return path47.resolve(__dirname, "..", "..", "..");
58485
+ return path48.resolve(__dirname, "..", "..", "..");
57944
58486
  }
57945
58487
 
57946
58488
  // src/cli/slashHandlers/git.ts
@@ -58392,7 +58934,7 @@ import { promises as fs29 } from "node:fs";
58392
58934
  init_zod();
58393
58935
  init_taskTool();
58394
58936
  import { promises as fs28 } from "node:fs";
58395
- import path51 from "node:path";
58937
+ import path52 from "node:path";
58396
58938
  import { randomBytes as randomBytes4 } from "node:crypto";
58397
58939
  var CsvFanoutArgsSchema = external_exports.object({
58398
58940
  csv_path: external_exports.string().min(1),
@@ -58486,8 +59028,8 @@ function resolveMaxConcurrency(env = process.env) {
58486
59028
  }
58487
59029
  async function runCsvFanout(args, deps, opts) {
58488
59030
  const start = Date.now();
58489
- const absCsv = path51.isAbsolute(args.csv_path) ? args.csv_path : path51.join(opts.parentCwd, args.csv_path);
58490
- const absOut = path51.isAbsolute(args.output_csv_path) ? args.output_csv_path : path51.join(opts.parentCwd, args.output_csv_path);
59031
+ const absCsv = path52.isAbsolute(args.csv_path) ? args.csv_path : path52.join(opts.parentCwd, args.csv_path);
59032
+ const absOut = path52.isAbsolute(args.output_csv_path) ? args.output_csv_path : path52.join(opts.parentCwd, args.output_csv_path);
58491
59033
  const { headers: headers2, rows } = await readCsv(absCsv);
58492
59034
  if (headers2.length === 0) {
58493
59035
  throw new Error(`kraken_csv_fanout: ${absCsv} is empty`);
@@ -58543,7 +59085,7 @@ async function runCsvFanout(args, deps, opts) {
58543
59085
  errored += 1;
58544
59086
  errors.push(`${row[args.id_column] ?? i}: ${res.error}`);
58545
59087
  }
58546
- await fs28.mkdir(path51.dirname(absOut), { recursive: true });
59088
+ await fs28.mkdir(path52.dirname(absOut), { recursive: true });
58547
59089
  await queueWrite(serializeCsv(outHeaders, outputRecords));
58548
59090
  }
58549
59091
  }
@@ -58738,7 +59280,7 @@ function splitArgs(s) {
58738
59280
  // src/cli/slashHandlers/krakenWorkbench.ts
58739
59281
  init_messageHelpers();
58740
59282
  import { promises as fs30 } from "node:fs";
58741
- import path52 from "node:path";
59283
+ import path53 from "node:path";
58742
59284
 
58743
59285
  // src/cli/kraken/workbenchView.ts
58744
59286
  var EMPTY = {
@@ -58855,14 +59397,14 @@ function formatWorkbenchForTerminal(p3) {
58855
59397
 
58856
59398
  // src/cli/slashHandlers/krakenWorkbench.ts
58857
59399
  async function handleKrakenWorkbench(ctx) {
58858
- const dir = path52.join(ctx.cwd, ".zelari", "radio");
59400
+ const dir = path53.join(ctx.cwd, ".zelari", "radio");
58859
59401
  let latest = null;
58860
59402
  let latestMtime = 0;
58861
59403
  try {
58862
59404
  const files = await fs30.readdir(dir);
58863
59405
  for (const f of files) {
58864
59406
  if (!f.startsWith("workbench-") || !f.endsWith(".md")) continue;
58865
- const full = path52.join(dir, f);
59407
+ const full = path53.join(dir, f);
58866
59408
  const stat = await fs30.stat(full);
58867
59409
  if (stat.mtimeMs > latestMtime) {
58868
59410
  latestMtime = stat.mtimeMs;
@@ -58879,10 +59421,10 @@ async function handleKrakenWorkbench(ctx) {
58879
59421
  const parsed = parseWorkbench(content);
58880
59422
  const rendered = formatWorkbenchForTerminal(parsed);
58881
59423
  if (!rendered.trim()) {
58882
- appendSystem(ctx.setMessages, `[kraken workbench] ${path52.basename(latest)}: (no nodes / no events yet)`);
59424
+ appendSystem(ctx.setMessages, `[kraken workbench] ${path53.basename(latest)}: (no nodes / no events yet)`);
58883
59425
  return;
58884
59426
  }
58885
- appendSystem(ctx.setMessages, `[kraken workbench] ${path52.basename(latest)}:
59427
+ appendSystem(ctx.setMessages, `[kraken workbench] ${path53.basename(latest)}:
58886
59428
  ${rendered}`);
58887
59429
  }
58888
59430
 
@@ -59189,15 +59731,15 @@ ${result.output.split("\n").slice(-8).join("\n")}` : "";
59189
59731
  // src/cli/slashHandlers/promoteMember.ts
59190
59732
  init_messageHelpers();
59191
59733
  import { promises as fs31 } from "node:fs";
59192
- import path55 from "node:path";
59734
+ import path56 from "node:path";
59193
59735
  import os12 from "node:os";
59194
59736
  async function handlePromoteMember(ctx, memberId) {
59195
59737
  try {
59196
59738
  const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
59197
59739
  const { skill, markdown } = promoteMember2(memberId);
59198
- const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path55.join(os12.homedir(), ".tmp", "zelari-code", "skills");
59740
+ const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path56.join(os12.homedir(), ".tmp", "zelari-code", "skills");
59199
59741
  await fs31.mkdir(skillDir, { recursive: true });
59200
- const filePath = path55.join(skillDir, `${skill.id}.md`);
59742
+ const filePath = path56.join(skillDir, `${skill.id}.md`);
59201
59743
  await fs31.writeFile(filePath, markdown, "utf8");
59202
59744
  appendSystem(
59203
59745
  ctx.setMessages,
@@ -59215,24 +59757,24 @@ async function handlePromoteMember(ctx, memberId) {
59215
59757
 
59216
59758
  // src/cli/branchManager.ts
59217
59759
  import { promises as fs32, existsSync as existsSync44, readFileSync as readFileSync36, writeFileSync as writeFileSync23, mkdirSync as mkdirSync19, statSync as statSync7, rmSync as rmSync3 } from "node:fs";
59218
- import path56 from "node:path";
59760
+ import path57 from "node:path";
59219
59761
  import os13 from "node:os";
59220
59762
  var META_FILENAME = "meta.json";
59221
59763
  var SESSIONS_SUBDIR = "sessions";
59222
59764
  function getBranchesBaseDir() {
59223
- return process.env.ANATHEMA_BRANCHES_DIR ?? path56.join(os13.homedir(), ".tmp", "zelari-code", "branches");
59765
+ return process.env.ANATHEMA_BRANCHES_DIR ?? path57.join(os13.homedir(), ".tmp", "zelari-code", "branches");
59224
59766
  }
59225
59767
  function getSessionsBaseDir() {
59226
- return process.env.ANATHEMA_SESSIONS_DIR ?? path56.join(os13.homedir(), ".tmp", "zelari-code", "sessions");
59768
+ return process.env.ANATHEMA_SESSIONS_DIR ?? path57.join(os13.homedir(), ".tmp", "zelari-code", "sessions");
59227
59769
  }
59228
59770
  function branchPathFor(name, baseDir) {
59229
- return path56.join(baseDir, name);
59771
+ return path57.join(baseDir, name);
59230
59772
  }
59231
59773
  function metaPathFor(name, baseDir) {
59232
- return path56.join(baseDir, name, META_FILENAME);
59774
+ return path57.join(baseDir, name, META_FILENAME);
59233
59775
  }
59234
59776
  function sessionsPathFor(name, baseDir) {
59235
- return path56.join(baseDir, name, SESSIONS_SUBDIR);
59777
+ return path57.join(baseDir, name, SESSIONS_SUBDIR);
59236
59778
  }
59237
59779
  function readBranchMeta(name, baseDir) {
59238
59780
  const metaPath = metaPathFor(name, baseDir);
@@ -59257,7 +59799,7 @@ function readBranchMeta(name, baseDir) {
59257
59799
  }
59258
59800
  function writeBranchMeta(name, baseDir, meta3) {
59259
59801
  const metaPath = metaPathFor(name, baseDir);
59260
- mkdirSync19(path56.dirname(metaPath), { recursive: true });
59802
+ mkdirSync19(path57.dirname(metaPath), { recursive: true });
59261
59803
  writeFileSync23(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
59262
59804
  }
59263
59805
  async function countSessions(name, baseDir) {
@@ -59308,14 +59850,14 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
59308
59850
  if (branchExists(name, baseDir)) {
59309
59851
  throw new BranchAlreadyExistsError(name);
59310
59852
  }
59311
- const sourcePath = path56.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
59853
+ const sourcePath = path57.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
59312
59854
  if (!existsSync44(sourcePath)) {
59313
59855
  throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
59314
59856
  }
59315
59857
  const branchPath = branchPathFor(name, baseDir);
59316
59858
  const branchSessionsPath = sessionsPathFor(name, baseDir);
59317
59859
  mkdirSync19(branchSessionsPath, { recursive: true });
59318
- const destPath = path56.join(branchSessionsPath, `${fromSessionId}.jsonl`);
59860
+ const destPath = path57.join(branchSessionsPath, `${fromSessionId}.jsonl`);
59319
59861
  await fs32.copyFile(sourcePath, destPath);
59320
59862
  const meta3 = {
59321
59863
  name,
@@ -59419,14 +59961,14 @@ async function handleBranchCheckout(ctx, branchName) {
59419
59961
  // src/cli/slashHandlers/workspace.ts
59420
59962
  init_messageHelpers();
59421
59963
  import { promises as fs33 } from "node:fs";
59422
- import path57 from "node:path";
59964
+ import path58 from "node:path";
59423
59965
  async function handleWorkspaceShow(ctx, what) {
59424
59966
  try {
59425
- const zelari = path57.join(process.cwd(), ".zelari");
59967
+ const zelari = path58.join(process.cwd(), ".zelari");
59426
59968
  let content;
59427
59969
  switch (what) {
59428
59970
  case "plan": {
59429
- const planPath = path57.join(zelari, "plan.md");
59971
+ const planPath = path58.join(zelari, "plan.md");
59430
59972
  try {
59431
59973
  content = await fs33.readFile(planPath, "utf-8");
59432
59974
  } catch {
@@ -59435,7 +59977,7 @@ async function handleWorkspaceShow(ctx, what) {
59435
59977
  break;
59436
59978
  }
59437
59979
  case "decisions": {
59438
- const decisionsDir = path57.join(zelari, "decisions");
59980
+ const decisionsDir = path58.join(zelari, "decisions");
59439
59981
  try {
59440
59982
  const files = (await fs33.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
59441
59983
  if (files.length === 0) {
@@ -59445,7 +59987,7 @@ async function handleWorkspaceShow(ctx, what) {
59445
59987
  `];
59446
59988
  const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
59447
59989
  for (const f of files) {
59448
- const raw = await fs33.readFile(path57.join(decisionsDir, f), "utf-8");
59990
+ const raw = await fs33.readFile(path58.join(decisionsDir, f), "utf-8");
59449
59991
  const { meta: meta3, body } = parseFrontmatter2(raw);
59450
59992
  const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
59451
59993
  lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
@@ -59458,7 +60000,7 @@ async function handleWorkspaceShow(ctx, what) {
59458
60000
  break;
59459
60001
  }
59460
60002
  case "risks": {
59461
- const risksPath = path57.join(zelari, "risks.md");
60003
+ const risksPath = path58.join(zelari, "risks.md");
59462
60004
  try {
59463
60005
  content = await fs33.readFile(risksPath, "utf-8");
59464
60006
  } catch {
@@ -59467,7 +60009,7 @@ async function handleWorkspaceShow(ctx, what) {
59467
60009
  break;
59468
60010
  }
59469
60011
  case "agents": {
59470
- const agentsPath = path57.join(process.cwd(), "AGENTS.MD");
60012
+ const agentsPath = path58.join(process.cwd(), "AGENTS.MD");
59471
60013
  try {
59472
60014
  content = await fs33.readFile(agentsPath, "utf-8");
59473
60015
  } catch {
@@ -59476,7 +60018,7 @@ async function handleWorkspaceShow(ctx, what) {
59476
60018
  break;
59477
60019
  }
59478
60020
  case "docs": {
59479
- const docsDir = path57.join(zelari, "docs");
60021
+ const docsDir = path58.join(zelari, "docs");
59480
60022
  try {
59481
60023
  const files = (await fs33.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
59482
60024
  content = files.length ? `# Docs (${files.length})
@@ -59518,7 +60060,7 @@ async function handleWorkspaceReset(ctx, force) {
59518
60060
  return;
59519
60061
  }
59520
60062
  try {
59521
- const target = path57.join(process.cwd(), ".zelari");
60063
+ const target = path58.join(process.cwd(), ".zelari");
59522
60064
  await fs33.rm(target, { recursive: true, force: true });
59523
60065
  appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
59524
60066
  } catch (err) {
@@ -59530,7 +60072,7 @@ async function handleWorkspaceReset(ctx, force) {
59530
60072
  init_provider2();
59531
60073
 
59532
60074
  // src/cli/slashHandlers/skills.ts
59533
- import path58 from "node:path";
60075
+ import path59 from "node:path";
59534
60076
  import os14 from "node:os";
59535
60077
 
59536
60078
  // src/cli/skillHistory.ts
@@ -59659,7 +60201,7 @@ function handleSkillPicker(ctx, skills, openPicker, fallbackMessage) {
59659
60201
  });
59660
60202
  }
59661
60203
  async function handleSkillStats(ctx, skillId) {
59662
- const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path58.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
60204
+ const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path59.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
59663
60205
  try {
59664
60206
  const records = await readSkillHistory(historyFile);
59665
60207
  const stats = getSkillStats(records, skillId);
@@ -59675,7 +60217,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
59675
60217
  appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
59676
60218
  return;
59677
60219
  }
59678
- const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path58.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
60220
+ const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path59.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
59679
60221
  try {
59680
60222
  const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
59681
60223
  appendSystem(ctx.setMessages, formatted);
@@ -61219,7 +61761,7 @@ function createStreamScrubber2() {
61219
61761
  init_taskTool();
61220
61762
  init_sessionTodos();
61221
61763
  import { promises as fs36 } from "node:fs";
61222
- import path60 from "node:path";
61764
+ import path61 from "node:path";
61223
61765
  import { randomUUID as randomUUID6 } from "node:crypto";
61224
61766
  init_headlessSpine();
61225
61767
  async function runHeadless(opts) {
@@ -61376,7 +61918,7 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
61376
61918
  try {
61377
61919
  let preflightGraph;
61378
61920
  if (opts.runPlan && opts.runPlan.trim() !== "") {
61379
- const planPath = path60.join(cwd, ".zelari", "radio", `plan-${opts.runPlan}.json`);
61921
+ const planPath = path61.join(cwd, ".zelari", "radio", `plan-${opts.runPlan}.json`);
61380
61922
  log(`loading pre-flight plan: ${planPath}`);
61381
61923
  let raw;
61382
61924
  try {
@@ -61416,8 +61958,8 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
61416
61958
  log(formatKrakenGraphAscii2(graph));
61417
61959
  if (opts.planOnly) {
61418
61960
  const planId = randomUUID6();
61419
- const planDir = path60.join(cwd, ".zelari", "radio");
61420
- const planPath = path60.join(planDir, `plan-${planId}.json`);
61961
+ const planDir = path61.join(cwd, ".zelari", "radio");
61962
+ const planPath = path61.join(planDir, `plan-${planId}.json`);
61421
61963
  await fs36.mkdir(planDir, { recursive: true });
61422
61964
  await fs36.writeFile(
61423
61965
  planPath,
@@ -61550,6 +62092,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
61550
62092
  workspace: process.cwd()
61551
62093
  });
61552
62094
  const seededHistory = await seedHeadlessModelHistory(spine, opts.history);
62095
+ emitEvent(sessionStartedEvent(spine));
61553
62096
  if (opts.task) spine.userMessage(opts.task);
61554
62097
  resetKrakenCandidates();
61555
62098
  resetKrakenTurnMetrics();
@@ -61862,8 +62405,9 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
61862
62405
  emittedWrites: pass.emittedWrites + retry.emittedWrites
61863
62406
  };
61864
62407
  }
62408
+ let strictExit = 0;
61865
62409
  if (pass.finalReason === "completed" && pass.exitCode === 0 && opts.mode === "kraken" && isKrakenSelectionEnabled() && !planModeFromOpts(opts)) {
61866
- const strictGate = evaluateStrictBuildGate("build");
62410
+ const strictGate = await evaluateStrictBuildGate("build", { emit: (input) => spine.appendEvent(input) });
61867
62411
  const gate = strictGate.gate;
61868
62412
  const verificationPayload = strictGateEventPayload(strictGate);
61869
62413
  spine.verificationRun(verificationPayload);
@@ -61896,13 +62440,20 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
61896
62440
  successfulWrites: pass.successfulWrites + repair.successfulWrites,
61897
62441
  emittedWrites: pass.emittedWrites + repair.emittedWrites
61898
62442
  };
61899
- const after = evaluateStrictBuildGate("build");
62443
+ const after = await evaluateStrictBuildGate("build", { emit: (input) => spine.appendEvent(input) });
61900
62444
  const afterPayload = strictGateEventPayload(after);
61901
62445
  spine.verificationRun(afterPayload);
61902
62446
  if (opts.output === "json") {
61903
62447
  emitEvent({ type: "verification_run", ...afterPayload });
61904
62448
  }
61905
62449
  if (!after.blocked) markRepairSucceeded();
62450
+ else {
62451
+ strictExit = strictGateExitCode(after);
62452
+ const gateMsg = `[headless] Kraken BUILD: strict completion gate still blocked after repair pass \u2014 closing non-success (exit ${strictExit}): ${after.summary}`;
62453
+ if (opts.output === "json") emitEvent({ type: "log", message: gateMsg });
62454
+ else process.stderr.write(`[zelari-code --headless] ${gateMsg}
62455
+ `);
62456
+ }
61906
62457
  }
61907
62458
  }
61908
62459
  progressRuntime.finish(pass.finalReason);
@@ -61951,7 +62502,8 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
61951
62502
  }
61952
62503
  }
61953
62504
  try {
61954
- await spine.close(pass.finalReason === "error" ? "error" : "completed");
62505
+ const closeStatus = pass.finalReason === "error" ? "error" : strictExit !== 0 ? "stopped" : "completed";
62506
+ await spine.close(closeStatus);
61955
62507
  } catch {
61956
62508
  }
61957
62509
  if (opts.exportSessionPath) {
@@ -61960,7 +62512,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
61960
62512
  if (json2) {
61961
62513
  if (opts.exportSessionPath === "-") process.stdout.write(json2 + "\n");
61962
62514
  else {
61963
- await fs36.mkdir(path60.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
62515
+ await fs36.mkdir(path61.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
61964
62516
  await fs36.writeFile(opts.exportSessionPath, json2, "utf8");
61965
62517
  }
61966
62518
  }
@@ -61968,6 +62520,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
61968
62520
  }
61969
62521
  }
61970
62522
  if (pass.finalReason === "error") return 3;
62523
+ if (strictExit !== 0) return strictExit;
61971
62524
  return pass.exitCode;
61972
62525
  }
61973
62526
  async function buildCouncilToolRegistry(planMode, opts) {
@@ -62007,6 +62560,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
62007
62560
  workspace: process.cwd()
62008
62561
  });
62009
62562
  const seededHistory = await seedHeadlessModelHistory(spine, opts.history);
62563
+ emitEvent(sessionStartedEvent(spine));
62010
62564
  if (opts.task) spine.userMessage(opts.task);
62011
62565
  const { shouldAllowCouncilBuild: shouldAllowCouncilBuild2 } = await Promise.resolve().then(() => (init_buildPolicy(), buildPolicy_exports));
62012
62566
  let councilRunMode = planModeFromOpts(opts) ? "design-phase" : "implementation";
@@ -62125,7 +62679,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
62125
62679
  if (json2) {
62126
62680
  if (opts.exportSessionPath === "-") process.stdout.write(json2 + "\n");
62127
62681
  else {
62128
- await fs36.mkdir(path60.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
62682
+ await fs36.mkdir(path61.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
62129
62683
  await fs36.writeFile(opts.exportSessionPath, json2, "utf8");
62130
62684
  }
62131
62685
  }
@@ -62144,6 +62698,7 @@ async function runHeadlessZelari(opts, provider, model, providerStream) {
62144
62698
  workspace: projectRoot
62145
62699
  });
62146
62700
  const seededHistory = await seedHeadlessModelHistory(spine, opts.history);
62701
+ emitEvent(sessionStartedEvent(spine));
62147
62702
  if (opts.task) spine.userMessage(opts.task);
62148
62703
  spine.missionPhase("design", "mission-start");
62149
62704
  const { buildMissionBrief: buildMissionBrief2 } = await Promise.resolve().then(() => (init_council(), council_exports));
@@ -62210,6 +62765,13 @@ ${JSON.stringify({ deliverable: brief.deliverableThisMission, mvp: brief.sliceMv
62210
62765
  emit,
62211
62766
  buildViaAgent,
62212
62767
  onMissionPhase: (phase2, note) => spine.missionPhase(phase2, note),
62768
+ onMissionProgress: (advice, iteration) => spine.missionProgress({
62769
+ recommendation: advice.recommendation,
62770
+ rationale: advice.rationale,
62771
+ blockers: advice.blockers,
62772
+ ...advice.trend ? { trend: advice.trend } : {},
62773
+ iteration
62774
+ }),
62213
62775
  runSlice: async ({
62214
62776
  userMessage: slicePrompt,
62215
62777
  runMode,
@@ -62414,8 +62976,22 @@ ${ragContext}` : slicePrompt;
62414
62976
  });
62415
62977
  if (state3.status === "error") exitCode = exitCode || 3;
62416
62978
  else if (state3.status === "success") {
62417
- exitCode = 0;
62418
- spine.missionPhase("done", "mission-success");
62979
+ const missionGate = await evaluateStrictBuildGate("build", {
62980
+ emit: (input) => spine.appendEvent(input),
62981
+ surface: "mission"
62982
+ });
62983
+ const missionVerificationPayload = strictGateEventPayload(missionGate);
62984
+ spine.verificationRun(missionVerificationPayload);
62985
+ if (opts.output === "json") {
62986
+ emitEvent({ type: "verification_run", ...missionVerificationPayload });
62987
+ }
62988
+ if (missionGate.blocked) {
62989
+ exitCode = strictGateExitCode(missionGate);
62990
+ spine.missionPhase("verification", "mission-strict-blocked");
62991
+ } else {
62992
+ exitCode = 0;
62993
+ spine.missionPhase("done", "mission-success");
62994
+ }
62419
62995
  } else if (state3.status === "stalled" || state3.status === "stopped") {
62420
62996
  exitCode = exitCode || 0;
62421
62997
  spine.missionPhase("verification", `mission-${state3.status}`);
@@ -62443,7 +63019,7 @@ ${ragContext}` : slicePrompt;
62443
63019
  if (json2) {
62444
63020
  if (opts.exportSessionPath === "-") process.stdout.write(json2 + "\n");
62445
63021
  else {
62446
- await fs36.mkdir(path60.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
63022
+ await fs36.mkdir(path61.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
62447
63023
  await fs36.writeFile(opts.exportSessionPath, json2, "utf8");
62448
63024
  }
62449
63025
  }
@@ -62657,8 +63233,8 @@ function normalizeDraft(raw, sourceUrl, provider, model) {
62657
63233
  let name = String(o.name ?? "").trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
62658
63234
  if (!name || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
62659
63235
  try {
62660
- const path63 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
62661
- name = path63 && /^[a-z0-9]/.test(path63) ? path63 : "imported-skill";
63236
+ const path64 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
63237
+ name = path64 && /^[a-z0-9]/.test(path64) ? path64 : "imported-skill";
62662
63238
  } catch {
62663
63239
  name = "imported-skill";
62664
63240
  }