@ashlr/hub 3.5.1 → 3.8.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 (124) hide show
  1. package/CHANGELOG.md +165 -0
  2. package/README.md +3 -0
  3. package/dist/build-identity.json +2 -2
  4. package/dist/cli/activation.d.ts +41 -0
  5. package/dist/cli/activation.js +485 -0
  6. package/dist/cli/activation.js.map +1 -0
  7. package/dist/core/daemon/cutoff-checkpoint-scheduler.js +7 -0
  8. package/dist/core/daemon/cutoff-checkpoint-scheduler.js.map +1 -1
  9. package/dist/core/daemon/loop.d.ts +19 -0
  10. package/dist/core/daemon/loop.js +235 -5
  11. package/dist/core/daemon/loop.js.map +1 -1
  12. package/dist/core/daemon/overnight-status.d.ts +152 -0
  13. package/dist/core/daemon/overnight-status.js +275 -0
  14. package/dist/core/daemon/overnight-status.js.map +1 -0
  15. package/dist/core/daemon/post-merge-halt.d.ts +212 -0
  16. package/dist/core/daemon/post-merge-halt.js +503 -0
  17. package/dist/core/daemon/post-merge-halt.js.map +1 -0
  18. package/dist/core/daemon/run-window.d.ts +218 -0
  19. package/dist/core/daemon/run-window.js +420 -0
  20. package/dist/core/daemon/run-window.js.map +1 -0
  21. package/dist/core/fleet/generated-repair-resolution.d.ts +36 -0
  22. package/dist/core/fleet/generated-repair-resolution.js +245 -0
  23. package/dist/core/fleet/generated-repair-resolution.js.map +1 -0
  24. package/dist/core/local-runtime/llama/port.d.ts +17 -0
  25. package/dist/core/local-runtime/llama/port.js +47 -0
  26. package/dist/core/local-runtime/llama/port.js.map +1 -0
  27. package/dist/core/local-runtime/llama/proxy-host.d.ts +54 -0
  28. package/dist/core/local-runtime/llama/proxy-host.js +159 -0
  29. package/dist/core/local-runtime/llama/proxy-host.js.map +1 -0
  30. package/dist/core/local-runtime/llama/proxy-process.d.ts +148 -0
  31. package/dist/core/local-runtime/llama/proxy-process.js +250 -0
  32. package/dist/core/local-runtime/llama/proxy-process.js.map +1 -0
  33. package/dist/core/local-runtime/llama/proxy-record.d.ts +40 -0
  34. package/dist/core/local-runtime/llama/proxy-record.js +133 -0
  35. package/dist/core/local-runtime/llama/proxy-record.js.map +1 -0
  36. package/dist/core/local-runtime/llama/proxy-supervisor.d.ts +118 -0
  37. package/dist/core/local-runtime/llama/proxy-supervisor.js +511 -0
  38. package/dist/core/local-runtime/llama/proxy-supervisor.js.map +1 -0
  39. package/dist/core/policy/local-only.d.ts +142 -2
  40. package/dist/core/policy/local-only.js +204 -15
  41. package/dist/core/policy/local-only.js.map +1 -1
  42. package/dist/core/run/budget.d.ts +22 -8
  43. package/dist/core/run/budget.js +80 -12
  44. package/dist/core/run/budget.js.map +1 -1
  45. package/dist/core/run/sandboxed-engine.js +3 -3
  46. package/dist/core/run/sandboxed-engine.js.map +1 -1
  47. package/dist/core/universe/builtins/preparation/manifest.json +1 -1
  48. package/dist/core/universe/builtins/preparation/preparation-bridge.mjs +61 -9
  49. package/dist/core/universe/builtins/preparation/preparation-verification-fixtures.mjs +60 -8
  50. package/dist/core/verse/session-engine.d.ts +42 -1
  51. package/dist/core/verse/session-engine.js +79 -2
  52. package/dist/core/verse/session-engine.js.map +1 -1
  53. package/dist/core/web/public/next/assets/{App-BbVoUCIi.js → App-BsbJFA2j.js} +2 -2
  54. package/dist/core/web/public/next/assets/{App-BbVoUCIi.js.map → App-BsbJFA2j.js.map} +1 -1
  55. package/dist/core/web/public/next/assets/{ApprovalsSection-BW9iVjUa.js → ApprovalsSection-2CCSoBTJ.js} +2 -2
  56. package/dist/core/web/public/next/assets/{ApprovalsSection-BW9iVjUa.js.map → ApprovalsSection-2CCSoBTJ.js.map} +1 -1
  57. package/dist/core/web/public/next/assets/{AutonomySection-Cpk_oEhc.css → AutonomySection-CHjxJt2z.css} +1 -1
  58. package/dist/core/web/public/next/assets/AutonomySection-yeNNUPur.js +2 -0
  59. package/dist/core/web/public/next/assets/AutonomySection-yeNNUPur.js.map +1 -0
  60. package/dist/core/web/public/next/assets/ChatSection-BfnLe5dT.css +1 -0
  61. package/dist/core/web/public/next/assets/ChatSection-DwDDnCZQ.js +80 -0
  62. package/dist/core/web/public/next/assets/ChatSection-DwDDnCZQ.js.map +1 -0
  63. package/dist/core/web/public/next/assets/{ConfirmDialog-CXevY3Au.js → ConfirmDialog-C363ty_c.js} +2 -2
  64. package/dist/core/web/public/next/assets/{ConfirmDialog-CXevY3Au.js.map → ConfirmDialog-C363ty_c.js.map} +1 -1
  65. package/dist/core/web/public/next/assets/{Dialog-Bn0CgEGz.js → Dialog-nK15L1M0.js} +2 -2
  66. package/dist/core/web/public/next/assets/{Dialog-Bn0CgEGz.js.map → Dialog-nK15L1M0.js.map} +1 -1
  67. package/dist/core/web/public/next/assets/{DiffViewer-QKyisN2L.js → DiffViewer-D369eZe8.js} +2 -2
  68. package/dist/core/web/public/next/assets/{DiffViewer-QKyisN2L.js.map → DiffViewer-D369eZe8.js.map} +1 -1
  69. package/dist/core/web/public/next/assets/McpSection-D88QlDpG.css +1 -0
  70. package/dist/core/web/public/next/assets/McpSection-DVgiSYG1.js +2 -0
  71. package/dist/core/web/public/next/assets/McpSection-DVgiSYG1.js.map +1 -0
  72. package/dist/core/web/public/next/assets/Meter-CLcr-4wS.css +1 -0
  73. package/dist/core/web/public/next/assets/Meter-Cmo1pLbk.js +2 -0
  74. package/dist/core/web/public/next/assets/Meter-Cmo1pLbk.js.map +1 -0
  75. package/dist/core/web/public/next/assets/{MutationTokenDialog-DbSHPy3w.js → MutationTokenDialog-B9I4PAcp.js} +2 -2
  76. package/dist/core/web/public/next/assets/{MutationTokenDialog-DbSHPy3w.js.map → MutationTokenDialog-B9I4PAcp.js.map} +1 -1
  77. package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-C-PCgCzo.js → ResourcePoolConsoleApp-z6tJj3s9.js} +2 -2
  78. package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-C-PCgCzo.js.map → ResourcePoolConsoleApp-z6tJj3s9.js.map} +1 -1
  79. package/dist/core/web/public/next/assets/{RouteErrorBoundary-Dw8ZjmkS.js → RouteErrorBoundary-OSajL45F.js} +2 -2
  80. package/dist/core/web/public/next/assets/{RouteErrorBoundary-Dw8ZjmkS.js.map → RouteErrorBoundary-OSajL45F.js.map} +1 -1
  81. package/dist/core/web/public/next/assets/SettingsSection-B3YH2Npi.js +2 -0
  82. package/dist/core/web/public/next/assets/SettingsSection-B3YH2Npi.js.map +1 -0
  83. package/dist/core/web/public/next/assets/{UniverseConsoleApp-Bk5HipM0.js → UniverseConsoleApp-wEKIzl1M.js} +2 -2
  84. package/dist/core/web/public/next/assets/{UniverseConsoleApp-Bk5HipM0.js.map → UniverseConsoleApp-wEKIzl1M.js.map} +1 -1
  85. package/dist/core/web/public/next/assets/{UniverseView-D8k9q0ce.js → UniverseView-CQVpJqqI.js} +2 -2
  86. package/dist/core/web/public/next/assets/{UniverseView-D8k9q0ce.js.map → UniverseView-CQVpJqqI.js.map} +1 -1
  87. package/dist/core/web/public/next/assets/{UsageSection-B2E06ZH1.js → UsageSection-dToBThXe.js} +2 -2
  88. package/dist/core/web/public/next/assets/{UsageSection-B2E06ZH1.js.map → UsageSection-dToBThXe.js.map} +1 -1
  89. package/dist/core/web/public/next/assets/VerseConsoleApp-CMkCizvL.css +1 -0
  90. package/dist/core/web/public/next/assets/VerseConsoleApp-DHU32JCQ.js +3 -0
  91. package/dist/core/web/public/next/assets/VerseConsoleApp-DHU32JCQ.js.map +1 -0
  92. package/dist/core/web/public/next/assets/{charts-DSUpczxG.js → charts-BopViIha.js} +2 -2
  93. package/dist/core/web/public/next/assets/{charts-DSUpczxG.js.map → charts-BopViIha.js.map} +1 -1
  94. package/dist/core/web/public/next/assets/global-DMWakEhP.js +2 -0
  95. package/dist/core/web/public/next/assets/global-DMWakEhP.js.map +1 -0
  96. package/dist/core/web/public/next/assets/global-z-JfD3ow.css +1 -0
  97. package/dist/core/web/public/next/assets/{icons-BxOX8-XB.js → icons-L06pQH6j.js} +2 -2
  98. package/dist/core/web/public/next/assets/{icons-BxOX8-XB.js.map → icons-L06pQH6j.js.map} +1 -1
  99. package/dist/core/web/public/next/assets/{index-BMRIET7F.js → index-CXgVQHek.js} +3 -3
  100. package/dist/core/web/public/next/assets/{index-BMRIET7F.js.map → index-CXgVQHek.js.map} +1 -1
  101. package/dist/core/web/public/next/assets/{queries-AxnVa02h.js → queries-D3sSlan1.js} +2 -2
  102. package/dist/core/web/public/next/assets/{queries-AxnVa02h.js.map → queries-D3sSlan1.js.map} +1 -1
  103. package/dist/core/web/public/next/assets/{use-guarded-action-BO6c94Wp.js → use-guarded-action-BxfJ9E-n.js} +2 -2
  104. package/dist/core/web/public/next/assets/{use-guarded-action-BO6c94Wp.js.map → use-guarded-action-BxfJ9E-n.js.map} +1 -1
  105. package/dist/core/web/public/next/assets/workspace-model-v0a1gl-C.js +2 -0
  106. package/dist/core/web/public/next/assets/workspace-model-v0a1gl-C.js.map +1 -0
  107. package/dist/core/web/public/next/index.html +1 -1
  108. package/dist/release-dependency-inventory.json +1 -1
  109. package/package.json +1 -1
  110. package/dist/core/web/public/next/assets/AutonomySection-CRqHUhED.js +0 -2
  111. package/dist/core/web/public/next/assets/AutonomySection-CRqHUhED.js.map +0 -1
  112. package/dist/core/web/public/next/assets/ChatSection-5irrh6Jw.js +0 -80
  113. package/dist/core/web/public/next/assets/ChatSection-5irrh6Jw.js.map +0 -1
  114. package/dist/core/web/public/next/assets/ChatSection-LQiAn6Eu.css +0 -1
  115. package/dist/core/web/public/next/assets/SettingsSection-CWxtTNlD.js +0 -2
  116. package/dist/core/web/public/next/assets/SettingsSection-CWxtTNlD.js.map +0 -1
  117. package/dist/core/web/public/next/assets/VerseConsoleApp-CRfZUV-T.js +0 -3
  118. package/dist/core/web/public/next/assets/VerseConsoleApp-CRfZUV-T.js.map +0 -1
  119. package/dist/core/web/public/next/assets/VerseConsoleApp-D2E3mU9a.css +0 -1
  120. package/dist/core/web/public/next/assets/global-C4jfYHrj.js +0 -2
  121. package/dist/core/web/public/next/assets/global-C4jfYHrj.js.map +0 -1
  122. package/dist/core/web/public/next/assets/global-D463gG1X.css +0 -1
  123. package/dist/core/web/public/next/assets/workspace-model-cE4LkKpE.js +0 -2
  124. package/dist/core/web/public/next/assets/workspace-model-cE4LkKpE.js.map +0 -1
@@ -0,0 +1,503 @@
1
+ /**
2
+ * post-merge-halt.ts — the ONE gate that makes an unattended night survivable.
3
+ *
4
+ * ── WHAT WAS ALREADY THERE, AND WHAT WAS NOT ───────────────────────────────
5
+ * The PRE-merge gate is genuinely solid and this module does not duplicate a
6
+ * line of it. `fleet/automerge-pass` → `inbox/merge.autoMergeProposal` already
7
+ * runs the repo's OWN typecheck / tests / lint in a throwaway worktree branched
8
+ * off the default-branch head (`inbox/merge.verifyProposal`), with the verify
9
+ * commands detected from the BASE tree so a diff cannot rewrite which commands
10
+ * run; refuses any diff touching package.json / lockfiles / CI / Dockerfile /
11
+ * Makefile outright; caps scope (4 files / 150 lines) and risk; requires an
12
+ * independent frontier judge with an HMAC-signed attestation; calls
13
+ * `assertMayMutate` so only `listEnrolled()` repos are ever touched; and
14
+ * re-checks the kill switch at every write boundary. A proposal that cannot be
15
+ * verified is left PENDING or rejected — never merged — and every rejection is
16
+ * audited (`inbox:proposal-rejected`) with a decisions-ledger row. That is the
17
+ * pre-merge half of the contract, and it holds.
18
+ *
19
+ * What did NOT exist anywhere is the half that matters at 4am: NOTHING re-ran
20
+ * the suite AFTER a merge landed, and nothing could halt the run when it went
21
+ * red. The only post-merge machinery is observation-only by construction —
22
+ * `fleet/automerge-canary-observer` declares `authority: 'observation-only',
23
+ * enforceSupported: false` and merely writes an audit row, and the M189
24
+ * regression sentinel is default-OFF, needs two consecutive RED observations,
25
+ * and is explicitly "proposal-only — NEVER applies, merges, or pushes".
26
+ *
27
+ * So a regression merged at 01:00 was invisible to the gate, and iteration N+1
28
+ * was then built on top of it, and N+2 on top of that. By morning the bad
29
+ * commit is load-bearing and backing it out is archaeology. THIS module makes
30
+ * that impossible: the first red suite after a merge ENDS THE RUN. A bad night
31
+ * costs exactly one revert, which this module names precisely, instead of a
32
+ * tangled morning.
33
+ *
34
+ * ── HOW IT KNOWS A MERGE LANDED ────────────────────────────────────────────
35
+ * By reading the repos, not by trusting a return value. It snapshots each
36
+ * enrolled repo's HEAD before the tick and compares after. Anything that moved
37
+ * a default branch — `automerge-pass`, a future path, anything at all — is
38
+ * caught, because the evidence is the repository itself. A result object can be
39
+ * wrong about what it did; `git rev-parse HEAD` cannot.
40
+ *
41
+ * ── FAIL SAFE ──────────────────────────────────────────────────────────────
42
+ * A landing that cannot be verified HALTS. Not "continue and hope": if the
43
+ * repo declares no required verify command, or the command cannot be run, or
44
+ * git cannot be read, or THE WORKING TREE IS DIRTY, the run ends and says why.
45
+ * That is the same direction the pre-merge gate already fails
46
+ * (`allowWithoutVerification` defaults false) and the same direction
47
+ * `daemonPaused()` projects an unreadable sentinel.
48
+ *
49
+ * Those cases are reported as UNPROVABLE, never as a regression, and the
50
+ * distinction is deliberate. Not being overly critical governs how harshly a
51
+ * CHANGE is judged; it says nothing about whether the EVIDENCE is sound. A
52
+ * dirty tree means the post-merge run measured the operator's uncommitted
53
+ * edits instead of the merge — the gate is not being strict there, it is
54
+ * being honest that it proved nothing. That case matters more than the rest,
55
+ * because it is the only one where the gate would otherwise say GREEN and be
56
+ * wrong, and a false green is what the whole run window exists to prevent.
57
+ *
58
+ * ── FOLLOW-UP: THE MERGE COMMIT MESSAGE ────────────────────────────────────
59
+ * Each merge is already exactly one revertable commit, but its message names
60
+ * only the proposal: `ashlr: merge proposal branch ashlr/merge/<id>`
61
+ * (inbox/merge.ts:2843-2854) over `ashlr: auto-merge proposal <id>`
62
+ * (inbox/merge.ts:2690). It says WHAT, not WHY. Carrying the why into the
63
+ * commit means editing inbox/merge.ts, which 14 automerge suites pin; until
64
+ * that is worth doing, the why lives in the overnight status record
65
+ * (daemon/overnight-status.ts), keyed by the same proposal id, and in the
66
+ * halt record written below.
67
+ *
68
+ * ── HOW IT HALTS ───────────────────────────────────────────────────────────
69
+ * By PARKING, never by killing. `stopDaemon()` is `setKill(true)`, which writes
70
+ * the global `~/.ashlr/KILL` that `assertMayMutate` also reads — it would
71
+ * disable the operator's own MCP write tools on arrival. This module's halt is
72
+ * `pauseDaemon()` (`~/.ashlr/daemon.paused`, see daemon/pause.ts), which stops
73
+ * autonomous dispatch and nothing else, and which one click reverses.
74
+ *
75
+ * No new runtime deps; node builtins only. Never throws out of a public API.
76
+ */
77
+ import { execFileSync } from 'node:child_process';
78
+ import { mkdirSync, writeFileSync, readdirSync, readFileSync, renameSync, unlinkSync } from 'node:fs';
79
+ import { homedir } from 'node:os';
80
+ import { join, dirname } from 'node:path';
81
+ import { randomUUID } from 'node:crypto';
82
+ import { audit } from '../sandbox/audit.js';
83
+ import { listEnrolled } from '../sandbox/policy.js';
84
+ import { detectVerifyCommands, runVerifyCommandAsync, } from '../run/verify-commands.js';
85
+ const GIT_TIMEOUT_MS = 10_000;
86
+ /** Failure kinds that mean "could not prove it", not "it is broken". */
87
+ const NON_REGRESSION_KINDS = new Set(['detection', 'harness', 'git', 'dirty-tree']);
88
+ /**
89
+ * Is the repo's working tree clean enough for a post-merge verdict to MEAN
90
+ * anything?
91
+ *
92
+ * Returns null when clean, or a short description of what is dirty.
93
+ * `git status --porcelain` already honours `.gitignore`, so build output and
94
+ * caches do not show up here — what does show up is a real uncommitted change
95
+ * or a real untracked, un-ignored file. Both change what the suite measures.
96
+ *
97
+ * An unreadable status is reported as dirty: the same fail-safe direction as
98
+ * everything else here. We are asking "can this verdict be trusted", and
99
+ * "I could not tell" is not a yes.
100
+ */
101
+ function workingTreeDirt(repo) {
102
+ const status = git(repo, ['status', '--porcelain', '--untracked-files=normal']);
103
+ if (status === null)
104
+ return 'git status could not be read';
105
+ if (status.length === 0)
106
+ return null;
107
+ const lines = status.split('\n').filter((line) => line.trim().length > 0);
108
+ const modified = lines.filter((line) => !line.startsWith('??')).length;
109
+ const untracked = lines.length - modified;
110
+ const parts = [];
111
+ if (modified > 0)
112
+ parts.push(`${modified} uncommitted change${modified === 1 ? '' : 's'}`);
113
+ if (untracked > 0)
114
+ parts.push(`${untracked} untracked file${untracked === 1 ? '' : 's'}`);
115
+ return parts.join(' and ');
116
+ }
117
+ /** Run git in `cwd`; trimmed stdout, or null on ANY failure. Never throws. */
118
+ function git(cwd, args) {
119
+ try {
120
+ return execFileSync('git', args, {
121
+ cwd,
122
+ timeout: GIT_TIMEOUT_MS,
123
+ stdio: 'pipe',
124
+ encoding: 'utf8',
125
+ // A merge landed by the fleet must not be re-read through a hook.
126
+ env: { ...process.env, GIT_OPTIONAL_LOCKS: '0' },
127
+ }).trim();
128
+ }
129
+ catch {
130
+ return null;
131
+ }
132
+ }
133
+ // ---------------------------------------------------------------------------
134
+ // Snapshot + landing detection
135
+ // ---------------------------------------------------------------------------
136
+ /**
137
+ * Read every ENROLLED repo's HEAD. Enrollment is the only source of repos —
138
+ * a repo that is not enrolled is never read, never verified, and can never
139
+ * appear in a landing or a halt.
140
+ *
141
+ * Never throws; an unreadable repo is recorded as such, not dropped.
142
+ */
143
+ export function snapshotEnrolledHeads(seams = {}) {
144
+ let repos;
145
+ try {
146
+ repos = (seams.listEnrolledRepos ?? listEnrolled)();
147
+ }
148
+ catch {
149
+ return [];
150
+ }
151
+ if (!Array.isArray(repos))
152
+ return [];
153
+ const out = [];
154
+ for (const repo of repos) {
155
+ if (typeof repo !== 'string' || repo.length === 0)
156
+ continue;
157
+ const head = git(repo, ['rev-parse', 'HEAD']);
158
+ out.push(head && /^[0-9a-f]{40}$/.test(head)
159
+ ? { repo, head }
160
+ : { repo, head: null, unreadable: 'git rev-parse HEAD failed' });
161
+ }
162
+ return out;
163
+ }
164
+ function parseLandedCommits(repo, beforeHead, afterHead) {
165
+ // `%H %P %s` — oid, parent oids, subject. Oldest first so the revert plan
166
+ // can be emitted newest-first by reversing it.
167
+ const raw = git(repo, ['log', '--reverse', '--format=%H%x00%P%x00%s', `${beforeHead}..${afterHead}`]);
168
+ if (raw === null || raw.length === 0)
169
+ return [];
170
+ const commits = [];
171
+ for (const line of raw.split('\n')) {
172
+ const [sha, parents, subject] = line.split('\0');
173
+ if (!sha || !/^[0-9a-f]{40}$/.test(sha))
174
+ continue;
175
+ commits.push({
176
+ sha,
177
+ subject: (subject ?? '').slice(0, 200),
178
+ isMerge: (parents ?? '').trim().split(/\s+/).filter(Boolean).length > 1,
179
+ });
180
+ }
181
+ return commits;
182
+ }
183
+ /**
184
+ * Which enrolled repos moved between two snapshots.
185
+ *
186
+ * A repo present in `before` but unreadable in `after` (or vice versa) is NOT
187
+ * treated as "nothing happened" — it is reported as a landing with an empty
188
+ * commit list so the caller fails safe rather than assuming quiet.
189
+ */
190
+ export function detectLandings(before, after) {
191
+ const beforeByRepo = new Map(before.map((s) => [s.repo, s]));
192
+ const landings = [];
193
+ const unreadable = [];
194
+ for (const now of after) {
195
+ const then = beforeByRepo.get(now.repo);
196
+ // A repo enrolled DURING the tick has no 'before'. Nothing can be said to
197
+ // have landed in it during this iteration, so it is not a landing.
198
+ if (!then)
199
+ continue;
200
+ if (then.head === null || now.head === null) {
201
+ unreadable.push({
202
+ repo: now.repo,
203
+ kind: 'git',
204
+ command: 'git rev-parse HEAD',
205
+ detail: now.unreadable ?? then.unreadable ?? 'repository head unreadable',
206
+ });
207
+ continue;
208
+ }
209
+ if (then.head === now.head)
210
+ continue;
211
+ const commits = parseLandedCommits(now.repo, then.head, now.head);
212
+ landings.push({
213
+ repo: now.repo,
214
+ beforeHead: then.head,
215
+ afterHead: now.head,
216
+ commits,
217
+ revertCommand: revertCommandFor(then.head, now.head, commits),
218
+ });
219
+ }
220
+ return { landings, unreadable };
221
+ }
222
+ /**
223
+ * The exact command that backs a landing out.
224
+ *
225
+ * A single merge commit — which is what `inbox/merge.mergeLocally` produces,
226
+ * a two-parent `commit-tree` against the verified base — reverts with
227
+ * `git revert -m 1 <oid>`. Several commits revert newest-first. A landing whose
228
+ * commits could not be listed falls back to resetting the branch to the exact
229
+ * OID it had before the iteration, which is always correct for a local-only
230
+ * merge that was never pushed (and `inbox/merge` never pushes: host auto-merge
231
+ * is disabled and returns a handoff instead).
232
+ */
233
+ export function revertCommandFor(beforeHead, afterHead, commits) {
234
+ if (commits.length === 0) {
235
+ return `git reset --hard ${beforeHead} # was ${afterHead}`;
236
+ }
237
+ const newestFirst = [...commits].reverse();
238
+ return newestFirst
239
+ .map((c) => `git revert --no-edit ${c.isMerge ? '-m 1 ' : ''}${c.sha}`)
240
+ .join(' && ');
241
+ }
242
+ // ---------------------------------------------------------------------------
243
+ // The gate
244
+ // ---------------------------------------------------------------------------
245
+ /**
246
+ * Re-run each landed-on repo's OWN required verify commands against the MERGED
247
+ * head, and decide whether the run may continue.
248
+ *
249
+ * Fast in the common case: when no enrolled repo moved, this is one
250
+ * `git rev-parse` per repo and returns `no-landing` without running anything.
251
+ * The expensive path only runs when something actually merged, which is exactly
252
+ * when it is worth paying for.
253
+ *
254
+ * `halt` is true for `regressed` and `unverifiable` and for nothing else.
255
+ * Never throws.
256
+ */
257
+ export async function runPostMergeGate(before, after, cfg, seams = {}) {
258
+ const now = seams.now ?? Date.now;
259
+ const startedAtMs = now();
260
+ const detect = seams.detect ?? detectVerifyCommands;
261
+ const run = seams.run ?? runVerifyCommandAsync;
262
+ const finish = (verdict, landings, failures, ranCommands, detail) => ({
263
+ verdict,
264
+ halt: verdict === 'regressed' || verdict === 'unverifiable',
265
+ landings,
266
+ failures,
267
+ ranCommands,
268
+ detail,
269
+ revertPlan: landings.map((l) => l.revertCommand),
270
+ durationMs: Math.max(0, now() - startedAtMs),
271
+ });
272
+ let landings;
273
+ let unreadable;
274
+ try {
275
+ ({ landings, unreadable } = detectLandings(before, after));
276
+ }
277
+ catch (error) {
278
+ const detail = `post-merge gate could not compare repository heads: ${errText(error)}`;
279
+ return finish('unverifiable', [], [{
280
+ repo: '(unknown)', kind: 'git', command: 'git rev-parse HEAD', detail,
281
+ }], 0, detail);
282
+ }
283
+ if (unreadable.length > 0) {
284
+ // FAIL SAFE. An enrolled repo we cannot read may have taken a merge we
285
+ // cannot see. Continuing would be iteration N+1 on an unknown tree.
286
+ const detail = `post-merge gate cannot read ${unreadable.length} enrolled repo(s) ` +
287
+ `(${unreadable.map((f) => f.repo).join(', ')}); the run cannot prove what landed`;
288
+ return finish('unverifiable', landings, unreadable, 0, detail);
289
+ }
290
+ if (landings.length === 0) {
291
+ return finish('no-landing', [], [], 0, 'no enrolled repository head moved this iteration');
292
+ }
293
+ const failures = [];
294
+ let ranCommands = 0;
295
+ let unverifiable = false;
296
+ for (const landing of landings) {
297
+ if (seams.signal?.aborted) {
298
+ // A shutdown mid-verification has NOT proven the merge good. Halt.
299
+ const detail = `post-merge verification was interrupted for ${landing.repo}; the merge is unproven`;
300
+ failures.push({ repo: landing.repo, kind: 'harness', command: '(interrupted)', detail });
301
+ return finish('unverifiable', landings, failures, ranCommands, detail);
302
+ }
303
+ // ── UNPROVABLE: a dirty tree ─────────────────────────────────────────
304
+ // Checked BEFORE the suite runs, because a suite run against a dirty tree
305
+ // is not cheap and not meaningful.
306
+ //
307
+ // The whole overnight safety property is "post-merge verification halts
308
+ // the run before a regression compounds". If that verification can be
309
+ // silently invalid, the property is void — and this is the one failure
310
+ // mode where the gate says GREEN and is wrong, which is strictly worse
311
+ // than a false halt. A tree carrying uncommitted edits measures the
312
+ // operator's work-in-progress, not the merge.
313
+ //
314
+ // This is NOT the gate being harsh about a change. Being un-harsh governs
315
+ // how we judge a diff; it says nothing about whether the evidence is
316
+ // sound. A dirty tree sits in the same category as a repo with no
317
+ // required verify command and a repo whose HEAD cannot be read: the gate
318
+ // is not refusing the change, it is refusing to pretend it proved one.
319
+ const dirt = workingTreeDirt(landing.repo);
320
+ if (dirt !== null) {
321
+ failures.push({
322
+ repo: landing.repo,
323
+ kind: 'dirty-tree',
324
+ command: 'git status --porcelain',
325
+ detail: `the working tree holds ${dirt}, so a post-merge run there would measure those ` +
326
+ `and not the merge — the result cannot be trusted either way`,
327
+ });
328
+ unverifiable = true;
329
+ continue;
330
+ }
331
+ let commands;
332
+ try {
333
+ commands = detect(landing.repo, 'merge');
334
+ }
335
+ catch (error) {
336
+ failures.push({
337
+ repo: landing.repo, kind: 'detection', command: 'detectVerifyCommands',
338
+ detail: `verify-command detection threw: ${errText(error)}`,
339
+ });
340
+ unverifiable = true;
341
+ continue;
342
+ }
343
+ const required = commands.filter((c) => c.required !== false);
344
+ if (required.length === 0) {
345
+ // Same fail-closed direction as the PRE-merge gate, which refuses a repo
346
+ // with no required command unless allowWithoutVerification is set. A
347
+ // merge that landed in a repo that can no longer prove itself is not
348
+ // something to keep building on for another six hours.
349
+ failures.push({
350
+ repo: landing.repo, kind: 'detection', command: 'detectVerifyCommands',
351
+ detail: 'no REQUIRED verify command detected after the merge — the landing cannot be proven good',
352
+ });
353
+ unverifiable = true;
354
+ continue;
355
+ }
356
+ for (const command of required) {
357
+ if (seams.signal?.aborted) {
358
+ const detail = `post-merge verification was interrupted for ${landing.repo}; the merge is unproven`;
359
+ failures.push({ repo: landing.repo, kind: 'harness', command: '(interrupted)', detail });
360
+ return finish('unverifiable', landings, failures, ranCommands, detail);
361
+ }
362
+ let result;
363
+ try {
364
+ ranCommands++;
365
+ result = await run(command, landing.repo, cfg, {
366
+ ...(seams.signal ? { signal: seams.signal } : {}),
367
+ });
368
+ }
369
+ catch (error) {
370
+ // A command that could not be EXECUTED proves nothing about the diff.
371
+ // Classified as 'harness' so it halts as unverifiable rather than as a
372
+ // regression the merge did not necessarily cause.
373
+ failures.push({
374
+ repo: landing.repo, kind: 'harness', command: command.cmd.join(' '),
375
+ detail: `verify command could not be run: ${errText(error)}`,
376
+ });
377
+ unverifiable = true;
378
+ continue;
379
+ }
380
+ if (!result.ok) {
381
+ failures.push({
382
+ repo: landing.repo,
383
+ kind: command.kind,
384
+ command: result.command || command.cmd.join(' '),
385
+ // Metadata only — command OUTPUT never reaches an audit summary.
386
+ detail: result.timedOut
387
+ ? `timed out after the merge`
388
+ : `failed after the merge (exit ${result.exitCode}` +
389
+ `${result.failureCategory ? `, ${result.failureCategory}` : ''})`,
390
+ });
391
+ }
392
+ }
393
+ }
394
+ const merged = landings.reduce((n, l) => n + Math.max(1, l.commits.length), 0);
395
+ // Only a check that actually RAN and came back red is a regression.
396
+ const red = failures.filter((f) => !NON_REGRESSION_KINDS.has(f.kind));
397
+ if (red.length > 0) {
398
+ return finish('regressed', landings, failures, ranCommands, `POST-MERGE REGRESSION: ${red.length} required check(s) RED in ` +
399
+ `${new Set(red.map((f) => f.repo)).size} repo(s) after ${merged} landed commit(s); run halted`);
400
+ }
401
+ if (unverifiable) {
402
+ const dirty = failures.filter((f) => f.kind === 'dirty-tree');
403
+ return finish('unverifiable', landings, failures, ranCommands, `POST-MERGE UNPROVABLE: landing could not be verified in ` +
404
+ `${new Set(failures.map((f) => f.repo)).size} repo(s)` +
405
+ (dirty.length > 0
406
+ ? ` (${dirty.length} with a dirty working tree — the verdict would have measured ` +
407
+ `uncommitted edits, not the merge)`
408
+ : '') +
409
+ `; run halted rather than built upon`);
410
+ }
411
+ return finish('clean', landings, failures, ranCommands, `post-merge suite green: ${ranCommands} required check(s) across ` +
412
+ `${landings.length} repo(s) after ${merged} landed commit(s)`);
413
+ }
414
+ function errText(error) {
415
+ return error instanceof Error ? error.message : String(error);
416
+ }
417
+ // ---------------------------------------------------------------------------
418
+ // The halt record — a bad night must cost one revert, not archaeology
419
+ // ---------------------------------------------------------------------------
420
+ export function postMergeHaltDir() {
421
+ return join(homedir(), '.ashlr', 'run-window');
422
+ }
423
+ /**
424
+ * Persist the halt durably (exclusive tmp + atomic rename) and audit it.
425
+ * Returns the record path, or null when it could not be written — a write
426
+ * failure must never itself throw out of the loop's shutdown path.
427
+ */
428
+ export function recordPostMergeHalt(result, opts = {}) {
429
+ if (!result.halt)
430
+ return null;
431
+ const nowMs = (opts.now ?? Date.now)();
432
+ const record = {
433
+ recordType: 'daemon-post-merge-halt',
434
+ haltedAt: new Date(nowMs).toISOString(),
435
+ verdict: result.verdict,
436
+ detail: result.detail,
437
+ landings: result.landings,
438
+ failures: result.failures,
439
+ revertPlan: result.revertPlan,
440
+ };
441
+ let path = null;
442
+ try {
443
+ const dir = postMergeHaltDir();
444
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
445
+ path = join(dir, `halt-${new Date(nowMs).toISOString().replace(/[:.]/g, '-')}.json`);
446
+ const tmp = `${path}.${process.pid}.${randomUUID()}.tmp`;
447
+ try {
448
+ writeFileSync(tmp, JSON.stringify(record, null, 2) + '\n', { mode: 0o600, flag: 'wx' });
449
+ renameSync(tmp, path);
450
+ }
451
+ catch (error) {
452
+ try {
453
+ unlinkSync(tmp);
454
+ }
455
+ catch { /* the rename already consumed it */ }
456
+ throw error;
457
+ }
458
+ void dirname(path);
459
+ }
460
+ catch {
461
+ path = null;
462
+ }
463
+ try {
464
+ audit({
465
+ action: 'daemon:post-merge-halt',
466
+ repo: result.landings[0]?.repo ?? null,
467
+ sandboxId: null,
468
+ summary: `${result.detail}${result.revertPlan.length > 0
469
+ ? ` — revert with: ${result.revertPlan.join(' ; ')}`
470
+ : ''}`,
471
+ result: 'refused',
472
+ });
473
+ }
474
+ catch {
475
+ // audit() swallows its own errors; this covers a thrown path resolution.
476
+ }
477
+ return path;
478
+ }
479
+ /** Read halt records back, newest first. Best-effort; never throws. */
480
+ export function readPostMergeHalts(limit = 20) {
481
+ try {
482
+ const dir = postMergeHaltDir();
483
+ const names = readdirSync(dir)
484
+ .filter((n) => n.startsWith('halt-') && n.endsWith('.json'))
485
+ .sort()
486
+ .reverse()
487
+ .slice(0, Math.max(0, limit));
488
+ const out = [];
489
+ for (const name of names) {
490
+ try {
491
+ const parsed = JSON.parse(readFileSync(join(dir, name), 'utf8'));
492
+ if (parsed?.recordType === 'daemon-post-merge-halt')
493
+ out.push(parsed);
494
+ }
495
+ catch { /* a malformed record is skipped, never fatal */ }
496
+ }
497
+ return out;
498
+ }
499
+ catch {
500
+ return [];
501
+ }
502
+ }
503
+ //# sourceMappingURL=post-merge-halt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"post-merge-halt.js","sourceRoot":"","sources":["../../../src/core/daemon/post-merge-halt.ts"],"names":[],"mappings":"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"}