instar 0.28.102 → 0.28.103

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 (247) hide show
  1. package/.claude/hooks/instar/before-prompt-recall.js +90 -0
  2. package/dist/cli.js +28 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/init.d.ts +1 -0
  5. package/dist/commands/init.d.ts.map +1 -1
  6. package/dist/commands/init.js +14 -1
  7. package/dist/commands/init.js.map +1 -1
  8. package/dist/commands/jobMigrate.d.ts +46 -0
  9. package/dist/commands/jobMigrate.d.ts.map +1 -0
  10. package/dist/commands/jobMigrate.js +292 -0
  11. package/dist/commands/jobMigrate.js.map +1 -0
  12. package/dist/commands/server.d.ts.map +1 -1
  13. package/dist/commands/server.js +88 -42
  14. package/dist/commands/server.js.map +1 -1
  15. package/dist/core/BackupManager.d.ts +27 -1
  16. package/dist/core/BackupManager.d.ts.map +1 -1
  17. package/dist/core/BackupManager.js +56 -1
  18. package/dist/core/BackupManager.js.map +1 -1
  19. package/dist/core/GitStateManager.d.ts +8 -0
  20. package/dist/core/GitStateManager.d.ts.map +1 -1
  21. package/dist/core/GitStateManager.js +31 -1
  22. package/dist/core/GitStateManager.js.map +1 -1
  23. package/dist/core/MigratorStepEngine.d.ts +170 -0
  24. package/dist/core/MigratorStepEngine.d.ts.map +1 -0
  25. package/dist/core/MigratorStepEngine.js +352 -0
  26. package/dist/core/MigratorStepEngine.js.map +1 -0
  27. package/dist/core/PostUpdateMigrator.d.ts +76 -0
  28. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  29. package/dist/core/PostUpdateMigrator.js +252 -1
  30. package/dist/core/PostUpdateMigrator.js.map +1 -1
  31. package/dist/core/PreCompactionFlush.d.ts +101 -0
  32. package/dist/core/PreCompactionFlush.d.ts.map +1 -0
  33. package/dist/core/PreCompactionFlush.js +282 -0
  34. package/dist/core/PreCompactionFlush.js.map +1 -0
  35. package/dist/core/PromptBuildRecall.d.ts +97 -0
  36. package/dist/core/PromptBuildRecall.d.ts.map +1 -0
  37. package/dist/core/PromptBuildRecall.js +152 -0
  38. package/dist/core/PromptBuildRecall.js.map +1 -0
  39. package/dist/core/selectIntelligenceProvider.d.ts +41 -0
  40. package/dist/core/selectIntelligenceProvider.d.ts.map +1 -0
  41. package/dist/core/selectIntelligenceProvider.js +89 -0
  42. package/dist/core/selectIntelligenceProvider.js.map +1 -0
  43. package/dist/lifeline/ServerSupervisor.d.ts +183 -0
  44. package/dist/lifeline/ServerSupervisor.d.ts.map +1 -1
  45. package/dist/lifeline/ServerSupervisor.js +443 -0
  46. package/dist/lifeline/ServerSupervisor.js.map +1 -1
  47. package/dist/memory/NativeModuleHealer.d.ts +89 -0
  48. package/dist/memory/NativeModuleHealer.d.ts.map +1 -1
  49. package/dist/memory/NativeModuleHealer.js +371 -1
  50. package/dist/memory/NativeModuleHealer.js.map +1 -1
  51. package/dist/memory/SemanticMemory.d.ts +131 -0
  52. package/dist/memory/SemanticMemory.d.ts.map +1 -1
  53. package/dist/memory/SemanticMemory.js +315 -0
  54. package/dist/memory/SemanticMemory.js.map +1 -1
  55. package/dist/messaging/DeliveryRetryManager.d.ts +60 -0
  56. package/dist/messaging/DeliveryRetryManager.d.ts.map +1 -1
  57. package/dist/messaging/DeliveryRetryManager.js +104 -0
  58. package/dist/messaging/DeliveryRetryManager.js.map +1 -1
  59. package/dist/monitoring/DegradationReporter.d.ts +164 -0
  60. package/dist/monitoring/DegradationReporter.d.ts.map +1 -1
  61. package/dist/monitoring/DegradationReporter.js +309 -1
  62. package/dist/monitoring/DegradationReporter.js.map +1 -1
  63. package/dist/monitoring/ErrorCodeExtractor.d.ts +69 -0
  64. package/dist/monitoring/ErrorCodeExtractor.d.ts.map +1 -0
  65. package/dist/monitoring/ErrorCodeExtractor.js +131 -0
  66. package/dist/monitoring/ErrorCodeExtractor.js.map +1 -0
  67. package/dist/monitoring/PromptGate.d.ts +34 -0
  68. package/dist/monitoring/PromptGate.d.ts.map +1 -1
  69. package/dist/monitoring/PromptGate.js +81 -1
  70. package/dist/monitoring/PromptGate.js.map +1 -1
  71. package/dist/monitoring/Redactor.d.ts +58 -0
  72. package/dist/monitoring/Redactor.d.ts.map +1 -0
  73. package/dist/monitoring/Redactor.js +143 -0
  74. package/dist/monitoring/Redactor.js.map +1 -0
  75. package/dist/monitoring/probes/LifelineProbe.d.ts +7 -0
  76. package/dist/monitoring/probes/LifelineProbe.d.ts.map +1 -1
  77. package/dist/monitoring/probes/LifelineProbe.js +7 -0
  78. package/dist/monitoring/probes/LifelineProbe.js.map +1 -1
  79. package/dist/monitoring/probes/__shared.d.ts +50 -0
  80. package/dist/monitoring/probes/__shared.d.ts.map +1 -0
  81. package/dist/monitoring/probes/__shared.js +51 -0
  82. package/dist/monitoring/probes/__shared.js.map +1 -0
  83. package/dist/remediation/IntentJournal.d.ts +64 -0
  84. package/dist/remediation/IntentJournal.d.ts.map +1 -0
  85. package/dist/remediation/IntentJournal.js +118 -0
  86. package/dist/remediation/IntentJournal.js.map +1 -0
  87. package/dist/remediation/MachineLock.d.ts +137 -0
  88. package/dist/remediation/MachineLock.d.ts.map +1 -0
  89. package/dist/remediation/MachineLock.js +314 -0
  90. package/dist/remediation/MachineLock.js.map +1 -0
  91. package/dist/remediation/PrimaryAggregatorLease.d.ts +205 -0
  92. package/dist/remediation/PrimaryAggregatorLease.d.ts.map +1 -0
  93. package/dist/remediation/PrimaryAggregatorLease.js +450 -0
  94. package/dist/remediation/PrimaryAggregatorLease.js.map +1 -0
  95. package/dist/remediation/RemediationContext.d.ts +63 -0
  96. package/dist/remediation/RemediationContext.d.ts.map +1 -0
  97. package/dist/remediation/RemediationContext.js +109 -0
  98. package/dist/remediation/RemediationContext.js.map +1 -0
  99. package/dist/remediation/RemediationKeyVault.d.ts +123 -0
  100. package/dist/remediation/RemediationKeyVault.d.ts.map +1 -0
  101. package/dist/remediation/RemediationKeyVault.js +564 -0
  102. package/dist/remediation/RemediationKeyVault.js.map +1 -0
  103. package/dist/remediation/Remediator.d.ts +288 -0
  104. package/dist/remediation/Remediator.d.ts.map +1 -0
  105. package/dist/remediation/Remediator.js +520 -0
  106. package/dist/remediation/Remediator.js.map +1 -0
  107. package/dist/remediation/RemediatorBootstrap.d.ts +141 -0
  108. package/dist/remediation/RemediatorBootstrap.d.ts.map +1 -0
  109. package/dist/remediation/RemediatorBootstrap.js +255 -0
  110. package/dist/remediation/RemediatorBootstrap.js.map +1 -0
  111. package/dist/remediation/TrustElevationSource.d.ts +153 -0
  112. package/dist/remediation/TrustElevationSource.d.ts.map +1 -0
  113. package/dist/remediation/TrustElevationSource.js +208 -0
  114. package/dist/remediation/TrustElevationSource.js.map +1 -0
  115. package/dist/remediation/audit/AuditProjection.d.ts +37 -0
  116. package/dist/remediation/audit/AuditProjection.d.ts.map +1 -0
  117. package/dist/remediation/audit/AuditProjection.js +118 -0
  118. package/dist/remediation/audit/AuditProjection.js.map +1 -0
  119. package/dist/remediation/audit/AuditWriter.d.ts +79 -0
  120. package/dist/remediation/audit/AuditWriter.d.ts.map +1 -0
  121. package/dist/remediation/audit/AuditWriter.js +132 -0
  122. package/dist/remediation/audit/AuditWriter.js.map +1 -0
  123. package/dist/remediation/channels/CliApprovalChannel.d.ts +37 -0
  124. package/dist/remediation/channels/CliApprovalChannel.d.ts.map +1 -0
  125. package/dist/remediation/channels/CliApprovalChannel.js +45 -0
  126. package/dist/remediation/channels/CliApprovalChannel.js.map +1 -0
  127. package/dist/remediation/channels/TelegramApprovalChannel.d.ts +37 -0
  128. package/dist/remediation/channels/TelegramApprovalChannel.d.ts.map +1 -0
  129. package/dist/remediation/channels/TelegramApprovalChannel.js +45 -0
  130. package/dist/remediation/channels/TelegramApprovalChannel.js.map +1 -0
  131. package/dist/remediation/runbooks/db-corruption.d.ts +86 -0
  132. package/dist/remediation/runbooks/db-corruption.d.ts.map +1 -0
  133. package/dist/remediation/runbooks/db-corruption.js +190 -0
  134. package/dist/remediation/runbooks/db-corruption.js.map +1 -0
  135. package/dist/remediation/runbooks/messaging-delivery-failed.d.ts +108 -0
  136. package/dist/remediation/runbooks/messaging-delivery-failed.d.ts.map +1 -0
  137. package/dist/remediation/runbooks/messaging-delivery-failed.js +172 -0
  138. package/dist/remediation/runbooks/messaging-delivery-failed.js.map +1 -0
  139. package/dist/remediation/runbooks/node-abi-mismatch.d.ts +70 -0
  140. package/dist/remediation/runbooks/node-abi-mismatch.d.ts.map +1 -0
  141. package/dist/remediation/runbooks/node-abi-mismatch.js +216 -0
  142. package/dist/remediation/runbooks/node-abi-mismatch.js.map +1 -0
  143. package/dist/remediation/runbooks/supervisor-preflight.d.ts +114 -0
  144. package/dist/remediation/runbooks/supervisor-preflight.d.ts.map +1 -0
  145. package/dist/remediation/runbooks/supervisor-preflight.js +299 -0
  146. package/dist/remediation/runbooks/supervisor-preflight.js.map +1 -0
  147. package/dist/scheduler/AgentMdAtomicSave.d.ts +85 -0
  148. package/dist/scheduler/AgentMdAtomicSave.d.ts.map +1 -0
  149. package/dist/scheduler/AgentMdAtomicSave.js +169 -0
  150. package/dist/scheduler/AgentMdAtomicSave.js.map +1 -0
  151. package/dist/scheduler/AgentMdReconcile.d.ts +62 -0
  152. package/dist/scheduler/AgentMdReconcile.d.ts.map +1 -0
  153. package/dist/scheduler/AgentMdReconcile.js +212 -0
  154. package/dist/scheduler/AgentMdReconcile.js.map +1 -0
  155. package/dist/scheduler/DisabledBodyDrift.d.ts +70 -0
  156. package/dist/scheduler/DisabledBodyDrift.d.ts.map +1 -0
  157. package/dist/scheduler/DisabledBodyDrift.js +149 -0
  158. package/dist/scheduler/DisabledBodyDrift.js.map +1 -0
  159. package/dist/scheduler/InstallBuiltinJobs.d.ts +53 -0
  160. package/dist/scheduler/InstallBuiltinJobs.d.ts.map +1 -0
  161. package/dist/scheduler/InstallBuiltinJobs.js +212 -0
  162. package/dist/scheduler/InstallBuiltinJobs.js.map +1 -0
  163. package/dist/scheduler/JobLoader.d.ts.map +1 -1
  164. package/dist/scheduler/JobLoader.js +48 -0
  165. package/dist/scheduler/JobLoader.js.map +1 -1
  166. package/dist/scheduler/MigrationInvariants.d.ts +62 -0
  167. package/dist/scheduler/MigrationInvariants.d.ts.map +1 -0
  168. package/dist/scheduler/MigrationInvariants.js +215 -0
  169. package/dist/scheduler/MigrationInvariants.js.map +1 -0
  170. package/dist/scheduler/MigrationLedger.d.ts +84 -0
  171. package/dist/scheduler/MigrationLedger.d.ts.map +1 -0
  172. package/dist/scheduler/MigrationLedger.js +114 -0
  173. package/dist/scheduler/MigrationLedger.js.map +1 -0
  174. package/dist/server/fileRoutes.d.ts.map +1 -1
  175. package/dist/server/fileRoutes.js +7 -0
  176. package/dist/server/fileRoutes.js.map +1 -1
  177. package/dist/server/routes.d.ts.map +1 -1
  178. package/dist/server/routes.js +141 -0
  179. package/dist/server/routes.js.map +1 -1
  180. package/package.json +3 -2
  181. package/scripts/classify-default-drift.mjs +238 -0
  182. package/scripts/eli16-overview-check.mjs +85 -0
  183. package/scripts/instar-dev-precommit.js +60 -1
  184. package/scripts/lint-degradation-emit-sites.js +116 -0
  185. package/scripts/lint-no-direct-destructive.js +12 -0
  186. package/scripts/protect-migration-guarantee.js +61 -0
  187. package/scripts/regen-default-job-templates.mjs +111 -0
  188. package/scripts/verify-runbook-pr-signature.js +366 -0
  189. package/src/data/builtin-manifest.json +93 -93
  190. package/src/scaffold/bootstrap.ts +138 -0
  191. package/src/scaffold/keys/instar-release-pub.pem +3 -0
  192. package/src/scaffold/templates/jobs/instar/.gitkeep +0 -0
  193. package/src/scaffold/templates/jobs/instar/commitment-detection.md +28 -0
  194. package/src/scaffold/templates/jobs/instar/evolution-overdue-check.md +28 -0
  195. package/src/scaffold/templates/jobs/instar/evolution-proposal-evaluate.md +30 -0
  196. package/src/scaffold/templates/jobs/instar/evolution-proposal-implement.md +25 -0
  197. package/src/scaffold/templates/jobs/instar/health-check.md +14 -0
  198. package/src/scaffold/templates/jobs/instar/identity-review.md +42 -0
  199. package/src/scaffold/templates/jobs/instar/insight-harvest.md +31 -0
  200. package/src/scaffold/templates/jobs/instar/overseer-development.md +23 -0
  201. package/src/scaffold/templates/jobs/instar/overseer-guardian.md +28 -0
  202. package/src/scaffold/templates/jobs/instar/overseer-infrastructure.md +26 -0
  203. package/src/scaffold/templates/jobs/instar/overseer-learning.md +28 -0
  204. package/src/scaffold/templates/jobs/instar/overseer-maintenance.md +27 -0
  205. package/src/scaffold/templates/jobs/instar/reflection-trigger.md +40 -0
  206. package/src/scaffold/templates/jobs/instar/relationship-maintenance.md +16 -0
  207. package/src/scaffold/templates.ts +1616 -0
  208. package/upgrades/0.28.102.md +6 -7
  209. package/upgrades/0.28.103.md +471 -0
  210. package/upgrades/side-effects/a47-primary-aggregator-lease.md +82 -0
  211. package/upgrades/side-effects/agentmd-atomic-save.md +66 -0
  212. package/upgrades/side-effects/agentmd-loader-200jobs-perf-benchmark.md +50 -0
  213. package/upgrades/side-effects/agentmd-reconcile-boot-lifecycle.md +66 -0
  214. package/upgrades/side-effects/api-safety-guard.md +80 -0
  215. package/upgrades/side-effects/c1-runbook-pr-gate.md +83 -0
  216. package/upgrades/side-effects/disabled-body-drift-detection.md +59 -0
  217. package/upgrades/side-effects/drift-classifier-release-time.md +71 -0
  218. package/upgrades/side-effects/eli16-overview-required-gate.md +77 -0
  219. package/upgrades/side-effects/f1-remediation-key-vault.md +195 -0
  220. package/upgrades/side-effects/f2-redactor-errorcode-extractor.md +130 -0
  221. package/upgrades/side-effects/f3-degradation-reporter-shim.md +105 -0
  222. package/upgrades/side-effects/f4-machine-locks-intent-journal-audit.md +117 -0
  223. package/upgrades/side-effects/f5-trust-elevation-source.md +258 -0
  224. package/upgrades/side-effects/f6-supervisor-handshake.md +234 -0
  225. package/upgrades/side-effects/f7-post-update-migrator-atomic-step.md +129 -0
  226. package/upgrades/side-effects/f8-remediator-skeleton.md +153 -0
  227. package/upgrades/side-effects/f8-rest-enforcement.md +72 -0
  228. package/upgrades/side-effects/file-viewer-jobs-instar-never-editable.md +41 -0
  229. package/upgrades/side-effects/frontmatter-interpolation-breakout-test.md +48 -0
  230. package/upgrades/side-effects/jobs-as-agentmd-phase-2.md +78 -0
  231. package/upgrades/side-effects/jobs-as-agentmd-phase-3.md +76 -0
  232. package/upgrades/side-effects/jobs-as-agentmd-phase-4-endpoints.md +62 -0
  233. package/upgrades/side-effects/jobs-as-agentmd-phase-5.md +67 -0
  234. package/upgrades/side-effects/jobs-as-agentmd-phase-6.md +53 -0
  235. package/upgrades/side-effects/jobs-endpoints-auth-verification.md +52 -0
  236. package/upgrades/side-effects/migration-guarantee-suite.md +61 -0
  237. package/upgrades/side-effects/migration-runtime-invariant-gate.md +59 -0
  238. package/upgrades/side-effects/migration-telemetry-invariant-8.md +68 -0
  239. package/upgrades/side-effects/npm-pack-templates-smoke.md +51 -0
  240. package/upgrades/side-effects/openclaw-import-before-prompt-build.md +84 -0
  241. package/upgrades/side-effects/openclaw-import-pre-compaction-flush.md +85 -0
  242. package/upgrades/side-effects/prompt-gate-no-prompt-cache.md +258 -0
  243. package/upgrades/side-effects/tier2-degradation-reporter-live-wire.md +146 -0
  244. package/upgrades/side-effects/w1-node-abi-mismatch-runbook.md +154 -0
  245. package/upgrades/side-effects/w2-supervisor-preflight-runbook.md +176 -0
  246. package/upgrades/side-effects/w3-messaging-delivery-failed-runbook.md +185 -0
  247. package/upgrades/side-effects/w4-db-corruption-runbook.md +114 -0
@@ -0,0 +1,299 @@
1
+ /**
2
+ * supervisor-preflight — the W-2 `ApprovedRunbook` wrapping
3
+ * `ServerSupervisor.preflightSelfHeal` per spec A34 (composed-multi-step
4
+ * surface alignment).
5
+ *
6
+ * SELF-HEALING-REMEDIATOR-V2-SPEC §A6 (errorCode provenance — structured
7
+ * sources only), §A9 (verify asserts durability not just liveness), §A21
8
+ * (verify probe error → verify-inconclusive, distinct from verify-failed),
9
+ * §A34 (W-2 is ONE runbook composing six heal steps; verify produces a
10
+ * SINGLE durable-state check after all steps), §A36 (essential requires
11
+ * blastRadius='machine'), §A57 Tier-2 (W-2 ships after F-5, F-6, F-7,
12
+ * F-8-rest are on main).
13
+ *
14
+ * Surface: `ServerSupervisor.invokeFromRemediator(ctx)` (added by W-2).
15
+ * The legacy in-line `preflightSelfHeal()` call inside `spawnServer()`
16
+ * stays unchanged as the boot-path safety net; this runbook is the
17
+ * parallel, Remediator-orchestrated path.
18
+ *
19
+ * Match contract:
20
+ * eventPrefilter.errorCode = ['BIND_FAILURE', 'CRASH_LOOP',
21
+ * 'SUPERVISOR_DEGRADED']
22
+ * eventPrefilter.provenance = ['native-binding', 'subsystem-explicit']
23
+ *
24
+ * `'free-text'` is intentionally NOT in the prefilter — §A6 mandates that
25
+ * runbooks only fire on structured-provenance events. The match() callback
26
+ * narrows further to lifeline/server subsystems so a same-errorCode event
27
+ * about an unrelated subsystem (e.g., memory) routes to no-matching-runbook.
28
+ *
29
+ * Verify (§A9):
30
+ * The verify step asserts the durable lifeline state.json marker exists
31
+ * and was written recently (post-restart). Clean marker → verified-healthy.
32
+ * Missing or corrupt marker → verify-failed. Probe path error (filesystem
33
+ * I/O throws) → verify-inconclusive per §A21 — distinct from verify-failed.
34
+ *
35
+ * essential:
36
+ * This runbook is `essential: true`. A wedged supervisor (bind-failure
37
+ * crash loop, shadow-install missing, better-sqlite3 ABI mismatch,
38
+ * merged-conflict settings.json) DoSes every server-mediated capability:
39
+ * Telegram relay, dashboard, jobs scheduler, threadline. Without the
40
+ * heal, the agent is offline. blastRadius is 'machine' (preflight
41
+ * touches shadow-install/node_modules, the node symlink, the git tree,
42
+ * and the lifeline state directory), which satisfies §A36's validator.
43
+ *
44
+ * A15 partial-upgrade rule (Tier-2 build-acceleration carve-out):
45
+ * F-6 (the supervisor handshake the W-2 runbook depends on) merged on
46
+ * 2026-05-13. The 7-day lag rule applies to PRODUCTION CUTOVER — turning
47
+ * the runbook live for end-users — not to the BUILD of the runbook code
48
+ * itself. The wrapper is constructible and unit-testable today; live
49
+ * activation gates on the separate `wrappers-active-after` config flag
50
+ * (defaults to false) and is the Tier-3 wiring PR's concern.
51
+ */
52
+ import path from 'node:path';
53
+ import fs from 'node:fs';
54
+ import { markerPath } from '../../lifeline/startupMarker.js';
55
+ // ── Verify probe ─────────────────────────────────────────────────────────
56
+ /**
57
+ * §A9 durable-state assertion for the supervisor-preflight runbook. The
58
+ * preflight heals six prerequisites:
59
+ *
60
+ * 1. shadow-install
61
+ * 2. node symlink
62
+ * 3. stuck git rebase
63
+ * 4. better-sqlite3 ABI mismatch
64
+ * 5. stale lifeline lock
65
+ * 6. settings.json merge-conflict
66
+ *
67
+ * Verifying every step in isolation would multiply the verify surface six-
68
+ * fold and re-implement detection logic the preflight body already owns.
69
+ * Per §A34, the verify produces a SINGLE durable-state check: did the
70
+ * lifeline re-spawn cleanly after the heal? The signal we read is the
71
+ * `lifeline-started-at.json` startup marker — written unconditionally
72
+ * by every lifeline startup, including the post-preflight one.
73
+ *
74
+ * Returns:
75
+ * - kind: 'ok' — marker present, well-formed, fresh-enough
76
+ * - kind: 'failed' — marker missing or corrupt (post-preflight
77
+ * spawn did NOT happen → heal did not durably
78
+ * recover the supervisor)
79
+ * - kind: 'inconclusive' — filesystem probe threw (cannot determine
80
+ * durability one way or the other → §A21)
81
+ */
82
+ export function verifyLifelineDurable(stateDir, options = {}) {
83
+ const maxAgeMs = options.maxAgeMs ?? 10 * 60_000; // 10 min default
84
+ const now = options.nowMs ?? Date.now();
85
+ try {
86
+ const p = markerPath(stateDir);
87
+ let raw;
88
+ try {
89
+ raw = fs.readFileSync(p, 'utf-8');
90
+ }
91
+ catch (err) {
92
+ const code = err.code;
93
+ if (code === 'ENOENT') {
94
+ return {
95
+ kind: 'failed',
96
+ reason: `lifeline startup marker missing at ${p}`,
97
+ };
98
+ }
99
+ // EACCES, EIO, etc — probe error, not durability failure.
100
+ return {
101
+ kind: 'inconclusive',
102
+ reason: `marker readFileSync threw (${code ?? 'unknown'}): ${err.message}`,
103
+ };
104
+ }
105
+ let parsed;
106
+ try {
107
+ parsed = JSON.parse(raw);
108
+ }
109
+ catch (err) {
110
+ return {
111
+ kind: 'failed',
112
+ reason: `marker JSON parse failed: ${err.message.slice(0, 200)}`,
113
+ };
114
+ }
115
+ if (!parsed ||
116
+ typeof parsed !== 'object' ||
117
+ typeof parsed.startedAt !== 'string' ||
118
+ typeof parsed.pid !== 'number' ||
119
+ typeof parsed.version !== 'string') {
120
+ return {
121
+ kind: 'failed',
122
+ reason: 'marker shape invalid (missing startedAt/pid/version)',
123
+ };
124
+ }
125
+ const startedAtMs = Date.parse(parsed.startedAt);
126
+ if (!Number.isFinite(startedAtMs)) {
127
+ return {
128
+ kind: 'failed',
129
+ reason: `marker startedAt is unparseable: ${parsed.startedAt}`,
130
+ };
131
+ }
132
+ const ageMs = now - startedAtMs;
133
+ if (ageMs > maxAgeMs) {
134
+ return {
135
+ kind: 'failed',
136
+ reason: `marker is stale (ageMs=${ageMs} > maxAgeMs=${maxAgeMs}) — lifeline did not respawn post-heal`,
137
+ };
138
+ }
139
+ // Negative age = clock skew or marker from the future. Treat as probe
140
+ // error (§A21) rather than failure — we cannot confidently say durability
141
+ // is broken when the clocks disagree.
142
+ if (ageMs < -60_000) {
143
+ return {
144
+ kind: 'inconclusive',
145
+ reason: `marker startedAt is in the future (ageMs=${ageMs}) — clock-skew probe error`,
146
+ };
147
+ }
148
+ return { kind: 'ok', markerStartedAt: parsed.startedAt };
149
+ }
150
+ catch (err) {
151
+ return {
152
+ kind: 'inconclusive',
153
+ reason: `verify wrapper threw: ${err instanceof Error ? err.message : String(err)}`,
154
+ };
155
+ }
156
+ }
157
+ let _verifyImpl = null;
158
+ export function _setVerifyImplForTesting(fn) {
159
+ _verifyImpl = fn;
160
+ }
161
+ /**
162
+ * Allow tests to inject a stub supervisor so the runbook can run end-to-end
163
+ * without spawning a real tmux session or touching the on-disk shadow-install.
164
+ */
165
+ let _supervisorForTesting = null;
166
+ export function _setSupervisorForTesting(s) {
167
+ _supervisorForTesting = s;
168
+ }
169
+ /**
170
+ * The state directory the verify step reads the lifeline marker from. Tests
171
+ * inject a tmpdir; production callers configure this at runbook-registration
172
+ * via a closure (the runbook factory) — for now, allow override.
173
+ */
174
+ let _stateDirForVerify = null;
175
+ export function _setStateDirForVerify(dir) {
176
+ _stateDirForVerify = dir;
177
+ }
178
+ // ── Match helpers ────────────────────────────────────────────────────────
179
+ const SUPERVISOR_SUBSYSTEMS = new Set([
180
+ 'lifeline',
181
+ 'server',
182
+ 'supervisor',
183
+ // Legacy emit-sites tag the server subsystem as 'http' in some places.
184
+ // Match() narrows to those by inspecting reason text.
185
+ ]);
186
+ const SUPERVISOR_ERROR_CODES = ['BIND_FAILURE', 'CRASH_LOOP', 'SUPERVISOR_DEGRADED'];
187
+ // ── ApprovedRunbook ──────────────────────────────────────────────────────
188
+ export const supervisorPreflightRunbook = {
189
+ id: 'supervisor-preflight',
190
+ // Lower than W-1 (node-abi-mismatch=100). When a BIND_FAILURE event also
191
+ // carries a NATIVE_MODULE_ABI_MISMATCH errorCode (rare but possible during
192
+ // crash-loop diagnosis), W-1's runbook is the right surface to dispatch
193
+ // because it's the precise heal. supervisor-preflight is the broad heal
194
+ // for the bind-failure root-cause class.
195
+ priority: 90,
196
+ surface: 'supervisor',
197
+ eventPrefilter: {
198
+ errorCode: SUPERVISOR_ERROR_CODES,
199
+ // §A6: NOT free-text. Only structured-provenance events fire this runbook.
200
+ provenance: ['native-binding', 'subsystem-explicit'],
201
+ },
202
+ match: (event) => {
203
+ // Narrow to lifeline/server subsystems. Defence-in-depth: a BIND_FAILURE
204
+ // event for an unrelated subsystem (e.g., a future memory-bind-failure)
205
+ // should NOT trigger a supervisor preflight.
206
+ if (SUPERVISOR_SUBSYSTEMS.has(event.subsystem))
207
+ return true;
208
+ // Legacy free-text subsystems may use 'http' or 'tunnel' — inspect the
209
+ // reason for an explicit supervisor/server/lifeline mention.
210
+ const reasonText = event.reason.full ?? event.reason.redacted ?? '';
211
+ return /\b(server|lifeline|supervisor)\b/i.test(reasonText);
212
+ },
213
+ preconditions: async (_event) => {
214
+ // Cheap guard: stateDir must be set + accessible (the supervisor cannot
215
+ // heal without somewhere to read/write shadow-install).
216
+ if (!_stateDirForVerify)
217
+ return true; // Production callers wire the dir
218
+ try {
219
+ fs.accessSync(_stateDirForVerify, fs.constants.R_OK | fs.constants.W_OK);
220
+ return true;
221
+ }
222
+ catch {
223
+ return false;
224
+ }
225
+ },
226
+ surfaceCallable: async (ctx) => {
227
+ const supervisor = _supervisorForTesting;
228
+ if (!supervisor) {
229
+ // Production callers must wire a supervisor instance via the runbook
230
+ // factory pattern. Until that lands (Tier-3 dispatcher wiring), the
231
+ // surfaceCallable returns a failure that surfaces in the audit
232
+ // projection as `surfaceCallable returned outcome=failure`.
233
+ return {
234
+ outcome: 'failure',
235
+ details: {
236
+ reason: 'no-supervisor-wired',
237
+ attemptId: ctx.attemptId,
238
+ },
239
+ };
240
+ }
241
+ const result = await supervisor.invokeFromRemediator(ctx);
242
+ // The Remediator's `ExecutionResult` type matches the supervisor's
243
+ // `SupervisorRemediatorExecutionResult` shape; pass through verbatim.
244
+ return result;
245
+ },
246
+ verify: async (ctx) => {
247
+ const impl = _verifyImpl;
248
+ let probe;
249
+ if (impl) {
250
+ probe = impl(ctx);
251
+ }
252
+ else if (_stateDirForVerify) {
253
+ probe = verifyLifelineDurable(_stateDirForVerify);
254
+ }
255
+ else {
256
+ probe = {
257
+ kind: 'inconclusive',
258
+ reason: 'no stateDir wired for verify — cannot read lifeline marker',
259
+ };
260
+ }
261
+ switch (probe.kind) {
262
+ case 'ok':
263
+ return {
264
+ outcome: 'verified-healthy',
265
+ reason: `lifeline marker present (startedAt=${probe.markerStartedAt}); preflight durable`,
266
+ };
267
+ case 'failed':
268
+ return { outcome: 'verify-failed', reason: probe.reason };
269
+ case 'inconclusive':
270
+ return { outcome: 'verify-inconclusive', reason: probe.reason };
271
+ }
272
+ },
273
+ blastRadius: 'machine',
274
+ reversibility: 'reversible',
275
+ // 3 minutes. Cold-cache shadow-install reinstall + better-sqlite3 rebuild
276
+ // can both run in one preflight cycle; each takes ~30-60s on slow hardware.
277
+ // The deadline race in F-8 will abort with `aborted-deadline` if exceeded.
278
+ expectedRuntimeMs: 180_000,
279
+ // §A36: essential requires blastRadius='machine' (satisfied above). A
280
+ // wedged supervisor DoSes every server-mediated capability, so this is the
281
+ // canonical essential runbook for the supervisor surface.
282
+ essential: true,
283
+ };
284
+ // Re-export the path helper so consumers can compute the marker location
285
+ // without re-importing from `src/lifeline/startupMarker.ts`.
286
+ export { markerPath as supervisorMarkerPath };
287
+ // For tests/observability — list the durable state assertions.
288
+ export const VERIFIED_HEAL_TARGETS = [
289
+ 'shadow-install',
290
+ 'node-symlink',
291
+ 'git-rebase',
292
+ 'better-sqlite3-abi',
293
+ 'stale-lifeline-lock',
294
+ 'settings-json',
295
+ ];
296
+ /** No-op compile-time use to keep `path` import alive for future helpers. */
297
+ const _ = path;
298
+ void _;
299
+ //# sourceMappingURL=supervisor-preflight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supervisor-preflight.js","sourceRoot":"","sources":["../../../src/remediation/runbooks/supervisor-preflight.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * AgentMdAtomicSave — race-safe two-rename commit for agentmd job edits.
3
+ *
4
+ * Per INSTAR-JOBS-AS-AGENTMD spec §Design Principles "Override = fork;
5
+ * race-safe two-rename commit":
6
+ *
7
+ * The save sequence is md-first, manifest-last:
8
+ * 1. Write `<file>.md.new` (staged body).
9
+ * 2. Write `<schedule>.json.new` (staged manifest).
10
+ * 3. rename(<file>.md.new → <file>.md) ← rename A
11
+ * 4. rename(<schedule>.json.new → <schedule>.json) ← rename B
12
+ *
13
+ * SIGKILL between rename A and rename B leaves a consistent state: the
14
+ * new body is on disk paired with the OLD manifest. The loader handles
15
+ * this gracefully — it loads the new body and the old manifest's
16
+ * schedule, which is a strictly-progressed state.
17
+ *
18
+ * SIGKILL before rename A is also consistent: the old body + old
19
+ * manifest are intact; the `.new` staged files are orphaned and the next
20
+ * boot's reconcile() can either reapply or discard them.
21
+ *
22
+ * Re-applying a half-committed save is idempotent because each rename is
23
+ * atomic at the filesystem level and overwrites any pre-existing target.
24
+ *
25
+ * This module is the canonical save helper for both Dashboard edits and
26
+ * future CLI edit commands. It is NOT yet wired into a UI consumer — the
27
+ * Phase 4 Dashboard UI rewrite is the consumer. Shipping the helper
28
+ * separately so the spec's atomicity guarantee has a tested
29
+ * implementation regardless of when the UI lands.
30
+ */
31
+ export interface AtomicSaveInput {
32
+ /** Final on-disk path of the .md body (e.g., `.instar/jobs/user/<slug>.md`). */
33
+ mdPath: string;
34
+ /** Final on-disk path of the per-slug manifest (e.g.,
35
+ * `.instar/jobs/schedule/<slug>.json`). */
36
+ manifestPath: string;
37
+ /** Bytes to write to mdPath. Caller is responsible for any normalization. */
38
+ mdBody: string;
39
+ /** Object to JSON-serialize into manifestPath. */
40
+ manifest: Record<string, unknown>;
41
+ }
42
+ export interface AtomicSaveResult {
43
+ ok: true;
44
+ mdWritten: string;
45
+ manifestWritten: string;
46
+ }
47
+ export interface AtomicSaveFailure {
48
+ ok: false;
49
+ stage: 'stage-md' | 'stage-manifest' | 'commit-md' | 'commit-manifest';
50
+ reason: string;
51
+ partial: {
52
+ mdNewExists: boolean;
53
+ manifestNewExists: boolean;
54
+ mdCommitted: boolean;
55
+ manifestCommitted: boolean;
56
+ };
57
+ }
58
+ /**
59
+ * Two-rename atomic save. Writes the body first, then the manifest. Each
60
+ * write goes to a `.new` sibling, then renames atomically over the final
61
+ * path. On failure at any stage, returns a structured `AtomicSaveFailure`
62
+ * the caller can use to drive recovery / Issues-card surface.
63
+ *
64
+ * Implementation notes:
65
+ * - We DO NOT delete the staged `.new` files on failure — leaving them
66
+ * in place gives reconcile() something to reason about. The next
67
+ * successful save will overwrite them.
68
+ * - `fs.renameSync` is atomic on POSIX filesystems for paths on the
69
+ * same filesystem (always the case here — both live under .instar/).
70
+ * - `fs.writeFileSync` with `flag: 'w'` truncates; we use it
71
+ * deliberately so a half-written `.new` on retry is replaced.
72
+ */
73
+ export declare function atomicSaveAgentMdJob(input: AtomicSaveInput): AtomicSaveResult | AtomicSaveFailure;
74
+ /**
75
+ * Discover any `.new` staged files left over from a crashed save. Used by
76
+ * reconcile() to surface "interrupted save" Issues-card rows.
77
+ */
78
+ export declare function listStagedNewFiles(jobsRootDir: string): string[];
79
+ /**
80
+ * Clean up an orphaned `.new` file. Used by reconcile() when the operator
81
+ * chooses "Discard staged changes" in the Issues-card UI. Routes through
82
+ * SafeFsExecutor per the destructive-tool funnel.
83
+ */
84
+ export declare function discardStagedFile(stagedPath: string): void;
85
+ //# sourceMappingURL=AgentMdAtomicSave.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentMdAtomicSave.d.ts","sourceRoot":"","sources":["../../src/scheduler/AgentMdAtomicSave.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAMH,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;IACf;gDAC4C;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,MAAM,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,IAAI,CAAC;IACT,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,UAAU,GAAG,gBAAgB,GAAG,WAAW,GAAG,iBAAiB,CAAC;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE;QACP,WAAW,EAAE,OAAO,CAAC;QACrB,iBAAiB,EAAE,OAAO,CAAC;QAC3B,WAAW,EAAE,OAAO,CAAC;QACrB,iBAAiB,EAAE,OAAO,CAAC;KAC5B,CAAC;CACH;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,GAAG,iBAAiB,CAiFjG;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAQhE;AAiBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAE1D"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * AgentMdAtomicSave — race-safe two-rename commit for agentmd job edits.
3
+ *
4
+ * Per INSTAR-JOBS-AS-AGENTMD spec §Design Principles "Override = fork;
5
+ * race-safe two-rename commit":
6
+ *
7
+ * The save sequence is md-first, manifest-last:
8
+ * 1. Write `<file>.md.new` (staged body).
9
+ * 2. Write `<schedule>.json.new` (staged manifest).
10
+ * 3. rename(<file>.md.new → <file>.md) ← rename A
11
+ * 4. rename(<schedule>.json.new → <schedule>.json) ← rename B
12
+ *
13
+ * SIGKILL between rename A and rename B leaves a consistent state: the
14
+ * new body is on disk paired with the OLD manifest. The loader handles
15
+ * this gracefully — it loads the new body and the old manifest's
16
+ * schedule, which is a strictly-progressed state.
17
+ *
18
+ * SIGKILL before rename A is also consistent: the old body + old
19
+ * manifest are intact; the `.new` staged files are orphaned and the next
20
+ * boot's reconcile() can either reapply or discard them.
21
+ *
22
+ * Re-applying a half-committed save is idempotent because each rename is
23
+ * atomic at the filesystem level and overwrites any pre-existing target.
24
+ *
25
+ * This module is the canonical save helper for both Dashboard edits and
26
+ * future CLI edit commands. It is NOT yet wired into a UI consumer — the
27
+ * Phase 4 Dashboard UI rewrite is the consumer. Shipping the helper
28
+ * separately so the spec's atomicity guarantee has a tested
29
+ * implementation regardless of when the UI lands.
30
+ */
31
+ import fs from 'node:fs';
32
+ import path from 'node:path';
33
+ import { SafeFsExecutor } from '../core/SafeFsExecutor.js';
34
+ /**
35
+ * Two-rename atomic save. Writes the body first, then the manifest. Each
36
+ * write goes to a `.new` sibling, then renames atomically over the final
37
+ * path. On failure at any stage, returns a structured `AtomicSaveFailure`
38
+ * the caller can use to drive recovery / Issues-card surface.
39
+ *
40
+ * Implementation notes:
41
+ * - We DO NOT delete the staged `.new` files on failure — leaving them
42
+ * in place gives reconcile() something to reason about. The next
43
+ * successful save will overwrite them.
44
+ * - `fs.renameSync` is atomic on POSIX filesystems for paths on the
45
+ * same filesystem (always the case here — both live under .instar/).
46
+ * - `fs.writeFileSync` with `flag: 'w'` truncates; we use it
47
+ * deliberately so a half-written `.new` on retry is replaced.
48
+ */
49
+ export function atomicSaveAgentMdJob(input) {
50
+ const { mdPath, manifestPath, mdBody, manifest } = input;
51
+ const mdNew = mdPath + '.new';
52
+ const manifestNew = manifestPath + '.new';
53
+ const partial = {
54
+ mdNewExists: false,
55
+ manifestNewExists: false,
56
+ mdCommitted: false,
57
+ manifestCommitted: false,
58
+ };
59
+ // Stage 1: ensure both parent directories exist.
60
+ try {
61
+ fs.mkdirSync(path.dirname(mdPath), { recursive: true });
62
+ fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
63
+ }
64
+ catch (err) {
65
+ return {
66
+ ok: false,
67
+ stage: 'stage-md',
68
+ reason: `Failed to create parent directories: ${err instanceof Error ? err.message : String(err)}`,
69
+ partial,
70
+ };
71
+ }
72
+ // Stage 2: write the body to its .new sibling.
73
+ try {
74
+ fs.writeFileSync(mdNew, mdBody, { encoding: 'utf-8', flag: 'w' });
75
+ partial.mdNewExists = true;
76
+ }
77
+ catch (err) {
78
+ return {
79
+ ok: false,
80
+ stage: 'stage-md',
81
+ reason: `Failed to stage md body: ${err instanceof Error ? err.message : String(err)}`,
82
+ partial,
83
+ };
84
+ }
85
+ // Stage 3: write the manifest to its .new sibling.
86
+ try {
87
+ fs.writeFileSync(manifestNew, JSON.stringify(manifest, null, 2) + '\n', { encoding: 'utf-8', flag: 'w' });
88
+ partial.manifestNewExists = true;
89
+ }
90
+ catch (err) {
91
+ return {
92
+ ok: false,
93
+ stage: 'stage-manifest',
94
+ reason: `Failed to stage manifest: ${err instanceof Error ? err.message : String(err)}`,
95
+ partial,
96
+ };
97
+ }
98
+ // Stage 4 (rename A): commit md-first. After this, the body is durable.
99
+ try {
100
+ fs.renameSync(mdNew, mdPath);
101
+ partial.mdCommitted = true;
102
+ partial.mdNewExists = false;
103
+ }
104
+ catch (err) {
105
+ return {
106
+ ok: false,
107
+ stage: 'commit-md',
108
+ reason: `Failed to commit md body: ${err instanceof Error ? err.message : String(err)}`,
109
+ partial,
110
+ };
111
+ }
112
+ // Stage 5 (rename B): commit manifest-last. SIGKILL between renames A
113
+ // and B leaves the new body + old manifest, which is a consistent
114
+ // strictly-progressed state.
115
+ try {
116
+ fs.renameSync(manifestNew, manifestPath);
117
+ partial.manifestCommitted = true;
118
+ partial.manifestNewExists = false;
119
+ }
120
+ catch (err) {
121
+ return {
122
+ ok: false,
123
+ stage: 'commit-manifest',
124
+ reason: `Failed to commit manifest: ${err instanceof Error ? err.message : String(err)}`,
125
+ partial,
126
+ };
127
+ }
128
+ return { ok: true, mdWritten: mdPath, manifestWritten: manifestPath };
129
+ }
130
+ /**
131
+ * Discover any `.new` staged files left over from a crashed save. Used by
132
+ * reconcile() to surface "interrupted save" Issues-card rows.
133
+ */
134
+ export function listStagedNewFiles(jobsRootDir) {
135
+ const staged = [];
136
+ walk(jobsRootDir, (file) => {
137
+ if (file.endsWith('.md.new') || file.endsWith('.json.new')) {
138
+ staged.push(file);
139
+ }
140
+ });
141
+ return staged;
142
+ }
143
+ function walk(dir, visit) {
144
+ if (!fs.existsSync(dir))
145
+ return;
146
+ let entries;
147
+ try {
148
+ entries = fs.readdirSync(dir, { withFileTypes: true });
149
+ }
150
+ catch {
151
+ return;
152
+ }
153
+ for (const e of entries) {
154
+ const p = path.join(dir, e.name);
155
+ if (e.isDirectory())
156
+ walk(p, visit);
157
+ else if (e.isFile())
158
+ visit(p);
159
+ }
160
+ }
161
+ /**
162
+ * Clean up an orphaned `.new` file. Used by reconcile() when the operator
163
+ * chooses "Discard staged changes" in the Issues-card UI. Routes through
164
+ * SafeFsExecutor per the destructive-tool funnel.
165
+ */
166
+ export function discardStagedFile(stagedPath) {
167
+ SafeFsExecutor.safeUnlinkSync(stagedPath, { operation: 'AgentMdAtomicSave discard staged .new file' });
168
+ }
169
+ //# sourceMappingURL=AgentMdAtomicSave.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentMdAtomicSave.js","sourceRoot":"","sources":["../../src/scheduler/AgentMdAtomicSave.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * AgentMdReconcile — boot-time consistency check for the agentmd job tree.
3
+ *
4
+ * Per INSTAR-JOBS-AS-AGENTMD spec §Runtime "Load lifecycle (boot)":
5
+ *
6
+ * reconcile() runs at boot, surfaces orphan/shadow/missing on boot.
7
+ * Output goes to Dashboard Issues card.
8
+ *
9
+ * Categories surfaced:
10
+ *
11
+ * - ORPHAN-MANIFEST: A `<slug>.json` per-slug manifest exists but the
12
+ * `.md` it references does NOT. Scheduler cannot resolve the body;
13
+ * entry is excluded from jobs[] and surfaced as Issues-card row.
14
+ *
15
+ * - SHADOW-MD: A `<slug>.md` exists in either `instar/` or `user/` but
16
+ * has no per-slug manifest. The body is on disk but the scheduler
17
+ * has no schedule for it — Issues-card "Add to schedule (disabled)"
18
+ * or "Delete file" actions apply.
19
+ *
20
+ * - MISSING-FROM-JOBS-JSON: A legacy `jobs.json` entry has no
21
+ * corresponding per-slug manifest AND no `.md` under user/ or
22
+ * instar/. Mid-migration state; not yet copied over.
23
+ *
24
+ * - STAGED-NEW: A `.md.new` or `.json.new` left over from a crashed
25
+ * atomic save (see AgentMdAtomicSave). Issues-card "Apply" /
26
+ * "Discard" actions apply.
27
+ *
28
+ * - CASE-COLLISION: Two slug files differ only by case (NFC normalized)
29
+ * in the same directory. Both are excluded from jobs[] by the
30
+ * loader; this row surfaces them together.
31
+ *
32
+ * Pure function — reads the file system, returns a structured report.
33
+ * The caller (boot lifecycle in JobLoader; Phase 4 Dashboard read
34
+ * endpoint) decides how to present the rows.
35
+ */
36
+ export type ReconcileFindingKind = 'orphan-manifest' | 'shadow-md' | 'missing-from-jobs-json' | 'staged-new' | 'case-collision';
37
+ export interface ReconcileFinding {
38
+ kind: ReconcileFindingKind;
39
+ /** Affected slug (when computable). */
40
+ slug?: string;
41
+ /** Affected file path (absolute). */
42
+ filePath?: string;
43
+ /** Two-pane comparison: for case-collision, both paths. */
44
+ conflictingPaths?: string[];
45
+ /** One-line operator-facing summary. */
46
+ summary: string;
47
+ /** Stable severity ordering for the Issues card. */
48
+ severity: 'info' | 'warning' | 'error';
49
+ }
50
+ export interface ReconcileReport {
51
+ findings: ReconcileFinding[];
52
+ summary: {
53
+ total: number;
54
+ byKind: Record<ReconcileFindingKind, number>;
55
+ };
56
+ }
57
+ export interface ReconcileOptions {
58
+ /** State directory root (e.g., `<projectDir>/.instar/`). */
59
+ stateDir: string;
60
+ }
61
+ export declare function reconcileAgentMdTree(opts: ReconcileOptions): ReconcileReport;
62
+ //# sourceMappingURL=AgentMdReconcile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentMdReconcile.d.ts","sourceRoot":"","sources":["../../src/scheduler/AgentMdReconcile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AA8BH,MAAM,MAAM,oBAAoB,GAC5B,iBAAiB,GACjB,WAAW,GACX,wBAAwB,GACxB,YAAY,GACZ,gBAAgB,CAAC;AAErB,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,oBAAoB,CAAC;IAC3B,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,wCAAwC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;KAC9C,CAAC;CACH;AAED,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAUD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,gBAAgB,GAAG,eAAe,CAoH5E"}