sentinelayer-cli 0.46.4 → 0.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/README.md +63 -3
  2. package/package.json +12 -7
  3. package/skills/omar-gate-setup/SKILL.md +103 -0
  4. package/skills/senti-agent-identity/SKILL.md +80 -0
  5. package/skills/senti-judge/SKILL.md +139 -0
  6. package/skills/senti-room-notifications/SKILL.md +73 -0
  7. package/skills/senti-room-notifications/listen-wrapper.cjs +150 -0
  8. package/skills/senti-room-notifications/package.json +1 -0
  9. package/skills/senti-room-notifications/test-classifier.js +109 -0
  10. package/skills/senti-room-notifications/test-receipt.js +54 -0
  11. package/skills/sentinelayer-cli/SKILL.md +222 -0
  12. package/src/agents/devtestbot/runner.js +22 -11
  13. package/src/agents/devtestbot/tool.js +24 -11
  14. package/src/agents/jules/fix-cycle.js +7 -1
  15. package/src/agents/jules/tools/runtime-audit.js +17 -12
  16. package/src/agents/shared-tools/dispatch-core.js +10 -5
  17. package/src/ai/aidenid.js +4 -2
  18. package/src/ai/domain-target-store.js +11 -5
  19. package/src/ai/identity-store.js +11 -5
  20. package/src/ai/site-store.js +8 -4
  21. package/src/audit/orchestrator.js +21 -14
  22. package/src/audit/package.js +6 -4
  23. package/src/audit/persona-loop.js +7 -0
  24. package/src/audit/replay.js +4 -2
  25. package/src/auth/gate.js +46 -8
  26. package/src/auth/service.js +34 -15
  27. package/src/auth/session-store.js +105 -47
  28. package/src/board/evidence-resolvers.js +3 -4
  29. package/src/cli.js +23 -3
  30. package/src/commands/ai/identity-lifecycle.js +30 -26
  31. package/src/commands/ai/provision-governance.js +32 -24
  32. package/src/commands/ai/shared.js +6 -4
  33. package/src/commands/audit.js +14 -12
  34. package/src/commands/auth.js +24 -4
  35. package/src/commands/board.js +2 -2
  36. package/src/commands/chat.js +8 -6
  37. package/src/commands/judge.js +125 -0
  38. package/src/commands/legacy-args.js +1 -0
  39. package/src/commands/mcp.js +45 -52
  40. package/src/commands/omargate.js +2 -1
  41. package/src/commands/plugin.js +11 -3
  42. package/src/commands/review.js +9 -4
  43. package/src/commands/scan.js +7 -7
  44. package/src/commands/session.js +607 -57
  45. package/src/commands/skills.js +45 -0
  46. package/src/commands/watch.js +9 -7
  47. package/src/config/output-filesystem.js +156 -0
  48. package/src/config/schema.js +4 -2
  49. package/src/config/service.js +40 -4
  50. package/src/cost/history.js +15 -9
  51. package/src/daemon/artifact-lineage.js +15 -12
  52. package/src/daemon/assignment-ledger.js +31 -25
  53. package/src/daemon/ast-drift.js +7 -3
  54. package/src/daemon/budget-governor.js +26 -20
  55. package/src/daemon/error-worker.js +28 -22
  56. package/src/daemon/fix-cycle.js +5 -4
  57. package/src/daemon/hybrid-mapper.js +19 -16
  58. package/src/daemon/ingest-refresh.js +4 -0
  59. package/src/daemon/jira-lifecycle.js +21 -17
  60. package/src/daemon/operator-control.js +18 -15
  61. package/src/daemon/reliability-lane.js +18 -15
  62. package/src/daemon/scope-engine.js +12 -8
  63. package/src/daemon/watchdog.js +15 -12
  64. package/src/engram/build-state.js +24 -3
  65. package/src/engram/capability-index.js +367 -0
  66. package/src/engram/compile-graph.js +1063 -0
  67. package/src/engram/contracts/action-receipt.js +82 -0
  68. package/src/engram/contracts/capability-index.js +259 -0
  69. package/src/engram/contracts/capability.js +377 -0
  70. package/src/engram/contracts/content-hash.js +8 -0
  71. package/src/engram/contracts/evidence.js +258 -0
  72. package/src/engram/contracts/graph.js +594 -0
  73. package/src/engram/contracts/identity.js +8 -0
  74. package/src/engram/contracts/index.js +149 -0
  75. package/src/engram/contracts/media-asset.js +85 -0
  76. package/src/engram/contracts/money.js +249 -0
  77. package/src/engram/contracts/source-snapshot.js +57 -0
  78. package/src/engram/contracts/task-context-pack.js +114 -0
  79. package/src/engram/contracts/temporal-segment.js +72 -0
  80. package/src/engram/contracts/versions.js +96 -0
  81. package/src/engram/extract-html.js +692 -0
  82. package/src/engram/graph-store.js +877 -0
  83. package/src/engram/index.js +98 -14
  84. package/src/engram/ingest-site.js +132 -0
  85. package/src/engram/live-state.js +1180 -0
  86. package/src/engram/namespace.js +34 -1
  87. package/src/engram/retrieve-site.js +753 -0
  88. package/src/engram/safe-reader.js +597 -0
  89. package/src/engram/source-intake.js +251 -0
  90. package/src/engram/store.js +227 -9
  91. package/src/ingest/engine.js +21 -28
  92. package/src/judge/verdict.js +305 -0
  93. package/src/legacy-cli.js +96 -19
  94. package/src/mcp/bridge-inputs.js +28 -4
  95. package/src/mcp/cli-command-tools.js +70 -0
  96. package/src/mcp/registry.js +17 -13
  97. package/src/mcp/session-stdio-server.js +297 -19
  98. package/src/memory/blackboard.js +5 -4
  99. package/src/net/gh-api-path.js +35 -9
  100. package/src/net/test-egress-guard.js +16 -4
  101. package/src/plugin/manifest.js +12 -8
  102. package/src/review/ai-review.js +110 -10
  103. package/src/review/investor-dd-devtestbot.js +17 -6
  104. package/src/review/investor-dd-orchestrator.js +21 -13
  105. package/src/review/local-review.js +26 -16
  106. package/src/review/omargate-cache.js +5 -5
  107. package/src/review/omargate-orchestrator.js +89 -17
  108. package/src/review/replay.js +9 -4
  109. package/src/review/report.js +22 -13
  110. package/src/review/typesafe-severity.js +201 -0
  111. package/src/session/admission-auth.js +34 -7
  112. package/src/session/admission.js +322 -71
  113. package/src/session/agent-access.js +137 -0
  114. package/src/session/agent-registry.js +2 -1
  115. package/src/session/attachment-summary.js +77 -0
  116. package/src/session/edit-summary.js +19 -0
  117. package/src/session/invitations.js +65 -1
  118. package/src/session/mentions.js +17 -9
  119. package/src/session/message-edits.js +5 -4
  120. package/src/session/preview.js +10 -2
  121. package/src/session/recap.js +3 -2
  122. package/src/session/room-files.js +519 -0
  123. package/src/session/route-classes.js +22 -8
  124. package/src/session/setup-guides.js +4 -0
  125. package/src/session/standup.js +192 -0
  126. package/src/session/sync.js +229 -10
  127. package/src/session/tickets.js +41 -0
  128. package/src/session/transcript.js +2 -1
  129. package/src/session/wake/resolve-target.js +7 -3
  130. package/src/skills/listener-runtime.cjs +92 -0
  131. package/src/skills/service.js +207 -0
  132. package/src/swarm/factory.js +5 -5
  133. package/src/swarm/pentest.js +9 -9
  134. package/src/swarm/report.js +4 -4
  135. package/src/swarm/runtime.js +15 -12
  136. package/src/swarm/scenario-dsl.js +7 -5
  137. package/src/telemetry/ledger.js +16 -7
@@ -1,6 +1,7 @@
1
1
  import fsp from "node:fs/promises";
2
2
  import path from "node:path";
3
3
 
4
+ import { bindOutputFileSystem, requireOutputFileSystem } from "../config/output-filesystem.js";
4
5
  import { ASSIGNMENT_STATUSES, resolveAssignmentLedgerStorage, listAssignments } from "./assignment-ledger.js";
5
6
  import { DAEMON_BUDGET_LIFECYCLE_STATES, listBudgetStates } from "./budget-governor.js";
6
7
  import { WORK_ITEM_STATUSES, listErrorQueue, resolveErrorDaemonStorage } from "./error-worker.js";
@@ -82,14 +83,16 @@ function loadJsonFile(filePath, defaultFactory) {
82
83
  });
83
84
  }
84
85
 
85
- async function writeJsonFile(filePath, payload = {}) {
86
- await fsp.mkdir(path.dirname(filePath), { recursive: true });
87
- await fsp.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
86
+ async function writeJsonFile(filePath, payload = {}, outputFs) {
87
+ requireOutputFileSystem(outputFs);
88
+ await outputFs.mkdir(path.dirname(filePath), { recursive: true });
89
+ await outputFs.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
88
90
  }
89
91
 
90
- async function appendJsonLine(filePath, payload = {}) {
91
- await fsp.mkdir(path.dirname(filePath), { recursive: true });
92
- await fsp.appendFile(filePath, `${JSON.stringify(payload)}\n`, "utf-8");
92
+ async function appendJsonLine(filePath, payload = {}, outputFs) {
93
+ requireOutputFileSystem(outputFs);
94
+ await outputFs.mkdir(path.dirname(filePath), { recursive: true });
95
+ await outputFs.appendFile(filePath, `${JSON.stringify(payload)}\n`, "utf-8");
93
96
  }
94
97
 
95
98
  function resolveBudgetHealthColor(budgetLifecycleState = "") {
@@ -271,12 +274,12 @@ export async function resolveOperatorControlStorage({
271
274
  env,
272
275
  homeDir,
273
276
  });
274
- return {
277
+ return bindOutputFileSystem({
275
278
  ...daemonStorage,
276
279
  operatorStatePath: path.join(daemonStorage.baseDir, "operator-control-state.json"),
277
280
  operatorEventsPath: path.join(daemonStorage.baseDir, "operator-events.ndjson"),
278
281
  operatorSnapshotsDir: path.join(daemonStorage.baseDir, "operator-snapshots"),
279
- };
282
+ }, daemonStorage.outputFs);
280
283
  }
281
284
 
282
285
  export async function buildOperatorControlSnapshot({
@@ -427,8 +430,8 @@ export async function buildOperatorControlSnapshot({
427
430
  workItems: rows,
428
431
  agentRoster,
429
432
  };
430
- await fsp.mkdir(storage.operatorSnapshotsDir, { recursive: true });
431
- await writeJsonFile(runPath, snapshot);
433
+ await storage.outputFs.mkdir(storage.operatorSnapshotsDir, { recursive: true });
434
+ await writeJsonFile(runPath, snapshot, storage.outputFs);
432
435
 
433
436
  const nextControlState = normalizeControlState(
434
437
  {
@@ -442,7 +445,7 @@ export async function buildOperatorControlSnapshot({
442
445
  normalizedNow
443
446
  );
444
447
  await Promise.all([
445
- writeJsonFile(storage.operatorStatePath, nextControlState),
448
+ writeJsonFile(storage.operatorStatePath, nextControlState, storage.outputFs),
446
449
  appendJsonLine(storage.operatorEventsPath, {
447
450
  timestamp: normalizedNow,
448
451
  eventType: "operator_snapshot",
@@ -452,7 +455,7 @@ export async function buildOperatorControlSnapshot({
452
455
  activeAgents: snapshot.summary.activeAgents,
453
456
  statusCounts: statusSummary.statusCounts,
454
457
  healthCounts: statusSummary.healthCounts,
455
- }),
458
+ }, storage.outputFs),
456
459
  ]);
457
460
 
458
461
  return {
@@ -606,8 +609,8 @@ export async function applyOperatorStopControl({
606
609
  }
607
610
 
608
611
  await Promise.all([
609
- writeJsonFile(storage.queuePath, queue),
610
- writeJsonFile(assignmentStorage.ledgerPath, ledger),
612
+ writeJsonFile(storage.queuePath, queue, storage.outputFs),
613
+ writeJsonFile(assignmentStorage.ledgerPath, ledger, assignmentStorage.outputFs),
611
614
  appendJsonLine(storage.operatorEventsPath, {
612
615
  timestamp: normalizedNow,
613
616
  eventType: "operator_stop",
@@ -618,7 +621,7 @@ export async function applyOperatorStopControl({
618
621
  reason: normalizedReason,
619
622
  jiraIssueKey,
620
623
  jiraCommented,
621
- }),
624
+ }, storage.outputFs),
622
625
  ]);
623
626
 
624
627
  return {
@@ -1,6 +1,7 @@
1
1
  import fsp from "node:fs/promises";
2
2
  import path from "node:path";
3
3
 
4
+ import { bindOutputFileSystem, requireOutputFileSystem } from "../config/output-filesystem.js";
4
5
  import { appendAdminErrorEvent, resolveErrorDaemonStorage, runErrorDaemonWorker } from "./error-worker.js";
5
6
 
6
7
  const RELIABILITY_SCHEMA_VERSION = "1.0.0";
@@ -142,14 +143,16 @@ async function readJsonFile(filePath, defaultFactory) {
142
143
  }
143
144
  }
144
145
 
145
- async function writeJsonFile(filePath, payload = {}) {
146
- await fsp.mkdir(path.dirname(filePath), { recursive: true });
147
- await fsp.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
146
+ async function writeJsonFile(filePath, payload = {}, outputFs) {
147
+ requireOutputFileSystem(outputFs);
148
+ await outputFs.mkdir(path.dirname(filePath), { recursive: true });
149
+ await outputFs.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
148
150
  }
149
151
 
150
- async function appendJsonLine(filePath, payload = {}) {
151
- await fsp.mkdir(path.dirname(filePath), { recursive: true });
152
- await fsp.appendFile(filePath, `${JSON.stringify(payload)}\n`, "utf-8");
152
+ async function appendJsonLine(filePath, payload = {}, outputFs) {
153
+ requireOutputFileSystem(outputFs);
154
+ await outputFs.mkdir(path.dirname(filePath), { recursive: true });
155
+ await outputFs.appendFile(filePath, `${JSON.stringify(payload)}\n`, "utf-8");
153
156
  }
154
157
 
155
158
  async function listRunArtifacts(runsDir, outputRoot) {
@@ -197,14 +200,14 @@ export async function resolveReliabilityLaneStorage({
197
200
  homeDir,
198
201
  });
199
202
  const reliabilityDir = path.join(daemonStorage.baseDir, "reliability");
200
- return {
203
+ return bindOutputFileSystem({
201
204
  ...daemonStorage,
202
205
  reliabilityDir,
203
206
  configPath: path.join(reliabilityDir, "lane-config.json"),
204
207
  billboardPath: path.join(reliabilityDir, "maintenance-billboard.json"),
205
208
  eventsPath: path.join(reliabilityDir, "reliability-events.ndjson"),
206
209
  runsDir: path.join(reliabilityDir, "runs"),
207
- };
210
+ }, daemonStorage.outputFs);
208
211
  }
209
212
 
210
213
  export async function setMaintenanceBillboard({
@@ -249,7 +252,7 @@ export async function setMaintenanceBillboard({
249
252
  };
250
253
 
251
254
  await Promise.all([
252
- writeJsonFile(storage.billboardPath, next),
255
+ writeJsonFile(storage.billboardPath, next, storage.outputFs),
253
256
  appendJsonLine(storage.eventsPath, {
254
257
  timestamp: normalizedNow,
255
258
  eventType: "maintenance_update",
@@ -258,7 +261,7 @@ export async function setMaintenanceBillboard({
258
261
  actor: normalizedActor,
259
262
  reason: normalizedReason,
260
263
  message: next.message,
261
- }),
264
+ }, storage.outputFs),
262
265
  ]);
263
266
 
264
267
  return {
@@ -439,11 +442,11 @@ export async function runReliabilityLane({
439
442
  : null,
440
443
  };
441
444
 
442
- await fsp.mkdir(storage.runsDir, { recursive: true });
445
+ await storage.outputFs.mkdir(storage.runsDir, { recursive: true });
443
446
  await Promise.all([
444
- writeJsonFile(storage.configPath, config),
445
- writeJsonFile(storage.billboardPath, billboard),
446
- writeJsonFile(runPath, runPayload),
447
+ writeJsonFile(storage.configPath, config, storage.outputFs),
448
+ writeJsonFile(storage.billboardPath, billboard, storage.outputFs),
449
+ writeJsonFile(runPath, runPayload, storage.outputFs),
447
450
  appendJsonLine(storage.eventsPath, {
448
451
  timestamp: normalizedNow,
449
452
  eventType: "reliability_run",
@@ -454,7 +457,7 @@ export async function runReliabilityLane({
454
457
  checkCount: checkResults.length,
455
458
  failureCount: failureResults.length,
456
459
  maintenanceEnabled: billboard.enabled,
457
- }),
460
+ }, storage.outputFs),
458
461
  ]);
459
462
 
460
463
  return {
@@ -3,7 +3,8 @@ import fsp from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import process from "node:process";
5
5
 
6
- import { resolveOutputRoot } from "../config/service.js";
6
+ import { bindOutputFileSystem, requireOutputFileSystem } from "../config/output-filesystem.js";
7
+ import { resolveOutputContext } from "../config/service.js";
7
8
  import { createAgentEvent } from "../events/schema.js";
8
9
  import { collectCodebaseIngest } from "../ingest/engine.js";
9
10
  import { appendToStream } from "../session/stream.js";
@@ -707,31 +708,33 @@ export async function resolveScopeEngineStorage({
707
708
  env,
708
709
  homeDir,
709
710
  } = {}) {
710
- const outputRoot = await resolveOutputRoot({
711
+ const { outputRoot, outputFs } = await resolveOutputContext({
711
712
  cwd: path.resolve(String(targetPath || ".")),
712
713
  outputDirOverride: outputDir,
713
714
  env,
714
715
  homeDir,
715
716
  });
717
+ requireOutputFileSystem(outputFs);
716
718
  const observabilityRoot = path.join(outputRoot, "observability");
717
719
  const baseDir = path.join(observabilityRoot, "scope-engine");
718
- return {
720
+ return bindOutputFileSystem({
719
721
  outputRoot,
720
722
  observabilityRoot,
721
723
  baseDir,
722
724
  runsDir: path.join(baseDir, "runs"),
723
- };
725
+ }, outputFs);
724
726
  }
725
727
 
726
728
  async function writeScopeEnvelopeArtifact(storage, workItemId, envelope, nowIso) {
729
+ requireOutputFileSystem(storage.outputFs);
727
730
  const artifactPath = path.join(
728
731
  storage.observabilityRoot,
729
732
  buildDayKey(nowIso),
730
733
  normalizeString(workItemId),
731
734
  "scope_envelope.json"
732
735
  );
733
- await fsp.mkdir(path.dirname(artifactPath), { recursive: true });
734
- await fsp.writeFile(artifactPath, `${JSON.stringify(envelope, null, 2)}\n`, "utf-8");
736
+ await storage.outputFs.mkdir(path.dirname(artifactPath), { recursive: true });
737
+ await storage.outputFs.writeFile(artifactPath, `${JSON.stringify(envelope, null, 2)}\n`, "utf-8");
735
738
  return artifactPath;
736
739
  }
737
740
 
@@ -740,9 +743,10 @@ function buildRunMetadataPath(storage, runId) {
740
743
  }
741
744
 
742
745
  async function persistRunMetadata(storage, payload) {
746
+ requireOutputFileSystem(storage.outputFs);
743
747
  const runPath = buildRunMetadataPath(storage, payload.runId);
744
- await fsp.mkdir(path.dirname(runPath), { recursive: true });
745
- await fsp.writeFile(runPath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
748
+ await storage.outputFs.mkdir(path.dirname(runPath), { recursive: true });
749
+ await storage.outputFs.writeFile(runPath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
746
750
  return runPath;
747
751
  }
748
752
 
@@ -1,6 +1,7 @@
1
1
  import fsp from "node:fs/promises";
2
2
  import path from "node:path";
3
3
 
4
+ import { bindOutputFileSystem, requireOutputFileSystem } from "../config/output-filesystem.js";
4
5
  import { loadConfig } from "../config/service.js";
5
6
  import { listAssignments, resolveAssignmentLedgerStorage } from "./assignment-ledger.js";
6
7
  import { listBudgetStates } from "./budget-governor.js";
@@ -302,14 +303,16 @@ async function readJsonFile(filePath, defaultFactory) {
302
303
  }
303
304
  }
304
305
 
305
- async function writeJsonFile(filePath, payload) {
306
- await fsp.mkdir(path.dirname(filePath), { recursive: true });
307
- await fsp.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
306
+ async function writeJsonFile(filePath, payload, outputFs) {
307
+ requireOutputFileSystem(outputFs);
308
+ await outputFs.mkdir(path.dirname(filePath), { recursive: true });
309
+ await outputFs.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
308
310
  }
309
311
 
310
- async function appendEvent(filePath, payload) {
311
- await fsp.mkdir(path.dirname(filePath), { recursive: true });
312
- await fsp.appendFile(filePath, `${JSON.stringify(payload)}\n`, "utf-8");
312
+ async function appendEvent(filePath, payload, outputFs) {
313
+ requireOutputFileSystem(outputFs);
314
+ await outputFs.mkdir(path.dirname(filePath), { recursive: true });
315
+ await outputFs.appendFile(filePath, `${JSON.stringify(payload)}\n`, "utf-8");
313
316
  }
314
317
 
315
318
  function buildRunId(nowIso, count) {
@@ -756,12 +759,12 @@ export async function resolveWatchdogStorage({
756
759
  env,
757
760
  homeDir,
758
761
  });
759
- return {
762
+ return bindOutputFileSystem({
760
763
  ...daemonStorage,
761
764
  watchdogStatePath: path.join(daemonStorage.baseDir, "watchdog-state.json"),
762
765
  watchdogEventsPath: path.join(daemonStorage.baseDir, "watchdog-events.ndjson"),
763
766
  watchdogRunsDir: path.join(daemonStorage.baseDir, "watchdog-runs"),
764
- };
767
+ }, daemonStorage.outputFs);
765
768
  }
766
769
 
767
770
  export async function runWatchdogTick({
@@ -879,7 +882,7 @@ export async function runWatchdogTick({
879
882
  normalizedNow
880
883
  );
881
884
 
882
- await fsp.mkdir(storage.watchdogRunsDir, { recursive: true });
885
+ await storage.outputFs.mkdir(storage.watchdogRunsDir, { recursive: true });
883
886
  const runId = nextState.lastRunId;
884
887
  const runPath = path.join(storage.watchdogRunsDir, `${runId}.json`);
885
888
  const runPayload = {
@@ -913,8 +916,8 @@ export async function runWatchdogTick({
913
916
  };
914
917
 
915
918
  await Promise.all([
916
- writeJsonFile(runPath, runPayload),
917
- writeJsonFile(storage.watchdogStatePath, nextState),
919
+ writeJsonFile(runPath, runPayload, storage.outputFs),
920
+ writeJsonFile(storage.watchdogStatePath, nextState, storage.outputFs),
918
921
  appendEvent(storage.watchdogEventsPath, {
919
922
  timestamp: normalizedNow,
920
923
  eventType: "watchdog_tick",
@@ -925,7 +928,7 @@ export async function runWatchdogTick({
925
928
  notificationCount: notifications.length,
926
929
  sentNotificationCount: notifications.filter((item) => item.sent).length,
927
930
  failedNotificationCount: notifications.filter((item) => !item.sent && !item.dryRun).length,
928
- }),
931
+ }, storage.outputFs),
929
932
  ]);
930
933
 
931
934
  return {
@@ -28,7 +28,7 @@
28
28
  import fsp from "node:fs/promises";
29
29
  import path from "node:path";
30
30
 
31
- import { namespaceDir } from "./store.js";
31
+ import { namespaceDir, ensureNamespace, resolveNamespaceDir, verifyNamespace } from "./store.js";
32
32
 
33
33
  export const BUILD_STATES = Object.freeze({
34
34
  ABSENT: "absent",
@@ -63,13 +63,32 @@ async function writeAtomic(file, value) {
63
63
  await fsp.rename(tmp, file);
64
64
  }
65
65
 
66
- export function createBuildState({ storeRoot } = {}) {
66
+ /**
67
+ * Resolve and verify the namespace directory for read-only build-state access.
68
+ * Uses resolveNamespaceDir (legacy migration) but NEVER creates a directory
69
+ * or sidecar on reads. Returns the dir path or null when absent.
70
+ */
71
+ async function resolveForRead(root, namespace, legacyLossyDirs) {
72
+ const dir = await resolveNamespaceDir(root, namespace, legacyLossyDirs);
73
+ try {
74
+ await fsp.access(dir);
75
+ } catch {
76
+ return null;
77
+ }
78
+ await verifyNamespace(dir, namespace);
79
+ return dir;
80
+ }
81
+
82
+ export function createBuildState({ storeRoot, legacyLossyDirs = "refuse" } = {}) {
67
83
  const root = path.resolve(String(storeRoot || "."));
68
84
 
69
85
  async function read(namespace) {
86
+ const dir = await resolveForRead(root, namespace, legacyLossyDirs);
87
+ if (!dir) return { state: BUILD_STATES.ABSENT };
88
+
70
89
  let raw;
71
90
  try {
72
- raw = await fsp.readFile(stateFile(root, namespace), "utf-8");
91
+ raw = await fsp.readFile(path.join(dir, STATE_FILE), "utf-8");
73
92
  } catch (error) {
74
93
  if (error && error.code === "ENOENT") return { state: BUILD_STATES.ABSENT };
75
94
  throw error;
@@ -106,6 +125,8 @@ export function createBuildState({ storeRoot } = {}) {
106
125
 
107
126
  /** Mark a build STARTED. Must be called before the first chunk is written. */
108
127
  async begin(namespace, { docDigest = null, expectedChunks = null, startedAt } = {}) {
128
+ const dir = await resolveNamespaceDir(root, namespace, legacyLossyDirs);
129
+ await ensureNamespace(dir, namespace);
109
130
  const record = {
110
131
  state: BUILD_STATES.BUILDING,
111
132
  namespace: namespace.raw,