@gobing-ai/spur 0.4.12 → 0.4.14

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 (63) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/rules/boundary/config-loading-ownership.yaml +1 -1
  3. package/config/rules/strict/runtime-boundaries.yaml +2 -2
  4. package/config/rules/typescript/output-boundaries.yaml +4 -10
  5. package/config/workflows/task-pipeline.yaml +2 -2
  6. package/package.json +1 -1
  7. package/plugins/sp/commands/dev-run.md +1 -1
  8. package/plugins/sp/plugin.json +1 -1
  9. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +3 -3
  10. package/plugins/sp/skills/spur-dev/references/dev-operations.md +4 -4
  11. package/plugins/sp/skills/spur-dev/references/execution-workflow.md +1 -1
  12. package/plugins/sp/skills/spur-dev/references/idea-evaluation.md +1 -1
  13. package/spur.js +251 -246
  14. package/web/_astro/{BoardApp.BMprmPRc.js → BoardApp.Bjdp3XWV.js} +36 -36
  15. package/web/_astro/{BoardApp.B5rTVNd_.js → BoardApp.DCDZxLlQ.js} +1 -1
  16. package/web/_astro/{TaskDetail.Ddsur8qd.js → TaskDetail.B3vBxynO.js} +1 -1
  17. package/web/_astro/{arc.DLqVVBxP.js → arc.DcuHTB_T.js} +1 -1
  18. package/web/_astro/{architectureDiagram-3BPJPVTR.C-Pk3uAi.js → architectureDiagram-3BPJPVTR.ClOFWBON.js} +1 -1
  19. package/web/_astro/{blockDiagram-GPEHLZMM.BRO1i_Nm.js → blockDiagram-GPEHLZMM.otYyr7Zb.js} +1 -1
  20. package/web/_astro/{c4Diagram-AAUBKEIU.BU_cWu6e.js → c4Diagram-AAUBKEIU.BCjpc9LP.js} +1 -1
  21. package/web/_astro/channel.qgOLXjI_.js +1 -0
  22. package/web/_astro/{chunk-2J33WTMH.D2VXiQ2t.js → chunk-2J33WTMH.D5Wpnpt_.js} +1 -1
  23. package/web/_astro/{chunk-4BX2VUAB.BcHT772J.js → chunk-4BX2VUAB.DkQGtWNz.js} +1 -1
  24. package/web/_astro/{chunk-55IACEB6.DhDtICmo.js → chunk-55IACEB6.B7dqjAip.js} +1 -1
  25. package/web/_astro/{chunk-727SXJPM.BoKHp7gV.js → chunk-727SXJPM.BDQvnKE2.js} +1 -1
  26. package/web/_astro/{chunk-AQP2D5EJ.Chu4JRpE.js → chunk-AQP2D5EJ.D92doBX5.js} +1 -1
  27. package/web/_astro/{chunk-FMBD7UC4.wHdwKPlq.js → chunk-FMBD7UC4.C968oI5G.js} +1 -1
  28. package/web/_astro/{chunk-ND2GUHAM.ffN-M1JB.js → chunk-ND2GUHAM.AkkYC1bY.js} +1 -1
  29. package/web/_astro/{chunk-QZHKN3VN.FRubwg4S.js → chunk-QZHKN3VN.sCc-rKKo.js} +1 -1
  30. package/web/_astro/{classDiagram-4FO5ZUOK.c22tQsc_.js → classDiagram-4FO5ZUOK.pOQ9u0mY.js} +1 -1
  31. package/web/_astro/{classDiagram-v2-Q7XG4LA2.c22tQsc_.js → classDiagram-v2-Q7XG4LA2.pOQ9u0mY.js} +1 -1
  32. package/web/_astro/{cose-bilkent-S5V4N54A.B9c6fiSj.js → cose-bilkent-S5V4N54A.CRaeD6FY.js} +1 -1
  33. package/web/_astro/{cynefin-OW5HDTMX.DrZsrUHl.js → cynefin-OW5HDTMX.BiCPBu54.js} +1 -1
  34. package/web/_astro/{dagre-BM42HDAG.DG1U4b3y.js → dagre-BM42HDAG.C1-q3vkT.js} +1 -1
  35. package/web/_astro/{diagram-2AECGRRQ.CH15b-K3.js → diagram-2AECGRRQ.BSVOv30O.js} +1 -1
  36. package/web/_astro/{diagram-5GNKFQAL.BH1j7twR.js → diagram-5GNKFQAL.CHMkjJ-V.js} +1 -1
  37. package/web/_astro/{diagram-KO2AKTUF.q2zCkbAj.js → diagram-KO2AKTUF.CKY9M564.js} +1 -1
  38. package/web/_astro/{diagram-LMA3HP47.BLaaZnHV.js → diagram-LMA3HP47.B9yNJiTP.js} +1 -1
  39. package/web/_astro/{diagram-OG6HWLK6.BcpRdQwI.js → diagram-OG6HWLK6.Dk8AUGsk.js} +1 -1
  40. package/web/_astro/{erDiagram-TEJ5UH35.D7KBlqfM.js → erDiagram-TEJ5UH35.BQHR89TZ.js} +1 -1
  41. package/web/_astro/{flowDiagram-I6XJVG4X.D_gmuRqg.js → flowDiagram-I6XJVG4X.oNkWVgoK.js} +1 -1
  42. package/web/_astro/{ganttDiagram-6RSMTGT7.DzgWYniD.js → ganttDiagram-6RSMTGT7.Kz2U2nu9.js} +1 -1
  43. package/web/_astro/{gitGraphDiagram-PVQCEYII.576i3hm3.js → gitGraphDiagram-PVQCEYII.DHIZ_wPq.js} +1 -1
  44. package/web/_astro/{infoDiagram-5YYISTIA.DMCclBiz.js → infoDiagram-5YYISTIA.D0NJLcv9.js} +1 -1
  45. package/web/_astro/{ishikawaDiagram-YF4QCWOH.9Kyca4SH.js → ishikawaDiagram-YF4QCWOH.CFEfTWRG.js} +1 -1
  46. package/web/_astro/{journeyDiagram-JHISSGLW.chbdG-8q.js → journeyDiagram-JHISSGLW.Dc7IE7I4.js} +1 -1
  47. package/web/_astro/{kanban-definition-UN3LZRKU.Bds54-e0.js → kanban-definition-UN3LZRKU.D5v0xO9k.js} +1 -1
  48. package/web/_astro/{linear.D5ND9GN_.js → linear.wR5KzXnE.js} +1 -1
  49. package/web/_astro/{mermaid.core.D9N_PGbj.js → mermaid.core.BDQ2FoPd.js} +4 -4
  50. package/web/_astro/{mindmap-definition-RKZ34NQL.t0xop5vp.js → mindmap-definition-RKZ34NQL.W6_INsia.js} +1 -1
  51. package/web/_astro/{pieDiagram-4H26LBE5.DqgKh6hM.js → pieDiagram-4H26LBE5.61ihbgkK.js} +1 -1
  52. package/web/_astro/{quadrantDiagram-W4KKPZXB.DeuHVi2t.js → quadrantDiagram-W4KKPZXB.B5lEpFLo.js} +1 -1
  53. package/web/_astro/{requirementDiagram-4Y6WPE33.BdYFSG4W.js → requirementDiagram-4Y6WPE33.CmhVBVX0.js} +1 -1
  54. package/web/_astro/{sankeyDiagram-5OEKKPKP.k1LPPETJ.js → sankeyDiagram-5OEKKPKP.D7Vvj9Wf.js} +1 -1
  55. package/web/_astro/{sequenceDiagram-3UESZ5HK.DdyfpzoG.js → sequenceDiagram-3UESZ5HK.B1vPLixM.js} +1 -1
  56. package/web/_astro/{stateDiagram-AJRCARHV.DSEI8il0.js → stateDiagram-AJRCARHV.BnACmrGi.js} +1 -1
  57. package/web/_astro/{stateDiagram-v2-BHNVJYJU.CoMnQRKo.js → stateDiagram-v2-BHNVJYJU.CW4pnmc3.js} +1 -1
  58. package/web/_astro/{timeline-definition-PNZ67QCA.DM1AHNm-.js → timeline-definition-PNZ67QCA.CkzMCPnC.js} +1 -1
  59. package/web/_astro/{vennDiagram-CIIHVFJN.C-cJmVtl.js → vennDiagram-CIIHVFJN.CY4aNdAR.js} +1 -1
  60. package/web/_astro/{wardleyDiagram-YWT4CUSO.D0xMzKPb.js → wardleyDiagram-YWT4CUSO.BtoU9UFk.js} +1 -1
  61. package/web/_astro/{xychartDiagram-2RQKCTM6.BH5xJcEq.js → xychartDiagram-2RQKCTM6._9ijUUAs.js} +1 -1
  62. package/web/index.html +1 -1
  63. package/web/_astro/channel.Bs3Illhy.js +0 -1
package/spur.js CHANGED
@@ -85591,6 +85591,14 @@ async function findListeningPid(port, executor = new NodeProcessExecutor2, platf
85591
85591
  } catch {}
85592
85592
  return;
85593
85593
  }
85594
+ function isPidAlive(pid) {
85595
+ try {
85596
+ process.kill(pid, 0);
85597
+ return true;
85598
+ } catch (error) {
85599
+ return error.code !== "ESRCH";
85600
+ }
85601
+ }
85594
85602
  function sendSignalToPid(pid, signal) {
85595
85603
  if (testProcessKiller) {
85596
85604
  return testProcessKiller(pid, signal);
@@ -85814,7 +85822,17 @@ var init_project_stop = __esm(() => {
85814
85822
  });
85815
85823
 
85816
85824
  // ../../packages/app/src/services/project-registry.ts
85817
- import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync6, renameSync as renameSync4, rmdirSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
85825
+ import {
85826
+ existsSync as existsSync8,
85827
+ mkdirSync as mkdirSync4,
85828
+ readdirSync as readdirSync4,
85829
+ readFileSync as readFileSync6,
85830
+ renameSync as renameSync4,
85831
+ rmdirSync,
85832
+ rmSync as rmSync3,
85833
+ unlinkSync as unlinkSync3,
85834
+ writeFileSync as writeFileSync4
85835
+ } from "fs";
85818
85836
  import { dirname as dirname9, join as join18 } from "path";
85819
85837
 
85820
85838
  class ProjectRegistry {
@@ -85853,6 +85871,8 @@ class ProjectRegistry {
85853
85871
  if (code !== "EEXIST") {
85854
85872
  throw err;
85855
85873
  }
85874
+ if (this.reclaimDeadOwnerLock())
85875
+ continue;
85856
85876
  await new Promise((r) => setTimeout(r, 50));
85857
85877
  }
85858
85878
  }
@@ -85882,6 +85902,24 @@ class ProjectRegistry {
85882
85902
  }
85883
85903
  }
85884
85904
  }
85905
+ reclaimDeadOwnerLock() {
85906
+ let markers;
85907
+ try {
85908
+ markers = readdirSync4(this.lockDir);
85909
+ } catch {
85910
+ return false;
85911
+ }
85912
+ if (markers.length === 0 || markers.some((marker) => isPidAlive(Number.parseInt(marker, 10))))
85913
+ return false;
85914
+ const tombstone = `${this.lockDir}.stale-${crypto.randomUUID()}`;
85915
+ try {
85916
+ renameSync4(this.lockDir, tombstone);
85917
+ } catch {
85918
+ return false;
85919
+ }
85920
+ rmSync3(tombstone, { recursive: true, force: true });
85921
+ return true;
85922
+ }
85885
85923
  readRaw() {
85886
85924
  return this.readRawWithStatus().data;
85887
85925
  }
@@ -87819,7 +87857,7 @@ import {
87819
87857
  linkSync,
87820
87858
  lstatSync,
87821
87859
  mkdirSync as mkdirSync6,
87822
- readdirSync as readdirSync4,
87860
+ readdirSync as readdirSync5,
87823
87861
  readFileSync as readFileSync9,
87824
87862
  realpathSync as realpathSync4,
87825
87863
  renameSync as renameSync5,
@@ -88076,7 +88114,7 @@ async function migrateRunStorage(input) {
88076
88114
  let names;
88077
88115
  try {
88078
88116
  await resolveRunArtifactPath(fs, dirs.projectRoot, join26(dirs.scratchDir, "migration-probe"));
88079
- const dirents = readdirSync4(dirs.scratchDir, { withFileTypes: true });
88117
+ const dirents = readdirSync5(dirs.scratchDir, { withFileTypes: true });
88080
88118
  names = dirents.filter((item) => item.isFile()).map((item) => item.name);
88081
88119
  for (const item of dirents) {
88082
88120
  if (!item.isDirectory())
@@ -88085,7 +88123,7 @@ async function migrateRunStorage(input) {
88085
88123
  if (!existsSync11(join26(subtree, "agent-sessions")) && !existsSync11(join26(subtree, "artifacts")))
88086
88124
  continue;
88087
88125
  const walk = (dir) => {
88088
- for (const entry of readdirSync4(dir, { withFileTypes: true })) {
88126
+ for (const entry of readdirSync5(dir, { withFileTypes: true })) {
88089
88127
  if (entry.isDirectory())
88090
88128
  walk(join26(dir, entry.name));
88091
88129
  else if (entry.isFile())
@@ -92548,51 +92586,6 @@ var init_agent_doctor_report = __esm(() => {
92548
92586
  USAGE_SNAPSHOT_STALE_MS = 6 * 60 * 60 * 1000;
92549
92587
  });
92550
92588
 
92551
- // ../../packages/app/src/services/inline-run-attribution.ts
92552
- import { join as join31, resolve as resolve10 } from "path";
92553
- async function openInlineRunProjectDb(workdir) {
92554
- const { mkdirSync } = process.getBuiltinModule("node:fs");
92555
- const url = join31(resolve10(workdir), ".spur", "spur.db");
92556
- mkdirSync(join31(url, ".."), { recursive: true });
92557
- const adapter = await createMigratedDb({ url });
92558
- return { adapter, close: () => adapter.close() };
92559
- }
92560
- function countUnattributedInlineStages(projectRoot) {
92561
- const scratchDir = runStoragePaths(projectRoot).scratchDir;
92562
- let files;
92563
- try {
92564
- files = readdirSync5(scratchDir).filter((entry) => entry.endsWith("-attribution.jsonl"));
92565
- } catch {
92566
- return 0;
92567
- }
92568
- let count = 0;
92569
- for (const file of files) {
92570
- let text;
92571
- try {
92572
- text = readFileSync11(join31(scratchDir, file), "utf8");
92573
- } catch {
92574
- continue;
92575
- }
92576
- for (const line of text.split(`
92577
- `)) {
92578
- if (line.trim() === "")
92579
- continue;
92580
- try {
92581
- const row = JSON.parse(line);
92582
- if (row.executor === null)
92583
- count += 1;
92584
- } catch {}
92585
- }
92586
- }
92587
- return count;
92588
- }
92589
- var readdirSync5, readFileSync11;
92590
- var init_inline_run_attribution = __esm(() => {
92591
- init_src2();
92592
- init_run_storage();
92593
- ({ readdirSync: readdirSync5, readFileSync: readFileSync11 } = process.getBuiltinModule("node:fs"));
92594
- });
92595
-
92596
92589
  // ../../packages/app/src/services/agent-doctor-ops.ts
92597
92590
  class AgentDoctorOps {
92598
92591
  ctx;
@@ -92754,8 +92747,7 @@ class AgentDoctorOps {
92754
92747
  cache: cacheField,
92755
92748
  usage: usage ?? null,
92756
92749
  inlineFailFast: {
92757
- refreshJob: readObservationRefreshStatus(this.ctx.cwd),
92758
- unattributedStages: countUnattributedInlineStages(this.ctx.cwd)
92750
+ refreshJob: readObservationRefreshStatus(this.ctx.cwd)
92759
92751
  }
92760
92752
  }, { enveloped }));
92761
92753
  } else {
@@ -92775,12 +92767,11 @@ var init_agent_doctor_ops = __esm(() => {
92775
92767
  init_agent_doctor_report();
92776
92768
  init_agent_quota_refresh();
92777
92769
  init_executor_tier();
92778
- init_inline_run_attribution();
92779
92770
  });
92780
92771
 
92781
92772
  // ../../packages/app/src/observability/agent-run-log.ts
92782
92773
  import { closeSync as closeSync4, mkdirSync as mkdirSync8, openSync as openSync4, writeSync as writeSync3 } from "fs";
92783
- import { dirname as dirname17, join as join32 } from "path";
92774
+ import { dirname as dirname17, join as join31 } from "path";
92784
92775
 
92785
92776
  class AgentRunLog {
92786
92777
  filePath;
@@ -92792,7 +92783,7 @@ class AgentRunLog {
92792
92783
  bytes = 0;
92793
92784
  truncated = false;
92794
92785
  constructor(options) {
92795
- this.filePath = join32(options.dir, `${options.runId}.md`);
92786
+ this.filePath = join31(options.dir, `${options.runId}.md`);
92796
92787
  this.secrets = options.secrets ?? [];
92797
92788
  this.maxBytes = options.maxBytes ?? DEFAULT_AGENT_RUN_LOG_MAX_BYTES;
92798
92789
  }
@@ -93028,7 +93019,7 @@ var init_event_bridge = __esm(() => {
93028
93019
 
93029
93020
  // ../../packages/app/src/services/run-session-observer.ts
93030
93021
  import { homedir as homedir7 } from "os";
93031
- import { basename as basename5, isAbsolute as isAbsolute6, relative as relative4, resolve as resolve11, sep as sep5 } from "path";
93022
+ import { basename as basename5, isAbsolute as isAbsolute6, relative as relative4, resolve as resolve10, sep as sep5 } from "path";
93032
93023
  function claudeProjectDir(cwd) {
93033
93024
  return cwd.replace(/[^a-zA-Z0-9]/g, "-");
93034
93025
  }
@@ -93139,7 +93130,7 @@ class RunSessionObserver {
93139
93130
  const candidates = [];
93140
93131
  let newest = 0;
93141
93132
  for (const rel of files) {
93142
- const full = resolve11(wm.root, rel);
93133
+ const full = resolve10(wm.root, rel);
93143
93134
  if (wm.projectDirs !== undefined && !wm.projectDirs.has(relative4(wm.root, full).split(sep5, 1)[0] ?? "")) {
93144
93135
  continue;
93145
93136
  }
@@ -93230,10 +93221,10 @@ class RunSessionObserver {
93230
93221
  }
93231
93222
  resolveRoot(source, sessionDir) {
93232
93223
  if (sessionDir !== undefined && sessionDir !== "") {
93233
- return isAbsolute6(sessionDir) ? sessionDir : resolve11(this.cwd, sessionDir);
93224
+ return isAbsolute6(sessionDir) ? sessionDir : resolve10(this.cwd, sessionDir);
93234
93225
  }
93235
93226
  const roots = SOURCE_DEFINITIONS[source].defaultRoots;
93236
- return resolve11(this.home, roots[0] ?? "");
93227
+ return resolve10(this.home, roots[0] ?? "");
93237
93228
  }
93238
93229
  }
93239
93230
  function firstString2(...values) {
@@ -93266,7 +93257,7 @@ var init_run_session_observer = __esm(() => {
93266
93257
 
93267
93258
  // ../../packages/app/src/services/agent-run-executor.ts
93268
93259
  import { homedir as homedir8 } from "os";
93269
- import { basename as basename6, join as join33, relative as relative5 } from "path";
93260
+ import { basename as basename6, join as join32, relative as relative5 } from "path";
93270
93261
  import { isatty as isatty2 } from "tty";
93271
93262
 
93272
93263
  class AgentRunExecutor {
@@ -93377,7 +93368,7 @@ class AgentRunExecutor {
93377
93368
  const envParentRunId = this.ctx.env.SPUR_RUN_ID ?? "";
93378
93369
  const parentRunId = flagParentRunId !== "" ? flagParentRunId : envParentRunId !== "" && envParentRunId !== coordinationRunId ? envParentRunId : undefined;
93379
93370
  const agentLog = new AgentRunLog({
93380
- dir: join33(this.ctx.cwd, ".spur", "memory", "runs"),
93371
+ dir: join32(this.ctx.cwd, ".spur", "memory", "runs"),
93381
93372
  runId: coordinationRunId,
93382
93373
  secrets: configuredSecretValues(this.ctx.env)
93383
93374
  });
@@ -93688,9 +93679,9 @@ class AgentRunExecutor {
93688
93679
  const relDir = relative5(this.ctx.cwd, runDir);
93689
93680
  const record = readWorkflowRunRecord(runDir, runId);
93690
93681
  if (record.kind === "pair" || record.kind === "incomplete") {
93691
- refs.push({ kind: "log", path: join33(relDir, basename6(record.markdownPath)) });
93682
+ refs.push({ kind: "log", path: join32(relDir, basename6(record.markdownPath)) });
93692
93683
  } else if (record.kind === "legacy-log") {
93693
- refs.push({ kind: "log", path: join33(relDir, basename6(record.logPath)) });
93684
+ refs.push({ kind: "log", path: join32(relDir, basename6(record.logPath)) });
93694
93685
  }
93695
93686
  return refs;
93696
93687
  }
@@ -93982,7 +93973,7 @@ var init_agent_service = __esm(() => {
93982
93973
  });
93983
93974
 
93984
93975
  // ../../packages/app/src/services/agent-trace-service.ts
93985
- import { join as join34 } from "path";
93976
+ import { join as join33 } from "path";
93986
93977
 
93987
93978
  class AgentTraceService {
93988
93979
  deps;
@@ -93990,7 +93981,7 @@ class AgentTraceService {
93990
93981
  this.deps = deps;
93991
93982
  }
93992
93983
  get logDir() {
93993
- return this.deps.logDir ?? join34(this.deps.projectPath, ".spur", "memory", "runs");
93984
+ return this.deps.logDir ?? join33(this.deps.projectPath, ".spur", "memory", "runs");
93994
93985
  }
93995
93986
  async trace(runId) {
93996
93987
  const db = await this.deps.openDb(this.deps.projectPath);
@@ -94046,7 +94037,7 @@ class AgentTraceService {
94046
94037
  return rows.map((row) => row.session_id).filter((id) => id !== null && id !== "");
94047
94038
  }
94048
94039
  async existingLog(runId) {
94049
- const path = join34(this.logDir, `${runId}.md`);
94040
+ const path = join33(this.logDir, `${runId}.md`);
94050
94041
  try {
94051
94042
  return await createNodeFileSystem2(this.deps.projectPath).exists(path) ? path : null;
94052
94043
  } catch {
@@ -94403,7 +94394,7 @@ var init_agent_usage_source = __esm(() => {
94403
94394
  });
94404
94395
 
94405
94396
  // ../../packages/app/src/services/anchor-qualifier.ts
94406
- import { basename as basename7, join as join35 } from "path";
94397
+ import { basename as basename7, join as join34 } from "path";
94407
94398
  function normalizeDoneReason(reason) {
94408
94399
  const match = ABSOLUTE_DONE_REASON_RE.exec(reason);
94409
94400
  if (match === null)
@@ -94555,7 +94546,7 @@ async function qualifyAnchors(fs, opts) {
94555
94546
  const dryRun = opts.dryRun ?? false;
94556
94547
  const projectRoot = await resolveRepoRoot(opts.projectRoot, taskDirs[0]);
94557
94548
  const index = await buildTrackedBasenameIndex(projectRoot);
94558
- const durableVerdictExists = opts.durableVerdictExists ?? ((wbs) => fs.exists(join35(projectRoot, ".spur", "memory", "evidence", `${wbs}-verdict.json`)));
94549
+ const durableVerdictExists = opts.durableVerdictExists ?? ((wbs) => fs.exists(join34(projectRoot, ".spur", "memory", "evidence", `${wbs}-verdict.json`)));
94559
94550
  const fileReports = [];
94560
94551
  const scopedFiles = opts.files === undefined ? null : [...opts.files].sort();
94561
94552
  for (const dir of scopedFiles === null ? taskDirs : ["."]) {
@@ -94565,7 +94556,7 @@ async function qualifyAnchors(fs, opts) {
94565
94556
  } catch {
94566
94557
  continue;
94567
94558
  }
94568
- const mdFiles = scopedFiles ?? entries.filter((name) => name.endsWith(".md") && name !== "kanban.md").map((name) => join35(dir, name)).sort();
94559
+ const mdFiles = scopedFiles ?? entries.filter((name) => name.endsWith(".md") && name !== "kanban.md").map((name) => join34(dir, name)).sort();
94569
94560
  for (const filePath of mdFiles) {
94570
94561
  let raw;
94571
94562
  try {
@@ -94666,7 +94657,7 @@ var init_anchor_qualifier = __esm(() => {
94666
94657
  });
94667
94658
 
94668
94659
  // ../../packages/app/src/services/board-catalog-service.ts
94669
- import { isAbsolute as isAbsolute7, relative as relative6, resolve as resolve12, sep as sep6 } from "path";
94660
+ import { isAbsolute as isAbsolute7, relative as relative6, resolve as resolve11, sep as sep6 } from "path";
94670
94661
  function declaredBoardModules(config) {
94671
94662
  return config?.bootstrap?.modules ?? [];
94672
94663
  }
@@ -94674,8 +94665,8 @@ function resolveContained(index, projectRoot, moduleDirectory, declaredPath, id)
94674
94665
  if (isAbsolute7(declaredPath)) {
94675
94666
  throw new BoardModuleConfigError(index, id, `"${declaredPath}" must be relative to the module directory`);
94676
94667
  }
94677
- const root = resolve12(projectRoot, moduleDirectory);
94678
- const target = resolve12(root, declaredPath);
94668
+ const root = resolve11(projectRoot, moduleDirectory);
94669
+ const target = resolve11(root, declaredPath);
94679
94670
  const rel = relative6(root, target);
94680
94671
  if (rel === "" || rel.startsWith("..") || isAbsolute7(rel) || rel.split(sep6).includes("..")) {
94681
94672
  throw new BoardModuleConfigError(index, id, `"${declaredPath}" escapes the module directory "${moduleDirectory}"`);
@@ -94725,7 +94716,7 @@ async function prepareBoardModules(input) {
94725
94716
  descriptors.push({ ...descriptorBase(declaration), type: "iframe", route, url: declaration.url });
94726
94717
  continue;
94727
94718
  }
94728
- const root = resolve12(input.projectRoot, declaration.directory);
94719
+ const root = resolve11(input.projectRoot, declaration.directory);
94729
94720
  const entryPath = resolveContained(index, input.projectRoot, declaration.directory, declaration.entry, declaration.id);
94730
94721
  const styles = declaration.styles.map((style) => ({
94731
94722
  declared: style,
@@ -94857,7 +94848,7 @@ var init_bounded_child_run = __esm(() => {
94857
94848
  });
94858
94849
 
94859
94850
  // ../../packages/app/src/workflow/workflow-resolver.ts
94860
- import { basename as basename8, isAbsolute as isAbsolute8, join as join36, resolve as resolve13 } from "path";
94851
+ import { basename as basename8, isAbsolute as isAbsolute8, join as join35, resolve as resolve12 } from "path";
94861
94852
  function registeredWorkflowPaths(config) {
94862
94853
  const paths = config?.workflows?.paths ?? [".spur/workflows/"];
94863
94854
  const bundledRoot = bundledConfigRoot();
@@ -94865,7 +94856,7 @@ function registeredWorkflowPaths(config) {
94865
94856
  for (const path of paths) {
94866
94857
  if (path.startsWith(BUNDLED_PATH_PREFIX)) {
94867
94858
  if (bundledRoot !== null)
94868
- expanded.push(join36(bundledRoot, path.slice(BUNDLED_PATH_PREFIX.length)));
94859
+ expanded.push(join35(bundledRoot, path.slice(BUNDLED_PATH_PREFIX.length)));
94869
94860
  } else {
94870
94861
  expanded.push(path);
94871
94862
  }
@@ -94873,11 +94864,11 @@ function registeredWorkflowPaths(config) {
94873
94864
  return expanded;
94874
94865
  }
94875
94866
  function normalizeLayerPath(cwd, path) {
94876
- return resolve13(cwd, path);
94867
+ return resolve12(cwd, path);
94877
94868
  }
94878
94869
  function workflowLayers(opts) {
94879
94870
  const sharedRoot = opts.sharedRoot !== undefined ? opts.sharedRoot : bundledConfigRoot();
94880
- const sharedPath = sharedRoot !== null ? normalizeLayerPath(opts.cwd, join36(sharedRoot, "workflows")) : null;
94871
+ const sharedPath = sharedRoot !== null ? normalizeLayerPath(opts.cwd, join35(sharedRoot, "workflows")) : null;
94881
94872
  const layers = [];
94882
94873
  const seen = new Set;
94883
94874
  const add = (id, path) => {
@@ -94886,7 +94877,7 @@ function workflowLayers(opts) {
94886
94877
  seen.add(path);
94887
94878
  layers.push({ id, path });
94888
94879
  };
94889
- add("project", normalizeLayerPath(opts.cwd, join36(opts.cwd, ".spur", "workflows")));
94880
+ add("project", normalizeLayerPath(opts.cwd, join35(opts.cwd, ".spur", "workflows")));
94890
94881
  for (const entry of opts.registered) {
94891
94882
  if (sharedPath !== null && normalizeLayerPath(opts.cwd, entry) === sharedPath)
94892
94883
  continue;
@@ -94930,7 +94921,7 @@ async function scanWorkflowByName(cwd, name, registered) {
94930
94921
  try {
94931
94922
  const files = (await fs.readDir(layer.path)).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
94932
94923
  for (const f of files) {
94933
- const abs = join36(layer.path, f);
94924
+ const abs = join35(layer.path, f);
94934
94925
  const wfName = await readWorkflowNameFast(fs, abs);
94935
94926
  if (wfName === name) {
94936
94927
  return { path: abs, source: layer.id };
@@ -94942,7 +94933,7 @@ async function scanWorkflowByName(cwd, name, registered) {
94942
94933
  }
94943
94934
  function resolveWorkflowFile(cwd, file) {
94944
94935
  const fs = createNodeFileSystem2();
94945
- const projectPath = resolve13(cwd, file);
94936
+ const projectPath = resolve12(cwd, file);
94946
94937
  if (fs.exists(projectPath)) {
94947
94938
  return { path: projectPath, source: "project" };
94948
94939
  }
@@ -94950,20 +94941,20 @@ function resolveWorkflowFile(cwd, file) {
94950
94941
  const base = basename8(file);
94951
94942
  if (!isAbsolute8(file)) {
94952
94943
  const withYaml = isYaml ? file : `${file}.yaml`;
94953
- const projectSpur = resolve13(cwd, ".spur", "workflows", withYaml);
94944
+ const projectSpur = resolve12(cwd, ".spur", "workflows", withYaml);
94954
94945
  if (fs.exists(projectSpur)) {
94955
94946
  return { path: projectSpur, source: "project" };
94956
94947
  }
94957
94948
  if (!isYaml) {
94958
- const projectDirect = resolve13(cwd, `${file}.yaml`);
94949
+ const projectDirect = resolve12(cwd, `${file}.yaml`);
94959
94950
  if (fs.exists(projectDirect)) {
94960
94951
  return { path: projectDirect, source: "project" };
94961
94952
  }
94962
- const projectSpurPipeline = resolve13(cwd, ".spur", "workflows", `${file}-pipeline.yaml`);
94953
+ const projectSpurPipeline = resolve12(cwd, ".spur", "workflows", `${file}-pipeline.yaml`);
94963
94954
  if (fs.exists(projectSpurPipeline)) {
94964
94955
  return { path: projectSpurPipeline, source: "project" };
94965
94956
  }
94966
- const projectDirectPipeline = resolve13(cwd, `${file}-pipeline.yaml`);
94957
+ const projectDirectPipeline = resolve12(cwd, `${file}-pipeline.yaml`);
94967
94958
  if (fs.exists(projectDirectPipeline)) {
94968
94959
  return { path: projectDirectPipeline, source: "project" };
94969
94960
  }
@@ -94972,16 +94963,16 @@ function resolveWorkflowFile(cwd, file) {
94972
94963
  const bundledRoot = bundledConfigRoot();
94973
94964
  if (bundledRoot !== null) {
94974
94965
  const sharedName = isYaml ? base : `${base}.yaml`;
94975
- const sharedPath = join36(bundledRoot, "workflows", sharedName);
94966
+ const sharedPath = join35(bundledRoot, "workflows", sharedName);
94976
94967
  if (fs.exists(sharedPath)) {
94977
94968
  return { path: sharedPath, source: "shared" };
94978
94969
  }
94979
94970
  if (!isYaml) {
94980
- const sharedPipeline = join36(bundledRoot, "workflows", `${base}-pipeline.yaml`);
94971
+ const sharedPipeline = join35(bundledRoot, "workflows", `${base}-pipeline.yaml`);
94981
94972
  if (fs.exists(sharedPipeline)) {
94982
94973
  return { path: sharedPipeline, source: "shared" };
94983
94974
  }
94984
- const sharedLiteral = join36(bundledRoot, "workflows", base);
94975
+ const sharedLiteral = join35(bundledRoot, "workflows", base);
94985
94976
  if (fs.exists(sharedLiteral)) {
94986
94977
  return { path: sharedLiteral, source: "shared" };
94987
94978
  }
@@ -94993,7 +94984,7 @@ function resolveWorkflowFile(cwd, file) {
94993
94984
  async function resolveWorkflowDefinition(cwd, fileOrName, options = {}) {
94994
94985
  const registered = options.registered ?? [];
94995
94986
  const bareName = basename8(fileOrName) === fileOrName && !/\.ya?ml$/.test(fileOrName);
94996
- const named = bareName && !createNodeFileSystem2().exists(resolve13(cwd, fileOrName)) ? await scanWorkflowByName(cwd, fileOrName, registered) : null;
94987
+ const named = bareName && !createNodeFileSystem2().exists(resolve12(cwd, fileOrName)) ? await scanWorkflowByName(cwd, fileOrName, registered) : null;
94997
94988
  let resolved = named ?? resolveWorkflowFile(cwd, fileOrName);
94998
94989
  if (resolved.path === null) {
94999
94990
  const scanned = await scanWorkflowByName(cwd, fileOrName, registered);
@@ -95030,7 +95021,7 @@ var init_workflow_resolver = __esm(() => {
95030
95021
  });
95031
95022
 
95032
95023
  // ../../packages/app/src/services/done-transition-guard.ts
95033
- import { dirname as dirname19, join as join37 } from "path";
95024
+ import { dirname as dirname19, join as join36 } from "path";
95034
95025
  async function readVerdictFrom(fs, path, wbs) {
95035
95026
  let exists;
95036
95027
  try {
@@ -95075,7 +95066,7 @@ async function readVerdictFrom(fs, path, wbs) {
95075
95066
  return { artifact: parsed, path, missing: false };
95076
95067
  }
95077
95068
  async function readVerdictArtifact(fs, runDir, wbs) {
95078
- const evidenceDir = join37(dirname19(runDir), "memory", "evidence");
95069
+ const evidenceDir = join36(dirname19(runDir), "memory", "evidence");
95079
95070
  const evidence = await readVerdictFrom(fs, `${evidenceDir}/${wbs}-verdict.json`, wbs);
95080
95071
  if (!evidence.missing) {
95081
95072
  const { missing: _missing, ...result } = evidence;
@@ -95103,7 +95094,7 @@ async function findUnresolvedTestEvidence(fs, projectRoot, artifact) {
95103
95094
  if (String(row.status).toUpperCase() !== "MET" || typeof row.evidence !== "string")
95104
95095
  continue;
95105
95096
  for (const path of citedTestPaths(row.evidence)) {
95106
- if (!await fs.exists(join37(projectRoot, path)))
95097
+ if (!await fs.exists(join36(projectRoot, path)))
95107
95098
  unresolved.push(`${row.id ?? "?"}: ${path}`);
95108
95099
  }
95109
95100
  }
@@ -95823,7 +95814,7 @@ var init_planning_check_base = __esm(() => {
95823
95814
  });
95824
95815
 
95825
95816
  // ../../packages/app/src/services/task-record.ts
95826
- import { join as join38 } from "path";
95817
+ import { join as join37 } from "path";
95827
95818
  function escapeTablePipe(s) {
95828
95819
  return s.replace(/\|/g, "\\|");
95829
95820
  }
@@ -96237,7 +96228,7 @@ async function resolveDiffBase(options = {}) {
96237
96228
  return explicit;
96238
96229
  const wbs = options.wbs?.trim();
96239
96230
  if (wbs !== undefined && wbs !== "") {
96240
- const baseFile = join38(options.cwd ?? process.cwd(), ".spur", "run", `${wbs}-base.sha`);
96231
+ const baseFile = join37(options.cwd ?? process.cwd(), ".spur", "run", `${wbs}-base.sha`);
96241
96232
  try {
96242
96233
  const sha = (await createNodeFileSystem2().readFile(baseFile)).trim();
96243
96234
  if (DIFF_BASE_RE.test(sha))
@@ -96477,6 +96468,8 @@ function verifyConfidenceBasis(tables) {
96477
96468
  if (weak.length > 0)
96478
96469
  return `${weak.length} row(s) PARTIAL/UNMET`;
96479
96470
  const nonExecutable = tables.acs.filter((row) => {
96471
+ if (row.status.trim().toUpperCase() !== "MET")
96472
+ return true;
96480
96473
  const types = (row.evidenceType ?? "").split("+").map((t) => t.trim().toLowerCase());
96481
96474
  return !types.some((t) => t === "test" || t === "command");
96482
96475
  });
@@ -96761,7 +96754,7 @@ var init_verify_answer_lint = __esm(() => {
96761
96754
  });
96762
96755
 
96763
96756
  // ../../packages/app/src/services/feature-check.ts
96764
- import { dirname as dirname20, join as join39 } from "path";
96757
+ import { dirname as dirname20, join as join38 } from "path";
96765
96758
  function isGroupFeature(fm) {
96766
96759
  const tags = fm.tags;
96767
96760
  return Array.isArray(tags) && tags.includes("group") ? "group" : "standard";
@@ -96859,9 +96852,9 @@ function decodeVerdictRows(source, sectionName, required) {
96859
96852
  function defaultVerdictRunDir(tasksDir) {
96860
96853
  const norm = tasksDir.replace(/\\/g, "/");
96861
96854
  if (/\/docs\/tasks\d*$/.test(norm) || /\/docs\/tasks$/.test(norm)) {
96862
- return join39(dirname20(dirname20(tasksDir)), ".spur", "run");
96855
+ return join38(dirname20(dirname20(tasksDir)), ".spur", "run");
96863
96856
  }
96864
- return join39(dirname20(tasksDir), ".spur", "run");
96857
+ return join38(dirname20(tasksDir), ".spur", "run");
96865
96858
  }
96866
96859
  function scenarioKeys(scenarios) {
96867
96860
  return scenarios.map((s, i) => ({
@@ -97220,7 +97213,7 @@ var init_feature_check = __esm(() => {
97220
97213
  if (options?.asStatus === "done" && options?.runDir) {
97221
97214
  await this.checkFeatureVerificationReceipt(featureId, raw, options.runDir, findings, options.receiptRunPort);
97222
97215
  }
97223
- const dogfoodDir = options?.dogfoodDir ?? (options?.featuresDir ? join39(dirname20(options.featuresDir), "dogfood") : undefined);
97216
+ const dogfoodDir = options?.dogfoodDir ?? (options?.featuresDir ? join38(dirname20(options.featuresDir), "dogfood") : undefined);
97224
97217
  const primaryTasksDir = options?.tasksDir ?? options?.tasksDirs?.[0];
97225
97218
  const runDir = options?.runDir ?? (primaryTasksDir ? defaultVerdictRunDir(primaryTasksDir) : undefined);
97226
97219
  const taskScanDirs = options?.tasksDirs && options.tasksDirs.length > 0 ? options.tasksDirs : options?.tasksDir ? [options.tasksDir] : [];
@@ -97348,7 +97341,7 @@ var init_feature_check = __esm(() => {
97348
97341
  }
97349
97342
  let learningsContent;
97350
97343
  try {
97351
- learningsContent = await this.fs.readFile(join39(this.projectRoot, ".spur", "context", "learnings.md"));
97344
+ learningsContent = await this.fs.readFile(join38(this.projectRoot, ".spur", "context", "learnings.md"));
97352
97345
  } catch {
97353
97346
  learningsContent = undefined;
97354
97347
  }
@@ -97834,7 +97827,7 @@ var init_task_size_precheck = __esm(() => {
97834
97827
  });
97835
97828
 
97836
97829
  // ../../packages/app/src/services/task-check.ts
97837
- import { basename as basename9, dirname as dirname21, join as join40 } from "path";
97830
+ import { basename as basename9, dirname as dirname21, join as join39 } from "path";
97838
97831
  function isPlaceholderBody(body) {
97839
97832
  const stripped = body.replace(/<!--[\s\S]*?-->/g, "").replace(/^\s*>\s*TBD\s*$/gim, "").trim();
97840
97833
  return stripped.length === 0;
@@ -98109,7 +98102,7 @@ var init_task_check = __esm(() => {
98109
98102
  this.runL2(doc, entry, findings, raw);
98110
98103
  this.runL3(doc, entry, effectiveStatus, findings, isTransitionTarget);
98111
98104
  const tasksDir = dirname21(filePath);
98112
- const featuresDir = join40(dirname21(tasksDir), "features");
98105
+ const featuresDir = join39(dirname21(tasksDir), "features");
98113
98106
  await this.runL4(doc, fm, effectiveStatus, findings, featuresDir, tasksDir, wbs, status);
98114
98107
  await this.runL4Rollup(doc, wbs, effectiveStatus, findings, tasksDir);
98115
98108
  if (effectiveStatus !== "done" && effectiveStatus !== "cancelled") {
@@ -98749,7 +98742,7 @@ var init_task_check = __esm(() => {
98749
98742
  for (const cite of citations) {
98750
98743
  if (reported >= 5)
98751
98744
  break;
98752
- const abs = join40(projectRoot, cite.path);
98745
+ const abs = join39(projectRoot, cite.path);
98753
98746
  let exists = false;
98754
98747
  try {
98755
98748
  exists = await this.fs.exists(abs);
@@ -98835,7 +98828,7 @@ var init_task_check = __esm(() => {
98835
98828
  continue;
98836
98829
  }
98837
98830
  try {
98838
- const abs = join40(dir, name);
98831
+ const abs = join39(dir, name);
98839
98832
  const stat = await this.fs.stat(abs);
98840
98833
  if (stat !== null && stat !== undefined && stat.isDirectory?.())
98841
98834
  stack.push(abs);
@@ -98930,7 +98923,7 @@ var init_task_check = __esm(() => {
98930
98923
  }
98931
98924
  async checkVerdictArtifact(wbs, tasksDir, findings) {
98932
98925
  const projectRoot = resolveProjectRootFromTasksDir(tasksDir);
98933
- const runDir = join40(projectRoot, ".spur", "run");
98926
+ const runDir = join39(projectRoot, ".spur", "run");
98934
98927
  const loaded = await readVerdictArtifact(this.fs, runDir, wbs);
98935
98928
  const verdictPath = loaded.path;
98936
98929
  if (loaded.readError) {
@@ -98988,13 +98981,13 @@ var init_task_check = __esm(() => {
98988
98981
  });
98989
98982
 
98990
98983
  // ../../packages/app/src/services/corpus-check.ts
98991
- import { basename as basename10, dirname as dirname22, join as join41, relative as relative7, resolve as resolve14 } from "path";
98984
+ import { basename as basename10, dirname as dirname22, join as join40, relative as relative7, resolve as resolve13 } from "path";
98992
98985
  async function loadTaskMatrix(projectRoot) {
98993
98986
  const fs = createNodeFileSystem2(projectRoot);
98994
- const candidates = [join41(projectRoot, ".spur", "tasks", "section-matrix.yaml")];
98987
+ const candidates = [join40(projectRoot, ".spur", "tasks", "section-matrix.yaml")];
98995
98988
  const bundledRoot = bundledConfigRoot();
98996
98989
  if (bundledRoot !== null)
98997
- candidates.push(join41(bundledRoot, "tasks", "section-matrix.yaml"));
98990
+ candidates.push(join40(bundledRoot, "tasks", "section-matrix.yaml"));
98998
98991
  let matrixPath;
98999
98992
  for (const candidate of candidates) {
99000
98993
  if (await fs.exists(candidate)) {
@@ -99037,7 +99030,7 @@ async function structuralSweep(projectRoot) {
99037
99030
  const wbs = fileName.match(/^(\d{4})_.+\.md$/)?.[1];
99038
99031
  if (wbs === undefined)
99039
99032
  continue;
99040
- const result = await taskService.check(join41(tasksDir, fileName), wbs);
99033
+ const result = await taskService.check(join40(tasksDir, fileName), wbs);
99041
99034
  for (const finding of result.findings) {
99042
99035
  findings.push({
99043
99036
  kind: "task",
@@ -99055,7 +99048,7 @@ async function structuralSweep(projectRoot) {
99055
99048
  const id = fileName.match(/^([A-Z][0-9]*)_.+\.md$/)?.[1];
99056
99049
  if (id === undefined)
99057
99050
  continue;
99058
- const result = await featureService.check(join41(featuresDir, fileName), id, {
99051
+ const result = await featureService.check(join40(featuresDir, fileName), id, {
99059
99052
  featuresDir,
99060
99053
  tasksDir: activeTasksDir,
99061
99054
  tasksDirs: taskDirs,
@@ -99084,7 +99077,7 @@ async function duplicateIds(cwd, taskDirs, featuresDir) {
99084
99077
  return [];
99085
99078
  }
99086
99079
  const pattern = kind === "task" ? /^(\d{4})_/ : /^([A-Z][0-9]*)_/;
99087
- return names.map((n) => ({ m: n.match(pattern), n })).filter((x) => x.m !== null).map((x) => ({ id: x.m[1], file: relative7(cwd, join41(dir, x.n)), kind }));
99080
+ return names.map((n) => ({ m: n.match(pattern), n })).filter((x) => x.m !== null).map((x) => ({ id: x.m[1], file: relative7(cwd, join40(dir, x.n)), kind }));
99088
99081
  };
99089
99082
  const all = [
99090
99083
  ...(await Promise.all(taskDirs.map((dir) => scan(dir, "task")))).flat(),
@@ -99136,14 +99129,14 @@ async function runCorpusCheck(cwd, since, report) {
99136
99129
  }
99137
99130
  function resolveProjectRoot(cwd) {
99138
99131
  const fs = createNodeFileSystem2(cwd);
99139
- let current = resolve14(cwd);
99132
+ let current = resolve13(cwd);
99140
99133
  while (true) {
99141
- if (fs.exists(join41(current, ".spur", "config.yaml"))) {
99134
+ if (fs.exists(join40(current, ".spur", "config.yaml"))) {
99142
99135
  return current;
99143
99136
  }
99144
99137
  const parent = dirname22(current);
99145
99138
  if (parent === current)
99146
- return resolve14(cwd);
99139
+ return resolve13(cwd);
99147
99140
  current = parent;
99148
99141
  }
99149
99142
  }
@@ -99160,14 +99153,14 @@ function diskReader(cwd) {
99160
99153
  return {
99161
99154
  list: async (dir) => {
99162
99155
  try {
99163
- return (await fs.readDir(join41(cwd, dir))).map((n) => `${dir}/${n}`);
99156
+ return (await fs.readDir(join40(cwd, dir))).map((n) => `${dir}/${n}`);
99164
99157
  } catch {
99165
99158
  return [];
99166
99159
  }
99167
99160
  },
99168
99161
  read: async (path) => {
99169
99162
  try {
99170
- return await fs.readFile(join41(cwd, path));
99163
+ return await fs.readFile(join40(cwd, path));
99171
99164
  } catch {
99172
99165
  return null;
99173
99166
  }
@@ -99327,7 +99320,7 @@ var init_corpus_check = __esm(() => {
99327
99320
  });
99328
99321
 
99329
99322
  // ../../packages/app/src/services/corpus-migrator.ts
99330
- import { join as join42, relative as relative8 } from "path";
99323
+ import { join as join41, relative as relative8 } from "path";
99331
99324
  function applyM1(data, flags) {
99332
99325
  const raw = data.status;
99333
99326
  if (typeof raw !== "string")
@@ -99627,7 +99620,7 @@ class CorpusMigrator {
99627
99620
  }
99628
99621
  async discoverFiles() {
99629
99622
  const entries = await this.fs.readDir(this.corpusDir);
99630
- return entries.filter((name) => name.endsWith(".md")).filter((name) => name !== "kanban.md").map((name) => join42(this.corpusDir, name)).sort();
99623
+ return entries.filter((name) => name.endsWith(".md")).filter((name) => name !== "kanban.md").map((name) => join41(this.corpusDir, name)).sort();
99631
99624
  }
99632
99625
  async migrateFile(filePath, dryRun, gitDates = new Map) {
99633
99626
  const flags = [];
@@ -99945,7 +99938,7 @@ var init_feature_receipt_run_port = __esm(() => {
99945
99938
 
99946
99939
  // ../../packages/app/src/services/feature-sync-suppression.ts
99947
99940
  import { createHash as createHash11 } from "crypto";
99948
- import { dirname as dirname23, join as join43 } from "path";
99941
+ import { dirname as dirname23, join as join42 } from "path";
99949
99942
  function computeSyncFingerprint(input) {
99950
99943
  const material = [
99951
99944
  input.featureContentHash,
@@ -99977,7 +99970,7 @@ async function readVerdictMtimeVector(fs, runDir) {
99977
99970
  const dir = runDir.replace(/\/$/, "");
99978
99971
  const vector = [];
99979
99972
  const seen = new Set;
99980
- for (const plane of [join43(dirname23(dir), "memory", "evidence"), dir]) {
99973
+ for (const plane of [join42(dirname23(dir), "memory", "evidence"), dir]) {
99981
99974
  let entries;
99982
99975
  try {
99983
99976
  entries = await fs.readDir(plane);
@@ -99989,7 +99982,7 @@ async function readVerdictMtimeVector(fs, runDir) {
99989
99982
  continue;
99990
99983
  seen.add(entry);
99991
99984
  try {
99992
- const stat = await fs.stat(join43(plane, entry));
99985
+ const stat = await fs.stat(join42(plane, entry));
99993
99986
  if (stat)
99994
99987
  vector.push(`${entry.replace("-verdict.json", "")}:${stat.mtimeMs}`);
99995
99988
  } catch {}
@@ -100946,7 +100939,7 @@ class FleetDispatcher {
100946
100939
  var RECEIPT_POLL_INTERVAL_MS = 250;
100947
100940
 
100948
100941
  // ../../packages/app/src/services/fleet-guest-service.ts
100949
- import { join as join44 } from "path";
100942
+ import { join as join43 } from "path";
100950
100943
 
100951
100944
  class FleetGuestService {
100952
100945
  ctx;
@@ -100972,7 +100965,7 @@ class FleetGuestService {
100972
100965
  }
100973
100966
  async read(id) {
100974
100967
  try {
100975
- const raw = await this.ctx.fs.readFile(join44(this.dir(), `${id}.json`));
100968
+ const raw = await this.ctx.fs.readFile(join43(this.dir(), `${id}.json`));
100976
100969
  const parsed = JSON.parse(raw);
100977
100970
  if (typeof parsed.id !== "string" || typeof parsed.role !== "string")
100978
100971
  return null;
@@ -101047,7 +101040,7 @@ class FleetGuestService {
101047
101040
  joinedAt: new Date().toISOString()
101048
101041
  };
101049
101042
  await this.ctx.fs.ensureDir(this.dir());
101050
- await this.ctx.fs.writeFile(join44(this.dir(), `${id}.json`), `${JSON.stringify(record, null, 4)}
101043
+ await this.ctx.fs.writeFile(join43(this.dir(), `${id}.json`), `${JSON.stringify(record, null, 4)}
101051
101044
  `);
101052
101045
  return { ok: true, guest: record };
101053
101046
  }
@@ -101097,7 +101090,7 @@ class FleetGuestService {
101097
101090
  });
101098
101091
  }
101099
101092
  try {
101100
- await this.ctx.fs.deleteFile(join44(this.dir(), `${record.id}.json`));
101093
+ await this.ctx.fs.deleteFile(join43(this.dir(), `${record.id}.json`));
101101
101094
  } catch {}
101102
101095
  }
101103
101096
  nextGuestId(role, declared, joined) {
@@ -101112,7 +101105,7 @@ class FleetGuestService {
101112
101105
  return `${GUEST_CLAIM_PREFIX}${id}`;
101113
101106
  }
101114
101107
  dir() {
101115
- return join44(this.ctx.cwd, GUEST_RECORD_DIR);
101108
+ return join43(this.ctx.cwd, GUEST_RECORD_DIR);
101116
101109
  }
101117
101110
  }
101118
101111
  var GUEST_LEASE_TTL_MS, GUEST_ROLES, GUEST_RECORD_DIR, GUEST_CLAIM_PREFIX = "guest:";
@@ -101120,7 +101113,7 @@ var init_fleet_guest_service = __esm(() => {
101120
101113
  init_src2();
101121
101114
  GUEST_LEASE_TTL_MS = CLAIM_TTL_MS;
101122
101115
  GUEST_ROLES = ["scribe", "coder", "reviewer", "planner"];
101123
- GUEST_RECORD_DIR = join44(".spur", "run", "guests");
101116
+ GUEST_RECORD_DIR = join43(".spur", "run", "guests");
101124
101117
  });
101125
101118
 
101126
101119
  // ../../packages/app/src/services/fleet-selector.ts
@@ -103263,16 +103256,16 @@ import {
103263
103256
  existsSync as existsSync13,
103264
103257
  mkdirSync as mkdirSync9,
103265
103258
  readdirSync as readdirSync6,
103266
- readFileSync as readFileSync12,
103259
+ readFileSync as readFileSync11,
103267
103260
  readlinkSync,
103268
103261
  realpathSync as realpathSync5,
103269
- rmSync as rmSync3,
103262
+ rmSync as rmSync4,
103270
103263
  symlinkSync,
103271
103264
  unlinkSync as unlinkSync5,
103272
103265
  writeFileSync as writeFileSync7
103273
103266
  } from "fs";
103274
103267
  import { homedir as homedir9 } from "os";
103275
- import { dirname as dirname24, isAbsolute as isAbsolute9, join as join45, resolve as resolve15, sep as sep7 } from "path";
103268
+ import { dirname as dirname24, isAbsolute as isAbsolute9, join as join44, resolve as resolve14, sep as sep7 } from "path";
103276
103269
  function parseImporterVersion(version) {
103277
103270
  if (version === undefined) {
103278
103271
  return null;
@@ -103325,9 +103318,9 @@ async function buildRefreshCoverage(db, selector, fanOut) {
103325
103318
  return { refreshed, skipped: [...DEFERRED_SOURCES], window: { since, until } };
103326
103319
  }
103327
103320
  async function runSessionAugmentedRoots(source, home, cwd, dao) {
103328
- const roots = getSourceDefinition(source).defaultRoots.map((root) => resolve15(home, root));
103321
+ const roots = getSourceDefinition(source).defaultRoots.map((root) => resolve14(home, root));
103329
103322
  const runRoots = [];
103330
- const runBases = [runStoragePaths(cwd).recordsDir, join45(cwd, ".spur", "run")];
103323
+ const runBases = [runStoragePaths(cwd).recordsDir, join44(cwd, ".spur", "run")];
103331
103324
  const seen = new Set;
103332
103325
  const seenRunAgents = new Set;
103333
103326
  const sourcesByRun = new Map;
@@ -103340,7 +103333,7 @@ async function runSessionAugmentedRoots(source, home, cwd, dao) {
103340
103333
  if (!existsSync13(runBase))
103341
103334
  continue;
103342
103335
  for (const runId of readdirSync6(runBase)) {
103343
- const sessionsRoot = join45(runBase, runId, "agent-sessions");
103336
+ const sessionsRoot = join44(runBase, runId, "agent-sessions");
103344
103337
  if (!existsSync13(sessionsRoot))
103345
103338
  continue;
103346
103339
  const mappedSources = sourcesByRun.get(runId);
@@ -103352,7 +103345,7 @@ async function runSessionAugmentedRoots(source, home, cwd, dao) {
103352
103345
  const identity = `${runId}\x00${agent}`;
103353
103346
  if (seenRunAgents.has(identity))
103354
103347
  continue;
103355
- const root = realpathSync5(join45(sessionsRoot, agent));
103348
+ const root = realpathSync5(join44(sessionsRoot, agent));
103356
103349
  if (seen.has(root))
103357
103350
  continue;
103358
103351
  seen.add(root);
@@ -103421,7 +103414,7 @@ class HistoryService {
103421
103414
  const dao = new RunSessionDao(db);
103422
103415
  const discovery = parsedSource !== "opencode" && opts.file === undefined && opts.root === undefined ? await runSessionAugmentedRoots(parsedSource, this.ctx.historyHome ?? homedir9(), this.ctx.cwd ?? process.cwd(), dao) : undefined;
103423
103416
  const scope = await estimateImportScope(parsedSource === "opencode" && opts.file === undefined && opts.root === undefined ? {
103424
- file: this.ctx.openCodeSourceDatabase ?? join45(this.ctx.historyHome ?? homedir9(), ".local/share/opencode/opencode.db"),
103417
+ file: this.ctx.openCodeSourceDatabase ?? join44(this.ctx.historyHome ?? homedir9(), ".local/share/opencode/opencode.db"),
103425
103418
  roots: []
103426
103419
  } : {
103427
103420
  ...opts.file !== undefined && opts.file.length > 0 ? { file: opts.file } : {},
@@ -103496,7 +103489,7 @@ class HistoryService {
103496
103489
  }
103497
103490
  async maintain(opts = {}) {
103498
103491
  const db = await this.ctx.getDb();
103499
- const dbPath = this.ctx.cwd ? join45(this.ctx.cwd, ".spur", "spur.db") : undefined;
103492
+ const dbPath = this.ctx.cwd ? join44(this.ctx.cwd, ".spur", "spur.db") : undefined;
103500
103493
  return maintainDatabase(db, {
103501
103494
  ...opts,
103502
103495
  ...dbPath && opts.dbPath === undefined ? { dbPath } : {}
@@ -104047,13 +104040,13 @@ function writeArtifact(artifact, opts) {
104047
104040
  }
104048
104041
  function resolveArtifactPaths(artifact, opts) {
104049
104042
  if (opts.out !== undefined) {
104050
- const out = resolve15(opts.out);
104043
+ const out = resolve14(opts.out);
104051
104044
  return { artifactPath: out, sidecarPath: `${out.replace(/\.json$/, "")}.errors.jsonl` };
104052
104045
  }
104053
- const base = resolve15(opts.cwd, ".spur", "reports", "history");
104054
- const dateDir = join45(base, artifact.generatedAt.slice(0, 10));
104046
+ const base = resolve14(opts.cwd, ".spur", "reports", "history");
104047
+ const dateDir = join44(base, artifact.generatedAt.slice(0, 10));
104055
104048
  const name = `analyze-${selectorDigest(artifact.selector)}`;
104056
- return { artifactPath: join45(dateDir, `${name}.json`), sidecarPath: join45(dateDir, `${name}.errors.jsonl`) };
104049
+ return { artifactPath: join44(dateDir, `${name}.json`), sidecarPath: join44(dateDir, `${name}.errors.jsonl`) };
104057
104050
  }
104058
104051
  function boundCoverage(coverage) {
104059
104052
  const overflow = [];
@@ -104075,8 +104068,8 @@ function boundCoverage(coverage) {
104075
104068
  return { bounded, overflow };
104076
104069
  }
104077
104070
  function updateLatestPointer(opts, artifactPath) {
104078
- const base = resolve15(opts.cwd, ".spur", "reports", "history");
104079
- const latest = join45(base, "latest.json");
104071
+ const base = resolve14(opts.cwd, ".spur", "reports", "history");
104072
+ const latest = join44(base, "latest.json");
104080
104073
  mkdirSync9(base, { recursive: true });
104081
104074
  if (existsSync13(latest))
104082
104075
  unlinkSync5(latest);
@@ -104084,14 +104077,14 @@ function updateLatestPointer(opts, artifactPath) {
104084
104077
  }
104085
104078
  function resolveArtifactPath(explicitPath, cwd) {
104086
104079
  if (explicitPath !== undefined && explicitPath.length > 0) {
104087
- return { path: resolve15(explicitPath), resolution: "explicit" };
104080
+ return { path: resolve14(explicitPath), resolution: "explicit" };
104088
104081
  }
104089
- const latest = resolve15(cwd, ".spur", "reports", "history", "latest.json");
104082
+ const latest = resolve14(cwd, ".spur", "reports", "history", "latest.json");
104090
104083
  if (!existsSync13(latest)) {
104091
104084
  throw new Error(`No artifact path given and no latest pointer at ${latest}. ` + "Run `spur history analyze` first, or pass an explicit artifact path.");
104092
104085
  }
104093
104086
  const target = readlinkSync(latest);
104094
- const resolved = isAbsolute9(target) ? target : resolve15(dirname24(latest), target);
104087
+ const resolved = isAbsolute9(target) ? target : resolve14(dirname24(latest), target);
104095
104088
  if (!existsSync13(resolved)) {
104096
104089
  throw new Error(`Latest pointer ${latest} \u2192 ${resolved}, but the target does not exist.`);
104097
104090
  }
@@ -104099,7 +104092,7 @@ function resolveArtifactPath(explicitPath, cwd) {
104099
104092
  }
104100
104093
  function runHistoryReport(opts) {
104101
104094
  const { path: artifactPath, resolution } = resolveArtifactPath(opts.path, opts.cwd);
104102
- const raw = readFileSync12(artifactPath, "utf8");
104095
+ const raw = readFileSync11(artifactPath, "utf8");
104103
104096
  let parsed;
104104
104097
  try {
104105
104098
  parsed = JSON.parse(raw);
@@ -104142,7 +104135,7 @@ function formatIssue(issue) {
104142
104135
  return `${issue.sourceFile}:${issue.sourceLine}: ${issue.reason}`;
104143
104136
  }
104144
104137
  function pruneReports(cwd, retentionDays = REPORT_RETENTION_DAYS, now = new Date) {
104145
- const dir = join45(cwd, ".spur", "reports", "history");
104138
+ const dir = join44(cwd, ".spur", "reports", "history");
104146
104139
  let entries;
104147
104140
  try {
104148
104141
  entries = readdirSync6(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
@@ -104160,7 +104153,7 @@ function pruneReports(cwd, retentionDays = REPORT_RETENTION_DAYS, now = new Date
104160
104153
  if (entryDate >= cutoff)
104161
104154
  continue;
104162
104155
  try {
104163
- rmSync3(join45(dir, name), { recursive: true, force: true });
104156
+ rmSync4(join44(dir, name), { recursive: true, force: true });
104164
104157
  pruned.push(name);
104165
104158
  } catch {}
104166
104159
  }
@@ -104205,6 +104198,19 @@ var init_history_service = __esm(() => {
104205
104198
  HISTORY_BUSY_ERROR_PATTERN = /database is locked|SQLITE_BUSY/i;
104206
104199
  });
104207
104200
 
104201
+ // ../../packages/app/src/services/inline-run-attribution.ts
104202
+ import { join as join45, resolve as resolve15 } from "path";
104203
+ async function openInlineRunProjectDb(workdir) {
104204
+ const { mkdirSync } = process.getBuiltinModule("node:fs");
104205
+ const url = join45(resolve15(workdir), ".spur", "spur.db");
104206
+ mkdirSync(join45(url, ".."), { recursive: true });
104207
+ const adapter = await createMigratedDb({ url });
104208
+ return { adapter, close: () => adapter.close() };
104209
+ }
104210
+ var init_inline_run_attribution = __esm(() => {
104211
+ init_src2();
104212
+ });
104213
+
104208
104214
  // ../../packages/app/src/services/job-worker-service.ts
104209
104215
  class JobHandlerRegistry {
104210
104216
  handlers = new Map;
@@ -104993,7 +104999,7 @@ var HANDOFF_TIMEOUT_MS = 5000, HANDOFF_POLL_MS = 50;
104993
104999
  var init_project_server_owner = () => {};
104994
105000
 
104995
105001
  // ../../packages/app/src/services/project-start.ts
104996
- import { existsSync as existsSync15, readdirSync as readdirSync8, readFileSync as readFileSync13, realpathSync as realpathSync7, statSync as statSync11 } from "fs";
105002
+ import { existsSync as existsSync15, readdirSync as readdirSync8, readFileSync as readFileSync12, realpathSync as realpathSync7, statSync as statSync11 } from "fs";
104997
105003
  import { basename as basename12, join as join47, resolve as resolve16 } from "path";
104998
105004
  function shQuote(value) {
104999
105005
  return `'${value.replace(/'/g, `'\\''`)}'`;
@@ -105056,14 +105062,6 @@ function resolveSpurServeCommand() {
105056
105062
  }
105057
105063
  throw new Error("Could not resolve the spur CLI to spawn `spur serve`. Ensure `spur` is on PATH (or invoke start via the monorepo CLI).");
105058
105064
  }
105059
- function isPidAlive(pid) {
105060
- try {
105061
- process.kill(pid, 0);
105062
- return true;
105063
- } catch (error) {
105064
- return error.code !== "ESRCH";
105065
- }
105066
- }
105067
105065
  function readOwnerClaimPid(projectRoot) {
105068
105066
  try {
105069
105067
  const directory = join47(realpathSync7(projectRoot), ".spur", "server-owner.lock");
@@ -105129,7 +105127,7 @@ function daemonLogSize(projectRoot) {
105129
105127
  }
105130
105128
  function daemonLogSuffix(projectRoot, offset) {
105131
105129
  try {
105132
- const content = readFileSync13(join47(projectRoot, ".spur", "run", "serve-spawn.log"), "utf8");
105130
+ const content = readFileSync12(join47(projectRoot, ".spur", "run", "serve-spawn.log"), "utf8");
105133
105131
  const tail = content.slice(offset).trim().slice(-2000).replace(/\n+/g, " | ");
105134
105132
  return tail === "" ? "" : ` Daemon log tail: ${tail}`;
105135
105133
  } catch {
@@ -108340,7 +108338,7 @@ var init_section_matrix_loader = __esm(() => {
108340
108338
  });
108341
108339
 
108342
108340
  // ../../packages/app/src/services/slash-commands-service.ts
108343
- import { existsSync as existsSync16, mkdirSync as mkdirSync11, readdirSync as readdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "fs";
108341
+ import { existsSync as existsSync16, mkdirSync as mkdirSync11, readdirSync as readdirSync9, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "fs";
108344
108342
  import { homedir as homedir11 } from "os";
108345
108343
  import { basename as basename13, dirname as dirname25, join as join50, resolve as resolve18 } from "path";
108346
108344
  function getBuiltinClaudeCommands() {
@@ -108430,7 +108428,7 @@ function scanMarkdownCommands(dirPath, options = {}) {
108430
108428
  }
108431
108429
  const fullPath = join50(dirPath, entry.name);
108432
108430
  const stem = basename13(entry.name, ".md");
108433
- const fileContent = readFileSync14(fullPath, "utf-8");
108431
+ const fileContent = readFileSync13(fullPath, "utf-8");
108434
108432
  const { frontmatter, body } = parseMarkdownFrontmatter(fileContent);
108435
108433
  const cleanPrefix = prefix.endsWith(":") ? prefix : prefix.endsWith("/") ? prefix : `${prefix}:`;
108436
108434
  const name = prefix === "/" ? `/${stem}` : `${cleanPrefix}${stem}`;
@@ -108522,13 +108520,13 @@ function discoverEnabledClaudePlugins(options = {}) {
108522
108520
  let userSettings = {};
108523
108521
  if (existsSync16(userSettingsPath)) {
108524
108522
  try {
108525
- userSettings = JSON.parse(readFileSync14(userSettingsPath, "utf-8"));
108523
+ userSettings = JSON.parse(readFileSync13(userSettingsPath, "utf-8"));
108526
108524
  } catch {}
108527
108525
  }
108528
108526
  let projectSettings = {};
108529
108527
  if (existsSync16(projectSettingsPath)) {
108530
108528
  try {
108531
- projectSettings = JSON.parse(readFileSync14(projectSettingsPath, "utf-8"));
108529
+ projectSettings = JSON.parse(readFileSync13(projectSettingsPath, "utf-8"));
108532
108530
  } catch {}
108533
108531
  }
108534
108532
  const enabledPlugins = {
@@ -108542,7 +108540,7 @@ function discoverEnabledClaudePlugins(options = {}) {
108542
108540
  let installedManifest = { plugins: {} };
108543
108541
  if (existsSync16(installedPath)) {
108544
108542
  try {
108545
- installedManifest = JSON.parse(readFileSync14(installedPath, "utf-8"));
108543
+ installedManifest = JSON.parse(readFileSync13(installedPath, "utf-8"));
108546
108544
  } catch {}
108547
108545
  }
108548
108546
  const installedPlugins = installedManifest.plugins ?? {};
@@ -108741,7 +108739,7 @@ function loadSlashCommands(filePath = getSlashCommandsFilePath(), autoGenerateIf
108741
108739
  return getBuiltinClaudeCommands();
108742
108740
  }
108743
108741
  try {
108744
- const content = readFileSync14(filePath, "utf-8");
108742
+ const content = readFileSync13(filePath, "utf-8");
108745
108743
  const raw = JSON.parse(content);
108746
108744
  const parsed = slashCommandsFileSchema.safeParse(raw);
108747
108745
  if (parsed.success && parsed.data.commands.length > 0) {
@@ -119526,7 +119524,7 @@ async function runPersistOut(input) {
119526
119524
  if (!wbs)
119527
119525
  continue;
119528
119526
  try {
119529
- const raw = readFileSync15(resolve26(toWorkdir, taskFile), "utf8");
119527
+ const raw = readFileSync14(resolve26(toWorkdir, taskFile), "utf8");
119530
119528
  const status = MarkdownDocument.parse(raw, "task").frontmatterData?.status;
119531
119529
  if (typeof status === "string" && (status === "done" || status === "cancelled")) {
119532
119530
  await reconcileExistingLifecycleRow(async () => targetDb.adapter, TASK_LIFECYCLE_PROFILE, wbs, status);
@@ -119550,7 +119548,7 @@ async function runPersistOut(input) {
119550
119548
  return { exitCode: 1, stdout: out, stderr: err };
119551
119549
  }
119552
119550
  }
119553
- var existsSync20, mkdirSync13, readFileSync15, writeFileSync10, lstat3, mkdir4, readdir4, readFile6, MAX_CITED_RUN_FILES = 64;
119551
+ var existsSync20, mkdirSync13, readFileSync14, writeFileSync10, lstat3, mkdir4, readdir4, readFile6, MAX_CITED_RUN_FILES = 64;
119554
119552
  var init_worktree_persist = __esm(() => {
119555
119553
  init_src2();
119556
119554
  init_dist5();
@@ -119563,7 +119561,7 @@ var init_worktree_persist = __esm(() => {
119563
119561
  init_inline_run_attribution();
119564
119562
  init_run_storage();
119565
119563
  init_verify_verdict();
119566
- ({ existsSync: existsSync20, mkdirSync: mkdirSync13, readFileSync: readFileSync15, writeFileSync: writeFileSync10 } = process.getBuiltinModule("node:fs"));
119564
+ ({ existsSync: existsSync20, mkdirSync: mkdirSync13, readFileSync: readFileSync14, writeFileSync: writeFileSync10 } = process.getBuiltinModule("node:fs"));
119567
119565
  ({ lstat: lstat3, mkdir: mkdir4, readdir: readdir4, readFile: readFile6 } = process.getBuiltinModule("node:fs/promises"));
119568
119566
  });
119569
119567
 
@@ -125525,7 +125523,7 @@ function childEnv(env) {
125525
125523
 
125526
125524
  // ../../packages/app/src/workflow/wrapup/drift-probe.ts
125527
125525
  import { spawnSync as spawnSync4 } from "child_process";
125528
- import { mkdirSync as mkdirSync16, readFileSync as readFileSync19, writeFileSync as writeFileSync11 } from "fs";
125526
+ import { mkdirSync as mkdirSync16, readFileSync as readFileSync18, writeFileSync as writeFileSync11 } from "fs";
125529
125527
  import { join as join74 } from "path";
125530
125528
  function solutionSectionOf(content) {
125531
125529
  const heading = /^#{2,4}\s+Solution\s*$/m.exec(content);
@@ -125601,7 +125599,7 @@ function runDriftProbe(env, options = {}, spur = defaultSpurRunner(env, options)
125601
125599
  };
125602
125600
  let tasks;
125603
125601
  try {
125604
- tasks = JSON.parse(readFileSync19(abs(relTasksFile), "utf8"));
125602
+ tasks = JSON.parse(readFileSync18(abs(relTasksFile), "utf8"));
125605
125603
  } catch {
125606
125604
  tasks = undefined;
125607
125605
  }
@@ -125682,7 +125680,7 @@ var init_drift_probe = __esm(() => {
125682
125680
 
125683
125681
  // ../../packages/app/src/workflow/wrapup/steps.ts
125684
125682
  import { spawnSync as spawnSync5 } from "child_process";
125685
- import { appendFileSync as appendFileSync6, existsSync as existsSync22, mkdirSync as mkdirSync17, readFileSync as readFileSync20, writeFileSync as writeFileSync12 } from "fs";
125683
+ import { appendFileSync as appendFileSync6, existsSync as existsSync22, mkdirSync as mkdirSync17, readFileSync as readFileSync19, writeFileSync as writeFileSync12 } from "fs";
125686
125684
  import { join as join75 } from "path";
125687
125685
  function jqPick(...values) {
125688
125686
  for (const value of values) {
@@ -125856,13 +125854,13 @@ function writeRouteReason(env, options = {}) {
125856
125854
  }
125857
125855
  let taskCount = -1;
125858
125856
  try {
125859
- const parsed = JSON.parse(readFileSync20(abs(join75(".spur", "run", `${runId}-wrapup-tasks.json`)), "utf8"));
125857
+ const parsed = JSON.parse(readFileSync19(abs(join75(".spur", "run", `${runId}-wrapup-tasks.json`)), "utf8"));
125860
125858
  if (Array.isArray(parsed))
125861
125859
  taskCount = parsed.length;
125862
125860
  } catch {}
125863
125861
  let probeClean = false;
125864
125862
  try {
125865
- const probe = JSON.parse(readFileSync20(abs(join75(".spur", "run", `${runId}-drift-probe.json`)), "utf8"));
125863
+ const probe = JSON.parse(readFileSync19(abs(join75(".spur", "run", `${runId}-drift-probe.json`)), "utf8"));
125866
125864
  probeClean = probe?.clean === true;
125867
125865
  } catch {}
125868
125866
  const mode = env.mode ?? "";
@@ -125882,7 +125880,7 @@ function writeRouteReason(env, options = {}) {
125882
125880
  }
125883
125881
  function readFileSyncSafe(path) {
125884
125882
  try {
125885
- return readFileSync20(path, "utf8");
125883
+ return readFileSync19(path, "utf8");
125886
125884
  } catch {
125887
125885
  return null;
125888
125886
  }
@@ -125905,7 +125903,7 @@ function verdictFromTestingSection(content) {
125905
125903
  }
125906
125904
  function verdictOfArtifact(verdictPath) {
125907
125905
  try {
125908
- const raw = jqPick(JSON.parse(readFileSync20(verdictPath, "utf8")).verdict, "");
125906
+ const raw = jqPick(JSON.parse(readFileSync19(verdictPath, "utf8")).verdict, "");
125909
125907
  const text = jqText(raw);
125910
125908
  return text.length > 0 ? text : null;
125911
125909
  } catch {
@@ -125928,7 +125926,7 @@ function runMetrics(env, options = {}) {
125928
125926
  };
125929
125927
  let captured;
125930
125928
  try {
125931
- captured = JSON.parse(readFileSync20(abs(relTasksFile), "utf8"));
125929
+ captured = JSON.parse(readFileSync19(abs(relTasksFile), "utf8"));
125932
125930
  } catch {
125933
125931
  captured = undefined;
125934
125932
  }
@@ -126267,7 +126265,7 @@ var init_pipeline_steps = __esm(() => {
126267
126265
  });
126268
126266
 
126269
126267
  // ../../packages/app/src/workflow/task-diffstat.ts
126270
- import { fstatSync as fstatSync4, mkdirSync as mkdirSync18, readFileSync as readFileSync21, renameSync as renameSync7, statSync as statSync14, writeFileSync as writeFileSync13 } from "fs";
126268
+ import { fstatSync as fstatSync4, mkdirSync as mkdirSync18, readFileSync as readFileSync20, renameSync as renameSync7, statSync as statSync14, writeFileSync as writeFileSync13 } from "fs";
126271
126269
  import { join as join76 } from "path";
126272
126270
  function defaultGitRunner(cwd) {
126273
126271
  return (args) => {
@@ -126283,7 +126281,7 @@ function defaultGitRunner(cwd) {
126283
126281
  function countNewlines2(path, cwd) {
126284
126282
  let content;
126285
126283
  try {
126286
- content = readFileSync21(cwd ? join76(cwd, path) : path, "utf8");
126284
+ content = readFileSync20(cwd ? join76(cwd, path) : path, "utf8");
126287
126285
  } catch {
126288
126286
  return 0;
126289
126287
  }
@@ -126328,7 +126326,7 @@ function runDiffstat(env, options = {}, git = defaultGitRunner(options.cwd)) {
126328
126326
  } catch {}
126329
126327
  let base = "";
126330
126328
  try {
126331
- base = readFileSync21(abs(join76(relRun, `${wbs}-base.sha`)), "utf8").trim();
126329
+ base = readFileSync20(abs(join76(relRun, `${wbs}-base.sha`)), "utf8").trim();
126332
126330
  } catch {}
126333
126331
  if (!/^[0-9a-f]{7,40}$/i.test(base))
126334
126332
  return failSafe("run base .spur/run/<wbs>-base.sha missing or malformed");
@@ -126484,9 +126482,9 @@ import {
126484
126482
  mkdtempSync,
126485
126483
  openSync as openSync8,
126486
126484
  readdirSync as readdirSync11,
126487
- readFileSync as readFileSync22,
126485
+ readFileSync as readFileSync21,
126488
126486
  rmdirSync as rmdirSync3,
126489
- rmSync as rmSync4,
126487
+ rmSync as rmSync5,
126490
126488
  statSync as statSync15,
126491
126489
  unlinkSync as unlinkSync7,
126492
126490
  writeFileSync as writeFileSync14
@@ -126564,7 +126562,7 @@ function buildReceipt(input) {
126564
126562
  }
126565
126563
  function readReceipt2(receiptPath) {
126566
126564
  try {
126567
- return JSON.parse(readFileSync22(receiptPath, "utf8"));
126565
+ return JSON.parse(readFileSync21(receiptPath, "utf8"));
126568
126566
  } catch {
126569
126567
  return null;
126570
126568
  }
@@ -126590,7 +126588,7 @@ function isTimeoutShapedLog(text) {
126590
126588
  }
126591
126589
  function readContendedRetryDigest(markerPath) {
126592
126590
  try {
126593
- return readFileSync22(markerPath, "utf8").trim();
126591
+ return readFileSync21(markerPath, "utf8").trim();
126594
126592
  } catch {
126595
126593
  return "";
126596
126594
  }
@@ -126631,10 +126629,8 @@ function resolveReceiptReuse(receiptPath, wbs, capture) {
126631
126629
  refusal: `receipt belongs to ${String(receipt.wbs)}, not ${wbs}`
126632
126630
  };
126633
126631
  }
126634
- return {
126635
- reuse: readReceiptStatus(receiptPath, capture.tree, wbs).reuse,
126636
- receiptStatus: readReceiptStatus(receiptPath, capture.tree, wbs)
126637
- };
126632
+ const receiptStatus = readReceiptStatus(receiptPath, capture.tree, wbs);
126633
+ return { reuse: receiptStatus.reuse, receiptStatus };
126638
126634
  }
126639
126635
  function workspaceOf(file, exists) {
126640
126636
  const segments = file.split("/");
@@ -126661,7 +126657,7 @@ function lightScope(changedFiles, exists = existsSync23) {
126661
126657
  matched = true;
126662
126658
  }
126663
126659
  if (rest.startsWith("commands/")) {
126664
- const flagTest = "plugins/sp/tests/flag-contract-parity.test.ts";
126660
+ const flagTest = "scripts/commands/flag-contract-parity.test.ts";
126665
126661
  if (exists(flagTest)) {
126666
126662
  tests.add(flagTest);
126667
126663
  matched = true;
@@ -126699,7 +126695,7 @@ function lightScope(changedFiles, exists = existsSync23) {
126699
126695
  }
126700
126696
  function workspaceHasTypecheck(workspace) {
126701
126697
  try {
126702
- const pkg = JSON.parse(readFileSync22(join77(workspace, "package.json"), "utf8"));
126698
+ const pkg = JSON.parse(readFileSync21(join77(workspace, "package.json"), "utf8"));
126703
126699
  return typeof pkg.scripts?.typecheck === "string";
126704
126700
  } catch {
126705
126701
  return false;
@@ -126727,7 +126723,8 @@ function planLightChecks(scope, hasTypecheck = workspaceHasTypecheck, builder) {
126727
126723
  }
126728
126724
  const testsByWorkspace = new Map;
126729
126725
  for (const test of scope.tests) {
126730
- const workspace = test.slice(0, test.indexOf("/tests/"));
126726
+ const cut = test.indexOf("/tests/");
126727
+ const workspace = test.slice(0, cut >= 0 ? cut : test.indexOf("/"));
126731
126728
  const paths = testsByWorkspace.get(workspace) ?? [];
126732
126729
  paths.push(test.slice(workspace.length + 1));
126733
126730
  testsByWorkspace.set(workspace, paths);
@@ -126792,7 +126789,7 @@ function runLightGate(env, options = {}) {
126792
126789
  let preserveFullReceipt = false;
126793
126790
  if (digest.length > 0) {
126794
126791
  try {
126795
- const prior = JSON.parse(readFileSync22(abs(receiptFile), "utf8"));
126792
+ const prior = JSON.parse(readFileSync21(abs(receiptFile), "utf8"));
126796
126793
  if (prior?.schemaVersion === RECEIPT_SCHEMA_VERSION && prior.inputDigest === digest) {
126797
126794
  if (prior.tier === "full") {
126798
126795
  preserveFullReceipt = true;
@@ -126883,10 +126880,10 @@ function runShellCommand(cmd, cwd, extraEnv) {
126883
126880
  return { output: `sh -c failed: ${result.error.message}
126884
126881
  `, code: 1 };
126885
126882
  }
126886
- return { output: readFileSync22(path, "utf8"), code: result.status ?? 1 };
126883
+ return { output: readFileSync21(path, "utf8"), code: result.status ?? 1 };
126887
126884
  } finally {
126888
126885
  closeSync8(fd);
126889
- rmSync4(dir, { recursive: true, force: true });
126886
+ rmSync5(dir, { recursive: true, force: true });
126890
126887
  }
126891
126888
  }
126892
126889
  function gateLockDir() {
@@ -126920,7 +126917,7 @@ function readGateLockClaim(dir) {
126920
126917
  }
126921
126918
  for (const marker of markers) {
126922
126919
  try {
126923
- const claim = JSON.parse(readFileSync22(join77(dir, marker), "utf8"));
126920
+ const claim = JSON.parse(readFileSync21(join77(dir, marker), "utf8"));
126924
126921
  if (typeof claim.pid === "number")
126925
126922
  return { claim, marker };
126926
126923
  } catch {
@@ -127007,7 +127004,7 @@ function acquireGateLock(env, logLine = () => {}) {
127007
127004
  return {
127008
127005
  release() {
127009
127006
  try {
127010
- rmSync4(join77(dir, marker), { force: true });
127007
+ rmSync5(join77(dir, marker), { force: true });
127011
127008
  rmdirSync3(dir);
127012
127009
  } catch {}
127013
127010
  },
@@ -127096,7 +127093,7 @@ function runQualityGate(mode, env, options = {}) {
127096
127093
  const statusFile = rel("-test-gate.status");
127097
127094
  const attemptFile = rel("-test-fix-attempt");
127098
127095
  const abs = (p) => cwd ? join77(cwd, p) : p;
127099
- const priorLog = mode === "recheck" && existsSync23(abs(logFile)) ? readFileSync22(abs(logFile), "utf8") : "";
127096
+ const priorLog = mode === "recheck" && existsSync23(abs(logFile)) ? readFileSync21(abs(logFile), "utf8") : "";
127100
127097
  writeFileSync14(abs(logFile), "");
127101
127098
  if (mode === "run")
127102
127099
  writeFileSync14(abs(attemptFile), `0
@@ -127127,7 +127124,7 @@ function runQualityGate(mode, env, options = {}) {
127127
127124
  `;
127128
127125
  process.stdout.write(line);
127129
127126
  appendFileSync7(abs(logFile), line);
127130
- writeFileSync14(abs(findingsFile), extractFindings(readFileSync22(abs(logFile), "utf8")));
127127
+ writeFileSync14(abs(findingsFile), extractFindings(readFileSync21(abs(logFile), "utf8")));
127131
127128
  writeFileSync14(abs(statusFile), `PASS
127132
127129
  `);
127133
127130
  return { status: "PASS", attempts: 0, logFile, findingsFile, statusFile, attemptFile };
@@ -127155,7 +127152,7 @@ function runQualityGate(mode, env, options = {}) {
127155
127152
  writeFileSync14(abs(`${logFile}.probe`), probe.output);
127156
127153
  gateRc = probe.code;
127157
127154
  appendFileSync7(abs(logFile), probe.output);
127158
- rmSync4(abs(`${logFile}.probe`), { force: true });
127155
+ rmSync5(abs(`${logFile}.probe`), { force: true });
127159
127156
  }
127160
127157
  let gateRuntimeMs = 0;
127161
127158
  let queueWaitMs = 0;
@@ -127177,7 +127174,7 @@ function runQualityGate(mode, env, options = {}) {
127177
127174
  writeFileSync14(abs(attemptLogPath), attempt.output);
127178
127175
  const locked = isTransientLock(attempt.output);
127179
127176
  appendFileSync7(abs(logFile), attempt.output);
127180
- rmSync4(abs(attemptLogPath), { force: true });
127177
+ rmSync5(abs(attemptLogPath), { force: true });
127181
127178
  gateRc = attempt.code;
127182
127179
  if (gateRc === 0 || !locked || gateAttempt >= MAX_GATE_ATTEMPTS)
127183
127180
  break;
@@ -127194,10 +127191,10 @@ function runQualityGate(mode, env, options = {}) {
127194
127191
  `);
127195
127192
  }
127196
127193
  if (gateRc !== 0) {
127197
- const gateLog = existsSync23(abs(logFile)) ? readFileSync22(abs(logFile), "utf8") : "";
127194
+ const gateLog = existsSync23(abs(logFile)) ? readFileSync21(abs(logFile), "utf8") : "";
127198
127195
  if (/^\s*0 fail\b/m.test(gateLog) && !/^\s*[1-9]\d*\s+fail\b/m.test(gateLog)) {
127199
127196
  const bunfigPath = cwd ? join77(cwd, "bunfig.toml") : "bunfig.toml";
127200
- const threshold = existsSync23(bunfigPath) ? parseCoverageThreshold(readFileSync22(bunfigPath, "utf8")) : null;
127197
+ const threshold = existsSync23(bunfigPath) ? parseCoverageThreshold(readFileSync21(bunfigPath, "utf8")) : null;
127201
127198
  if (threshold) {
127202
127199
  for (const shortfall of scanCoverageShortfalls(gateLog, threshold)) {
127203
127200
  process.stdout.write(`${shortfall}
@@ -127216,9 +127213,9 @@ function runQualityGate(mode, env, options = {}) {
127216
127213
  } else {
127217
127214
  process.stdout.write(`quality gate FAIL \u2014 last 40 lines follow (full log: ${logFile})
127218
127215
  `);
127219
- process.stdout.write(tailLines(readFileSync22(abs(logFile), "utf8"), 40));
127216
+ process.stdout.write(tailLines(readFileSync21(abs(logFile), "utf8"), 40));
127220
127217
  }
127221
- writeFileSync14(abs(findingsFile), extractFindings(readFileSync22(abs(logFile), "utf8")));
127218
+ writeFileSync14(abs(findingsFile), extractFindings(readFileSync21(abs(logFile), "utf8")));
127222
127219
  const status = gateRc === 0 ? "PASS" : "FAIL";
127223
127220
  writeFileSync14(abs(statusFile), `${status}
127224
127221
  `);
@@ -127584,10 +127581,11 @@ function reviewConfidenceBasis(rows, verdict) {
127584
127581
  return `${open.length} P1-P3 row(s) still open`;
127585
127582
  const carried = rows.filter((row) => {
127586
127583
  const disposition = row.disposition.trim().toUpperCase();
127587
- return disposition.startsWith("DEFER") || disposition === "ACCEPTED";
127584
+ const openP4 = (disposition.length === 0 || disposition === "OPEN") && /^P4/.test(row.priority.trim().toUpperCase());
127585
+ return openP4 || disposition.startsWith("DEFER") || disposition === "ACCEPTED";
127588
127586
  });
127589
127587
  if (carried.length > 0)
127590
- return `${carried.length} finding(s) carried as DEFER/ACCEPTED`;
127588
+ return `${carried.length} finding(s) carried as open P4/DEFER/ACCEPTED`;
127591
127589
  return "every finding is closed";
127592
127590
  }
127593
127591
  function buildReviewGateArtifact(wbs, taskContent, deferrals) {
@@ -127992,9 +127990,9 @@ import {
127992
127990
  fsyncSync as fsyncSync2,
127993
127991
  openSync as openSync9,
127994
127992
  readdirSync as readdirSync14,
127995
- readFileSync as readFileSync26,
127993
+ readFileSync as readFileSync25,
127996
127994
  renameSync as renameSync9,
127997
- rmSync as rmSync6,
127995
+ rmSync as rmSync7,
127998
127996
  statSync as statSync17,
127999
127997
  writeFileSync as writeFileSync17
128000
127998
  } from "fs";
@@ -128243,10 +128241,10 @@ function logicDigest(path) {
128243
128241
  const walk = (dir) => readdirSync14(dir, { withFileTypes: true }).flatMap((e) => e.isDirectory() ? walk(join85(dir, e.name)) : /\.(md|ya?ml)$/.test(e.name) ? [join85(dir, e.name)] : []).sort();
128244
128242
  for (const f of walk(path)) {
128245
128243
  h.update(f.slice(path.length));
128246
- h.update(readFileSync26(f));
128244
+ h.update(readFileSync25(f));
128247
128245
  }
128248
128246
  } else {
128249
- h.update(readFileSync26(path));
128247
+ h.update(readFileSync25(path));
128250
128248
  }
128251
128249
  return h.digest("hex");
128252
128250
  } catch {
@@ -128402,7 +128400,7 @@ function validateSelector(opts) {
128402
128400
  function buildProvenance(opts) {
128403
128401
  let raw;
128404
128402
  try {
128405
- raw = JSON.parse(readFileSync26(opts.artifact, "utf8"));
128403
+ raw = JSON.parse(readFileSync25(opts.artifact, "utf8"));
128406
128404
  } catch (err) {
128407
128405
  throw new Error(`could not parse fresh analyze artifact at ${opts.artifact}: ${err.message}`);
128408
128406
  }
@@ -128433,7 +128431,7 @@ function buildProvenance(opts) {
128433
128431
  if (!(opts.baseline !== undefined && existsSync28(opts.baseline)))
128434
128432
  return null;
128435
128433
  try {
128436
- return semanticArtifactDigest(JSON.parse(readFileSync26(opts.baseline, "utf8")));
128434
+ return semanticArtifactDigest(JSON.parse(readFileSync25(opts.baseline, "utf8")));
128437
128435
  } catch (err) {
128438
128436
  throw new Error(`could not parse baseline artifact at ${opts.baseline}: ${err.message}`);
128439
128437
  }
@@ -128454,7 +128452,7 @@ function buildProvenance(opts) {
128454
128452
  }
128455
128453
  function probe(opts) {
128456
128454
  const current = buildProvenance(opts);
128457
- const cachedText = existsSync28(opts.target) ? readFileSync26(opts.target, "utf8") : null;
128455
+ const cachedText = existsSync28(opts.target) ? readFileSync25(opts.target, "utf8") : null;
128458
128456
  const cached = cachedText === null ? null : parseProvenance(cachedText);
128459
128457
  const decision = opts.mode === "ad-hoc" ? { disposition: "miss", reasons: ["ad-hoc-never-cached"] } : decideCache(cached, current, {
128460
128458
  recompute: opts.recompute,
@@ -128522,7 +128520,7 @@ function refreshReport(publishedMarkdown, validatedAt, disposition) {
128522
128520
  function publishAtomically(candidatePath, targetPath) {
128523
128521
  const tmpPath = `${targetPath}.tmp`;
128524
128522
  try {
128525
- writeFileSync17(tmpPath, readFileSync26(candidatePath));
128523
+ writeFileSync17(tmpPath, readFileSync25(candidatePath));
128526
128524
  const fd = openSync9(tmpPath, "r");
128527
128525
  try {
128528
128526
  fsyncSync2(fd);
@@ -128532,7 +128530,7 @@ function publishAtomically(candidatePath, targetPath) {
128532
128530
  renameSync9(tmpPath, targetPath);
128533
128531
  } catch (err) {
128534
128532
  try {
128535
- rmSync6(tmpPath, { force: true });
128533
+ rmSync7(tmpPath, { force: true });
128536
128534
  } catch {}
128537
128535
  throw err;
128538
128536
  }
@@ -128607,7 +128605,7 @@ var init_history_anatomy = __esm(() => {
128607
128605
  });
128608
128606
 
128609
128607
  // ../../packages/app/src/services/history-anatomy-cli.ts
128610
- import { existsSync as existsSync29, mkdirSync as mkdirSync21, readFileSync as readFileSync27, writeFileSync as writeFileSync18 } from "fs";
128608
+ import { existsSync as existsSync29, mkdirSync as mkdirSync21, readFileSync as readFileSync26, writeFileSync as writeFileSync18 } from "fs";
128611
128609
  import { homedir as homedir14 } from "os";
128612
128610
  import { join as join86 } from "path";
128613
128611
  function parseFlags(args) {
@@ -128654,7 +128652,7 @@ function runCacheCli(argv, deps = {}) {
128654
128652
  }
128655
128653
  let artifact;
128656
128654
  try {
128657
- artifact = JSON.parse(readFileSync27(a, "utf8"));
128655
+ artifact = JSON.parse(readFileSync26(a, "utf8"));
128658
128656
  } catch {
128659
128657
  return { exitCode: 1, stdout: "", stderr: `could not parse artifact at ${a}
128660
128658
  ` };
@@ -128668,7 +128666,7 @@ function runCacheCli(argv, deps = {}) {
128668
128666
  return { exitCode: 1, stdout: "", stderr: `usage: spur history anatomy check <report.md>
128669
128667
  ` };
128670
128668
  }
128671
- const result = checkReportStructure(readFileSync27(a, "utf8"));
128669
+ const result = checkReportStructure(readFileSync26(a, "utf8"));
128672
128670
  const stdout = `${result.ok ? "PASS" : "FAIL"}
128673
128671
  ${result.problems.map((p) => `- ${p}
128674
128672
  `).join("")}`;
@@ -128708,7 +128706,7 @@ ${result.problems.map((p) => `- ${p}
128708
128706
  return { exitCode: 0, stdout: "", stderr: `assert-clean: git unavailable; skipped
128709
128707
  ` };
128710
128708
  }
128711
- const undeclared = diffPorcelain(readFileSync27(f.baseline, "utf8"), now, expects);
128709
+ const undeclared = diffPorcelain(readFileSync26(f.baseline, "utf8"), now, expects);
128712
128710
  if (undeclared.length > 0) {
128713
128711
  return {
128714
128712
  exitCode: 1,
@@ -128819,8 +128817,8 @@ ${reasons}`, stderr: "" };
128819
128817
  };
128820
128818
  }
128821
128819
  try {
128822
- const p = JSON.parse(readFileSync27(f.provenance, "utf8"));
128823
- writeFileSync18(f.out, `${stampReport(readFileSync27(f.candidate, "utf8"), p)}
128820
+ const p = JSON.parse(readFileSync26(f.provenance, "utf8"));
128821
+ writeFileSync18(f.out, `${stampReport(readFileSync26(f.candidate, "utf8"), p)}
128824
128822
  `);
128825
128823
  } catch {
128826
128824
  return { exitCode: 1, stdout: "", stderr: `stamp: could not read candidate or provenance
@@ -128840,7 +128838,7 @@ ${reasons}`, stderr: "" };
128840
128838
  }
128841
128839
  try {
128842
128840
  const disposition = f.disposition ?? "hit";
128843
- const refreshed = refreshReport(readFileSync27(f.report, "utf8"), f["validated-at"] ?? new Date().toISOString(), disposition);
128841
+ const refreshed = refreshReport(readFileSync26(f.report, "utf8"), f["validated-at"] ?? new Date().toISOString(), disposition);
128844
128842
  writeFileSync18(f.out, refreshed.endsWith(`
128845
128843
  `) ? refreshed : `${refreshed}
128846
128844
  `);
@@ -129462,7 +129460,7 @@ var init_transcript = __esm(() => {
129462
129460
  });
129463
129461
 
129464
129462
  // ../../packages/app/src/services/session-timeline.ts
129465
- import { readFileSync as readFileSync28 } from "fs";
129463
+ import { readFileSync as readFileSync27 } from "fs";
129466
129464
  function parseGroups(spec, count) {
129467
129465
  let next = 1;
129468
129466
  return spec.split(",").map((part) => {
@@ -129556,7 +129554,7 @@ function runSessionTimelineCli(argv, io) {
129556
129554
  return 0;
129557
129555
  }
129558
129556
  try {
129559
- const lines = readFileSync28(resolved.path, "utf8").split(`
129557
+ const lines = readFileSync27(resolved.path, "utf8").split(`
129560
129558
  `);
129561
129559
  const timeline = buildTimeline(lines, group);
129562
129560
  if (timeline.segments.length === 0) {
@@ -129920,7 +129918,7 @@ workflow argument is missing.`;
129920
129918
  });
129921
129919
 
129922
129920
  // ../../packages/app/src/workflow/run-summary-render.ts
129923
- import { existsSync as existsSync33, mkdirSync as mkdirSync24, readdirSync as readdirSync17, readFileSync as readFileSync31, writeFileSync as writeFileSync21 } from "fs";
129921
+ import { existsSync as existsSync33, mkdirSync as mkdirSync24, readdirSync as readdirSync17, readFileSync as readFileSync30, writeFileSync as writeFileSync21 } from "fs";
129924
129922
  import { join as join105 } from "path";
129925
129923
  function renderSummaryMarkdown(summary) {
129926
129924
  const line = (r, bold = false) => {
@@ -130023,7 +130021,7 @@ function gateFromReceipt(cwd, runId) {
130023
130021
  continue;
130024
130022
  sawReceipt = true;
130025
130023
  try {
130026
- const parsed = JSON.parse(readFileSync31(path, "utf8"));
130024
+ const parsed = JSON.parse(readFileSync30(path, "utf8"));
130027
130025
  if (parsed.runId !== runId)
130028
130026
  continue;
130029
130027
  const ms = parsed.gateRuntimeMs;
@@ -130057,7 +130055,7 @@ var init_run_summary_render = __esm(() => {
130057
130055
  });
130058
130056
 
130059
130057
  // ../../packages/app/src/workflow/run-summary-cli.ts
130060
- import { readFileSync as readFileSync32 } from "fs";
130058
+ import { readFileSync as readFileSync31 } from "fs";
130061
130059
  function runRunSummaryCli(argv, io) {
130062
130060
  const { env, write, writeErr, projectsRoot } = io;
130063
130061
  let transcript;
@@ -130095,7 +130093,7 @@ function runRunSummaryCli(argv, io) {
130095
130093
  const inputs = rollupArgs.map((arg) => {
130096
130094
  const eq = arg.indexOf("=");
130097
130095
  const [label, file] = eq > 0 ? [arg.slice(0, eq), arg.slice(eq + 1)] : [undefined, arg];
130098
- const summary = JSON.parse(readFileSync32(file, "utf8"));
130096
+ const summary = JSON.parse(readFileSync31(file, "utf8"));
130099
130097
  return { label: label ?? summary.runId ?? file, summary };
130100
130098
  });
130101
130099
  write(`${renderRollupMarkdown(inputs)}
@@ -130123,9 +130121,9 @@ function runRunSummaryCli(argv, io) {
130123
130121
  const inputs = progressArgs.map((arg) => {
130124
130122
  const eq = arg.indexOf("=");
130125
130123
  const [label, file] = eq > 0 ? [arg.slice(0, eq), arg.slice(eq + 1)] : [undefined, arg];
130126
- return { label, progress: JSON.parse(readFileSync32(file, "utf8")) };
130124
+ return { label, progress: JSON.parse(readFileSync31(file, "utf8")) };
130127
130125
  });
130128
- const { rows, skippedLines } = parseTranscriptRows(resolved.ok ? readFileSync32(resolved.path, "utf8").split(`
130126
+ const { rows, skippedLines } = parseTranscriptRows(resolved.ok ? readFileSync31(resolved.path, "utf8").split(`
130129
130127
  `) : []);
130130
130128
  const summary = buildRunSummary(rows, inputs, { since, skippedLines });
130131
130129
  if (resolved.ok)
@@ -130181,7 +130179,7 @@ init_loader2();
130181
130179
  // ../../node_modules/.bun/@gobing-ai+ts-infra@0.5.28+c7a50b3e42c8753b/node_modules/@gobing-ai/ts-infra/dist/application-node.js
130182
130180
  init_dist4();
130183
130181
  init_dist5();
130184
- import { appendFileSync as appendFileSync5, mkdirSync as mkdirSync15, readFileSync as readFileSync16, statSync as statSync13 } from "fs";
130182
+ import { appendFileSync as appendFileSync5, mkdirSync as mkdirSync15, readFileSync as readFileSync15, statSync as statSync13 } from "fs";
130185
130183
  import { dirname as dirname35, join as join69 } from "path";
130186
130184
 
130187
130185
  // ../../node_modules/.bun/@gobing-ai+ts-infra@0.5.28+c7a50b3e42c8753b/node_modules/@gobing-ai/ts-infra/dist/application/index.js
@@ -133990,7 +133988,7 @@ async function runNodeApplication(options) {
133990
133988
  let loadedAppConfig;
133991
133989
  let yamlBootstrap = {};
133992
133990
  if (options.configLoader?.configFile) {
133993
- const fileContent = readFileSync16(options.configLoader.configFile, "utf-8");
133991
+ const fileContent = readFileSync15(options.configLoader.configFile, "utf-8");
133994
133992
  const loaded = loadYamlConfig(options.configLoader, fileContent);
133995
133993
  yamlBootstrap = loaded.bootstrapConfig;
133996
133994
  loadedAppConfig = loaded.appConfig;
@@ -138431,7 +138429,7 @@ import { join as join73 } from "path";
138431
138429
  init_src3();
138432
138430
  init_loader2();
138433
138431
  init_src2();
138434
- import { existsSync as existsSync21, readFileSync as readFileSync18 } from "fs";
138432
+ import { existsSync as existsSync21, readFileSync as readFileSync17 } from "fs";
138435
138433
  import { join as join72 } from "path";
138436
138434
  // schemas/section-matrix.schema.json
138437
138435
  var section_matrix_schema_default = {
@@ -139530,13 +139528,13 @@ function loadTemplateBodies(projectRoot, variant) {
139530
139528
  let bodies = {};
139531
139529
  const localPath = join72(projectRoot, ".spur", "tasks", "templates", `${variant}.md`);
139532
139530
  if (existsSync21(localPath)) {
139533
- bodies = extractTemplateBodies(readFileSync18(localPath, "utf8"));
139531
+ bodies = extractTemplateBodies(readFileSync17(localPath, "utf8"));
139534
139532
  } else {
139535
139533
  const root = bundledConfigRoot();
139536
139534
  if (root !== null) {
139537
139535
  const templatePath = join72(root, "templates", "task", `${variant}.md`);
139538
139536
  if (existsSync21(templatePath)) {
139539
- bodies = extractTemplateBodies(readFileSync18(templatePath, "utf8"));
139537
+ bodies = extractTemplateBodies(readFileSync17(templatePath, "utf8"));
139540
139538
  }
139541
139539
  }
139542
139540
  }
@@ -142249,7 +142247,7 @@ function registerAgentCommand(program, context) {
142249
142247
  // src/release-ops.ts
142250
142248
  init_src();
142251
142249
  init_dist5();
142252
- import { existsSync as existsSync26, readdirSync as readdirSync13, readFileSync as readFileSync24 } from "fs";
142250
+ import { existsSync as existsSync26, readdirSync as readdirSync13, readFileSync as readFileSync23 } from "fs";
142253
142251
  import { join as join83 } from "path";
142254
142252
 
142255
142253
  // ../../scripts/commands/bundle-plugins.ts
@@ -142447,7 +142445,7 @@ async function readJson2(path) {
142447
142445
  function rootWorkspaceGlobs(repoRoot) {
142448
142446
  let root;
142449
142447
  try {
142450
- root = JSON.parse(readFileSync24(join83(repoRoot, "package.json"), "utf8"));
142448
+ root = JSON.parse(readFileSync23(join83(repoRoot, "package.json"), "utf8"));
142451
142449
  } catch {
142452
142450
  return [];
142453
142451
  }
@@ -143067,7 +143065,7 @@ async function publishPackage(target, opts = {}) {
143067
143065
  // ../../packages/app/src/services/pr-review/index.ts
143068
143066
  init_src();
143069
143067
  import { spawnSync as spawnSync7 } from "child_process";
143070
- import { existsSync as existsSync27, readFileSync as readFileSync25, writeFileSync as writeFileSync16 } from "fs";
143068
+ import { existsSync as existsSync27, readFileSync as readFileSync24, writeFileSync as writeFileSync16 } from "fs";
143071
143069
  import { join as join84, resolve as resolve29 } from "path";
143072
143070
  function createSpawnRunner(context = {}) {
143073
143071
  return (cmd) => {
@@ -143214,7 +143212,7 @@ function hasCodeReviewRules(repoRoot) {
143214
143212
  const agentsPath = join84(repoRoot, "AGENTS.md");
143215
143213
  if (!existsSync27(agentsPath))
143216
143214
  return false;
143217
- return /^## Code Review Rules\s*$/m.test(readFileSync25(agentsPath, "utf8"));
143215
+ return /^## Code Review Rules\s*$/m.test(readFileSync24(agentsPath, "utf8"));
143218
143216
  }
143219
143217
  function buildRequestBody(rulesPresent, focus) {
143220
143218
  const base = rulesPresent ? "@codex review" : `@codex review ${DEFAULT_FOCUS}`;
@@ -145619,7 +145617,7 @@ import { createRequire as createRequire3 } from "module";
145619
145617
  var CLI_CONFIG = {
145620
145618
  binaryName: "spur",
145621
145619
  binaryLabel: "spur",
145622
- binaryVersion: "0.4.12",
145620
+ binaryVersion: "0.4.14",
145623
145621
  configDir: ".spur",
145624
145622
  configFile: ".spur/config.yaml",
145625
145623
  databaseFile: ".spur/spur.db"
@@ -155430,7 +155428,7 @@ init_dist5();
155430
155428
  import { homedir as homedir17 } from "os";
155431
155429
  import { basename as basename20, join as join94, relative as relative19, resolve as resolve36 } from "path";
155432
155430
  // ../server/package.json
155433
- var version3 = "0.4.12";
155431
+ var version3 = "0.4.14";
155434
155432
 
155435
155433
  // ../server/src/modules/health/index.ts
155436
155434
  var startedAt = Date.now();
@@ -160175,6 +160173,13 @@ function registerExecCommand(self2, context, options = {}) {
160175
160173
  `));
160176
160174
  return;
160177
160175
  }
160176
+ for (const step of plan) {
160177
+ const placeholder = /\{(?:files|paths|workspace)\}/.exec(step.command)?.[0];
160178
+ if (placeholder === undefined)
160179
+ continue;
160180
+ fail(`action "${step.name}" needs ${placeholder} from the light-gate planner; it cannot run via self exec`);
160181
+ return;
160182
+ }
160178
160183
  const executor = new NodeProcessExecutor2({ output: { mode: "stream", isTTY: true } });
160179
160184
  const results = [];
160180
160185
  let exitCode = 0;
@@ -160477,7 +160482,7 @@ function registerWorkflowCleanCommand(workflow, context) {
160477
160482
 
160478
160483
  // ../../packages/app/src/workflow/commit-guard.ts
160479
160484
  init_dist5();
160480
- import { mkdirSync as mkdirSync23, readFileSync as readFileSync29, renameSync as renameSync10, writeFileSync as writeFileSync20 } from "fs";
160485
+ import { mkdirSync as mkdirSync23, readFileSync as readFileSync28, renameSync as renameSync10, writeFileSync as writeFileSync20 } from "fs";
160481
160486
  import { join as join100 } from "path";
160482
160487
  var COMMIT_GUARD_USAGE = "usage: spur workflow commit-guard start --run <id> | stage --run <id> [--base <sha>] -- <paths\u2026> | check --run <id> [--base <sha>]";
160483
160488
  var isBookkeeping = (path) => path === ".spur" || path.startsWith(".spur/");
@@ -160537,7 +160542,7 @@ function changedSinceBase(base, git) {
160537
160542
  }
160538
160543
  function readTreeStart(run, options) {
160539
160544
  try {
160540
- const parsed = JSON.parse(readFileSync29(at2(options, artifactPath(run)), "utf8"));
160545
+ const parsed = JSON.parse(readFileSync28(at2(options, artifactPath(run)), "utf8"));
160541
160546
  if (typeof parsed.head === "string" && Array.isArray(parsed.dirty))
160542
160547
  return parsed;
160543
160548
  } catch {}
@@ -160551,7 +160556,7 @@ function runContext(run, options) {
160551
160556
  let base = options.base ?? "";
160552
160557
  if (!base) {
160553
160558
  try {
160554
- const anchored = readFileSync29(at2(options, join100(".spur", "run", `${run}-base.sha`)), "utf8").trim();
160559
+ const anchored = readFileSync28(at2(options, join100(".spur", "run", `${run}-base.sha`)), "utf8").trim();
160555
160560
  if (/^[0-9a-f]{7,40}$/i.test(anchored))
160556
160561
  base = anchored;
160557
160562
  } catch {}
@@ -160607,7 +160612,7 @@ function conflictingTarget(paths, options, git) {
160607
160612
  if (unmerged.has(path))
160608
160613
  return `${path} (unmerged)`;
160609
160614
  try {
160610
- if (conflictMarkerLine(readFileSync29(at2(options, path), "utf8")))
160615
+ if (conflictMarkerLine(readFileSync28(at2(options, path), "utf8")))
160611
160616
  return `${path} (conflict markers)`;
160612
160617
  } catch {}
160613
160618
  }
@@ -161305,7 +161310,7 @@ init_src3();
161305
161310
 
161306
161311
  // ../../packages/app/src/workflow/persist-check.ts
161307
161312
  init_dist5();
161308
- import { existsSync as existsSync32, readdirSync as readdirSync16, readFileSync as readFileSync30, statSync as statSync19 } from "fs";
161313
+ import { existsSync as existsSync32, readdirSync as readdirSync16, readFileSync as readFileSync29, statSync as statSync19 } from "fs";
161309
161314
  import { basename as basename22, dirname as dirname45, isAbsolute as isAbsolute17, join as join103, relative as relative20, resolve as resolve38 } from "path";
161310
161315
  var EVIDENCE_DIR = join103(".spur", "memory", "evidence");
161311
161316
  var RUN_DIR = join103(".spur", "run");
@@ -161374,7 +161379,7 @@ function compareTrees(wtRoot, invokeRoot, files, foreignDivergent = new Set) {
161374
161379
  continue;
161375
161380
  }
161376
161381
  try {
161377
- if (readFileSync30(join103(wtRoot, rel)).equals(readFileSync30(invPath)))
161382
+ if (readFileSync29(join103(wtRoot, rel)).equals(readFileSync29(invPath)))
161378
161383
  ok++;
161379
161384
  else
161380
161385
  divergent.push(rel);
@@ -161401,7 +161406,7 @@ function readPersistOutSkips(candidates) {
161401
161406
  if (!existsSync32(cand))
161402
161407
  continue;
161403
161408
  try {
161404
- const parsed = JSON.parse(readFileSync30(cand, "utf8"));
161409
+ const parsed = JSON.parse(readFileSync29(cand, "utf8"));
161405
161410
  if (Array.isArray(parsed.evidenceSkipped)) {
161406
161411
  for (const item of parsed.evidenceSkipped) {
161407
161412
  if (item.reason === "foreign-divergent" && typeof item.name === "string") {
@@ -161495,7 +161500,7 @@ function registerWorkflowPersistCommand(workflow, context) {
161495
161500
  if (from.trim() === "" || options.taskFile.some((taskFile) => taskFile.trim() === ""))
161496
161501
  return usage(persistOut);
161497
161502
  try {
161498
- const result = await runPersistOut({ from, taskFiles: options.taskFile });
161503
+ const result = await runPersistOut({ from, taskFiles: options.taskFile, to: context.cwd });
161499
161504
  for (const line of result.stdout)
161500
161505
  context.output.write(line);
161501
161506
  for (const line of result.stderr)
@@ -162195,7 +162200,7 @@ function registerWorkflowSummaryCommand(workflow, context) {
162195
162200
 
162196
162201
  // src/commands/workflow/trace.ts
162197
162202
  init_src3();
162198
- import { closeSync as closeSync10, fstatSync as fstatSync5, openSync as openSync10, readFileSync as readFileSync33, readSync as readSync3 } from "fs";
162203
+ import { closeSync as closeSync10, fstatSync as fstatSync5, openSync as openSync10, readFileSync as readFileSync32, readSync as readSync3 } from "fs";
162199
162204
  import { join as join106 } from "path";
162200
162205
  import { setTimeout as sleep7 } from "timers/promises";
162201
162206
  function formatTraceList2(result) {
@@ -162381,7 +162386,7 @@ async function followRunLog(service, runId, dir, pollMs, write, wait = (ms) => s
162381
162386
  offset = 0;
162382
162387
  if (carried !== "") {
162383
162388
  try {
162384
- const nextText = readFileSync33(target, "utf8");
162389
+ const nextText = readFileSync32(target, "utf8");
162385
162390
  if (nextText.startsWith(carried))
162386
162391
  offset = Buffer.byteLength(carried);
162387
162392
  } catch {