@astrosheep/keiyaku 2.9.6 → 2.9.8

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 (87) hide show
  1. package/build/.tsbuildinfo +1 -1
  2. package/build/agents/harness/event-persistence.js +7 -5
  3. package/build/agents/harness/events.js +3 -2
  4. package/build/agents/harness/outcome.js +10 -0
  5. package/build/agents/providers/codex-app-server/adapter.js +6 -1
  6. package/build/agents/providers/codex-app-server/session.js +8 -7
  7. package/build/agents/selector.js +12 -1
  8. package/build/cli/commands/akuma/view/handler.js +3 -11
  9. package/build/cli/commands/contract/amend/handler.js +1 -1
  10. package/build/cli/commands/contract/amend/meta.js +4 -4
  11. package/build/cli/commands/contract/bind/handler.js +10 -4
  12. package/build/cli/commands/contract/bind/meta.js +6 -6
  13. package/build/cli/commands/contract/petition/handler.js +16 -3
  14. package/build/cli/commands/contract/petition/meta.js +4 -4
  15. package/build/cli/commands/metadata.js +3 -2
  16. package/build/cli/commands/projection/status/handler.js +5 -4
  17. package/build/cli/commands/projection/status/meta.js +2 -2
  18. package/build/cli/commands/projection/tell/handler.js +10 -2
  19. package/build/cli/commands/projection/tell/meta.js +3 -3
  20. package/build/cli/commands/task/add/meta.js +9 -1
  21. package/build/cli/commands/task/catalog.js +2 -0
  22. package/build/cli/commands/task/log/handler.js +12 -0
  23. package/build/cli/commands/task/log/meta.js +8 -0
  24. package/build/cli/commands/task/shared.js +2 -1
  25. package/build/cli/completion.js +8 -0
  26. package/build/cli/flags.js +8 -0
  27. package/build/cli/index.js +10 -6
  28. package/build/cli/parse-flags.js +6 -0
  29. package/build/cli/parse-metadata.js +1 -1
  30. package/build/cli/render/kanshi.js +2 -2
  31. package/build/cli/render/line-width.js +33 -0
  32. package/build/cli/render/path-prefix-compaction.js +131 -0
  33. package/build/cli/render/petition.js +4 -0
  34. package/build/cli/render/projection-activity.js +103 -21
  35. package/build/cli/render/shared.js +38 -15
  36. package/build/cli/render/status.js +8 -5
  37. package/build/cli/render/success-response.js +2 -0
  38. package/build/cli/render/tool-presentation.js +3 -3
  39. package/build/cli/render/wait.js +68 -48
  40. package/build/cli/subagent-guard.js +3 -0
  41. package/build/cli/types.js +1 -1
  42. package/build/config/settings/disease.js +4 -4
  43. package/build/config/settings/loader.js +44 -21
  44. package/build/core/addressing.js +40 -9
  45. package/build/core/amend.js +21 -5
  46. package/build/core/bind.js +111 -6
  47. package/build/core/call/context.js +19 -3
  48. package/build/core/call/execution.js +43 -20
  49. package/build/core/draft.js +1 -1
  50. package/build/core/ledger-batch.js +194 -0
  51. package/build/core/projection/generation/database.js +43 -2
  52. package/build/core/projection/generation/model.js +24 -3
  53. package/build/core/projection/generation/projection-generation-continuation.js +75 -10
  54. package/build/core/projection/generation/projection-generation-execution.js +46 -37
  55. package/build/core/projection/generation/projection-generation-launcher.js +148 -20
  56. package/build/core/projection/generation/projection-generation-process.js +3 -1
  57. package/build/core/projection/generation/projection-generation-runner.js +145 -66
  58. package/build/core/projection/generation/projection-generation-runtime.js +93 -11
  59. package/build/core/projection/generation/store.js +26 -1
  60. package/build/core/projection/generation/transitions.js +193 -14
  61. package/build/core/projection/index.js +5 -5
  62. package/build/core/projection/projection-core.js +1 -1
  63. package/build/core/projection/projection-kill.js +53 -10
  64. package/build/core/projection/projection-life-protocol.js +10 -0
  65. package/build/core/projection/projection-runner-lock.js +177 -37
  66. package/build/core/projection/projection-status.js +143 -55
  67. package/build/core/projection/projection-wait.js +53 -15
  68. package/build/core/projection/projection-wake.js +177 -46
  69. package/build/core/projection/tell/database.js +18 -0
  70. package/build/core/projection/tell/model.js +1 -0
  71. package/build/core/projection/tell/store.js +102 -55
  72. package/build/core/registry.js +82 -69
  73. package/build/core/scope.js +9 -9
  74. package/build/core/status/board.js +42 -4
  75. package/build/core/status/drift.js +21 -5
  76. package/build/core/status/ledger-batch.js +1 -158
  77. package/build/core/task/index.js +2 -2
  78. package/build/core/task/task-contract.js +18 -0
  79. package/build/core/task/task-git-runtime.js +8 -10
  80. package/build/core/task/task-git-store.js +55 -3
  81. package/build/core/worktree-path.js +39 -25
  82. package/build/flow-error.js +1 -1
  83. package/build/generated/version.js +2 -2
  84. package/build/git/refs.js +47 -1
  85. package/package.json +1 -1
  86. package/skills/keiyaku-akuma/SKILL.md +28 -0
  87. package/skills/keiyaku-workflow/SKILL.md +142 -18
@@ -76,6 +76,8 @@ export async function runProjectionGeneration(input) {
76
76
  let settled = false;
77
77
  let tellObserver;
78
78
  let graceTimer;
79
+ let intentTimer;
80
+ let handle;
79
81
  const clearGraceTimer = () => {
80
82
  if (graceTimer === undefined)
81
83
  return;
@@ -83,15 +85,19 @@ export async function runProjectionGeneration(input) {
83
85
  graceTimer = undefined;
84
86
  };
85
87
  try {
88
+ runnerLock = input.runnerLock
89
+ ?? acquireRunnerLockOrObserveDuplicate(input.projectionDirectory);
90
+ if (!runnerLock)
91
+ return { status: "duplicate" };
86
92
  const initial = store.readCurrentGeneration();
87
93
  if (initial?.launch.executionId !== input.executionId
88
94
  || initial.adoption
89
95
  || initial.verdict) {
90
96
  return { status: "rejected" };
91
97
  }
92
- runnerLock = acquireRunnerLockOrObserveDuplicate(input.projectionDirectory);
93
- if (!runnerLock)
94
- return { status: "duplicate" };
98
+ tellObserver = openProjectionTellObserver(input.projectionDirectory);
99
+ // Prime before adoption so a concurrent stop write cannot become the baseline.
100
+ tellObserver.hasDataVersionChanged();
95
101
  const adoption = store.adoptionIfCurrent({
96
102
  executionId: input.executionId,
97
103
  facts: { adoptedAt: new Date(now()).toISOString() },
@@ -99,11 +105,12 @@ export async function runProjectionGeneration(input) {
99
105
  if (adoption.status !== "committed")
100
106
  return { status: "rejected" };
101
107
  adopted = true;
102
- const current = store.readCurrentGeneration();
108
+ let current = store.readCurrentGeneration();
103
109
  if (current?.launch.executionId !== input.executionId) {
104
110
  throw new Error("adopted generation stopped being current");
105
111
  }
106
- // Pre-admission kill: skip provider work and commit killed directly.
112
+ // Adoption is the provider-admission boundary. A kill that committed first
113
+ // settles without constructing a provider execution.
107
114
  if (current.killIntent) {
108
115
  const killed = store.runnerKilledIfRequested({ executionId: input.executionId });
109
116
  if (killed.status !== "committed")
@@ -111,12 +118,61 @@ export async function runProjectionGeneration(input) {
111
118
  settled = true;
112
119
  return { status: "completed" };
113
120
  }
114
- const handle = input.execute(current.launch);
115
- tellObserver = openProjectionTellObserver(input.projectionDirectory);
121
+ const prepared = await input.prepareExecution(current.launch);
122
+ current = store.readCurrentGeneration();
123
+ if (current?.launch.executionId !== input.executionId) {
124
+ await prepared.dispose();
125
+ throw new Error("prepared generation stopped being current");
126
+ }
127
+ if (current.killIntent) {
128
+ await prepared.dispose();
129
+ const killed = store.runnerKilledIfRequested({ executionId: input.executionId });
130
+ if (killed.status !== "committed")
131
+ return { status: "rejected" };
132
+ settled = true;
133
+ return { status: "completed" };
134
+ }
135
+ handle = prepared.start();
136
+ const settleInterrupted = (facts = {}) => {
137
+ const transition = store.runnerInterruptedIfRequested({
138
+ executionId: input.executionId,
139
+ facts,
140
+ terminalAt: new Date(now()).toISOString(),
141
+ });
142
+ if (transition.status !== "committed")
143
+ return { status: "rejected" };
144
+ settled = true;
145
+ runnerLock.close();
146
+ runnerLock = undefined;
147
+ void input.wakeAfterInterrupt?.().catch(() => {
148
+ // The ordinary wake path persists its typed failure. A later tell can retry.
149
+ });
150
+ return { status: "completed" };
151
+ };
152
+ const settleRunnerOutcome = (execution) => {
153
+ const transition = store.runnerVerdictIfOpen({
154
+ executionId: input.executionId,
155
+ verdict: execution.verdict,
156
+ facts: execution.facts,
157
+ terminalAt: new Date(now()).toISOString(),
158
+ });
159
+ if (transition.status !== "committed")
160
+ return { status: "rejected" };
161
+ settled = true;
162
+ if (transition.records[0]?.facts.state === "interrupted") {
163
+ runnerLock.close();
164
+ runnerLock = undefined;
165
+ void input.wakeAfterInterrupt?.().catch(() => {
166
+ // The ordinary wake path persists its typed failure. A later observer retries.
167
+ });
168
+ }
169
+ return { status: "completed" };
170
+ };
116
171
  let gracefulAbortInvoked = false;
117
172
  let forceAbortInvoked = false;
118
173
  let sawKillIntent = false;
119
- const observeKillBoundary = () => {
174
+ let sawInterruptIntent = false;
175
+ const observeStopBoundary = () => {
120
176
  try {
121
177
  if (!tellObserver.hasDataVersionChanged())
122
178
  return;
@@ -125,9 +181,10 @@ export async function runProjectionGeneration(input) {
125
181
  return;
126
182
  if (live.verdict)
127
183
  return;
128
- if (!live.killIntent)
184
+ sawKillIntent = live.killIntent !== undefined;
185
+ sawInterruptIntent = live.interruptIntent !== undefined;
186
+ if (!sawKillIntent && !sawInterruptIntent)
129
187
  return;
130
- sawKillIntent = true;
131
188
  if (gracefulAbortInvoked)
132
189
  return;
133
190
  gracefulAbortInvoked = true;
@@ -153,12 +210,12 @@ export async function runProjectionGeneration(input) {
153
210
  const outcomeWatch = handle.outcome.then((execution) => {
154
211
  outcomeBox.status = "fulfilled";
155
212
  outcomeBox.execution = execution;
156
- observeKillBoundary();
213
+ observeStopBoundary();
157
214
  bumpActivity();
158
215
  }, (error) => {
159
216
  outcomeBox.status = "rejected";
160
217
  outcomeBox.error = error;
161
- observeKillBoundary();
218
+ observeStopBoundary();
162
219
  bumpActivity();
163
220
  });
164
221
  // Always rejection-observe so abandon paths never surface unhandled rejections.
@@ -168,7 +225,7 @@ export async function runProjectionGeneration(input) {
168
225
  for await (const _event of handle.events) {
169
226
  if (forceAbortInvoked)
170
227
  return;
171
- observeKillBoundary();
228
+ observeStopBoundary();
172
229
  bumpActivity();
173
230
  }
174
231
  }
@@ -178,10 +235,17 @@ export async function runProjectionGeneration(input) {
178
235
  bumpActivity();
179
236
  })();
180
237
  observeAbandoned(eventDrain);
181
- while (outcomeBox.status === "pending" && !sawKillIntent) {
238
+ // A provider may be silently executing when an operator interrupts it.
239
+ // The durable observer is the sole stop-intent authority in that interval.
240
+ intentTimer = setInterval(() => {
241
+ observeStopBoundary();
242
+ bumpActivity();
243
+ }, 100);
244
+ intentTimer.unref?.();
245
+ while (outcomeBox.status === "pending" && !sawKillIntent && !sawInterruptIntent) {
182
246
  await activityChanged();
183
247
  }
184
- if (sawKillIntent) {
248
+ if (sawKillIntent || sawInterruptIntent) {
185
249
  // Grace race: outcome settlement inside the window is stop proof.
186
250
  if (outcomeBox.status === "pending") {
187
251
  const graceExpired = new Promise((resolve) => {
@@ -201,6 +265,16 @@ export async function runProjectionGeneration(input) {
201
265
  const facts = killOutcome.status === "fulfilled"
202
266
  ? nonReservedRunnerKillFacts(killOutcome.execution.facts)
203
267
  : undefined;
268
+ if (sawInterruptIntent && killOutcome.status === "fulfilled" && killOutcome.execution.verdict === "interrupted") {
269
+ return settleInterrupted(facts);
270
+ }
271
+ if (sawInterruptIntent) {
272
+ if (killOutcome.status === "fulfilled")
273
+ return settleRunnerOutcome(killOutcome.execution);
274
+ if (killOutcome.status === "rejected")
275
+ throw killOutcome.error;
276
+ throw new Error("interrupt outcome did not establish provider stop");
277
+ }
204
278
  const killed = store.runnerKilledIfRequested({
205
279
  executionId: input.executionId,
206
280
  ...(facts && Object.keys(facts).length > 0 ? { facts } : {}),
@@ -214,6 +288,8 @@ export async function runProjectionGeneration(input) {
214
288
  forceAbortInvoked = true;
215
289
  try {
216
290
  await handle.abort("force");
291
+ if (sawInterruptIntent)
292
+ return settleInterrupted();
217
293
  const killed = store.runnerKilledIfRequested({ executionId: input.executionId });
218
294
  if (killed.status !== "committed")
219
295
  return { status: "rejected" };
@@ -225,7 +301,7 @@ export async function runProjectionGeneration(input) {
225
301
  executionId: input.executionId,
226
302
  verdict: "failed",
227
303
  facts: {
228
- operatorAction: "kill",
304
+ ...(sawKillIntent ? { operatorAction: "kill" } : {}),
229
305
  diagnostic: "escalation-failed",
230
306
  detail: errorDetail(error),
231
307
  failedAt: new Date(now()).toISOString(),
@@ -261,83 +337,86 @@ export async function runProjectionGeneration(input) {
261
337
  const tellFence = normalizeTellFence(pending.tellIds);
262
338
  if (tellFence.length > 0) {
263
339
  const successorExecutionId = createProjectionExecutionId(now());
264
- const transition = store.handoff({
265
- executionId: input.executionId,
266
- completionFacts: execution.facts,
267
- successorExecutionId,
268
- successorFacts: successorLaunchFacts(current.launch, execution.facts, pending.effort, now),
269
- tellFence,
270
- });
271
- if (transition.status !== "committed")
340
+ const successorFacts = successorLaunchFacts(current.launch, execution.facts, pending.effort, now);
341
+ let transitionReason;
342
+ if (!input.startSuccessor)
272
343
  return { status: "rejected" };
273
- settled = true;
274
- // Handoff makes the predecessor terminal before life authority moves.
275
- // A held projection lock must always mean that a current runner exists,
276
- // so release it before the successor makes its one-shot acquisition.
277
- runnerLock.close();
278
- runnerLock = undefined;
279
344
  try {
280
- input.spawnSuccessor?.(successorExecutionId);
345
+ const successorOwnership = runnerLock;
346
+ await input.startSuccessor(successorExecutionId, () => {
347
+ const transition = store.handoff({
348
+ executionId: input.executionId,
349
+ completionFacts: execution.facts,
350
+ successorExecutionId,
351
+ successorFacts,
352
+ tellFence,
353
+ });
354
+ if (transition.status !== "committed") {
355
+ transitionReason = transition.reason;
356
+ return false;
357
+ }
358
+ settled = true;
359
+ return true;
360
+ }, successorOwnership);
361
+ runnerLock = undefined;
281
362
  }
282
- catch (error) {
283
- store.verdictIfOpen({
284
- executionId: successorExecutionId,
285
- verdict: "launch-failed",
286
- facts: {
287
- stage: "spawn",
288
- detail: `successor runner spawn failed: ${errorDetail(error)}`,
289
- terminalAt: new Date(now()).toISOString(),
290
- },
291
- });
363
+ catch {
364
+ if (settled)
365
+ return { status: "handed-off", successorExecutionId };
366
+ return settleRunnerOutcome(execution);
292
367
  }
293
- return { status: "handed-off", successorExecutionId };
294
- }
295
- const verdict = store.verdictIfOpen({
296
- executionId: input.executionId,
297
- verdict: execution.verdict,
298
- facts: execution.facts,
299
- });
300
- if (verdict.status !== "committed") {
301
- // Kill intent may commit after the last outcome-boundary observation and
302
- // before this transaction. The settled outcome is already stop proof.
303
- if (verdict.reason === "kill-requested") {
304
- const facts = nonReservedRunnerKillFacts(execution.facts);
305
- const killed = store.runnerKilledIfRequested({
306
- executionId: input.executionId,
307
- ...(Object.keys(facts).length > 0 ? { facts } : {}),
308
- });
309
- if (killed.status !== "committed")
368
+ if (!settled) {
369
+ if (transitionReason !== "kill-requested" && transitionReason !== "interrupt-requested") {
310
370
  return { status: "rejected" };
311
- settled = true;
312
- return { status: "completed" };
371
+ }
372
+ }
373
+ else {
374
+ return { status: "handed-off", successorExecutionId };
313
375
  }
314
- return { status: "rejected" };
315
376
  }
316
- settled = true;
317
- return { status: "completed" };
377
+ return settleRunnerOutcome(execution);
318
378
  }
319
379
  catch (error) {
320
380
  if (adopted && !settled) {
321
- store.verdictIfOpen({
381
+ let cleanupError;
382
+ if (handle) {
383
+ try {
384
+ await handle.abort("force");
385
+ }
386
+ catch (failure) {
387
+ cleanupError = failure;
388
+ }
389
+ }
390
+ const live = store.readCurrentGeneration();
391
+ const detail = error instanceof Error ? error.message : String(error);
392
+ store.runnerVerdictIfOpen({
322
393
  executionId: input.executionId,
323
394
  verdict: "failed",
324
395
  facts: {
325
396
  failedAt: new Date(now()).toISOString(),
326
- detail: error instanceof Error ? error.message : String(error),
397
+ detail,
398
+ ...(cleanupError ? {
399
+ ...(live?.killIntent ? { operatorAction: "kill" } : {}),
400
+ diagnostic: "cleanup-escalation-failed",
401
+ cleanupDetail: errorDetail(cleanupError),
402
+ } : {}),
327
403
  error: {
328
404
  version: OUTCOME_ERROR_VERSION,
329
405
  code: "SUBAGENT_EXEC_ERROR",
330
- message: error instanceof Error ? error.message : String(error),
406
+ message: detail,
331
407
  timeoutMs: null,
332
408
  exitCode: null,
333
409
  },
334
410
  },
411
+ terminalAt: new Date(now()).toISOString(),
335
412
  });
336
413
  }
337
414
  throw error;
338
415
  }
339
416
  finally {
340
417
  clearGraceTimer();
418
+ if (intentTimer !== undefined)
419
+ clearInterval(intentTimer);
341
420
  try {
342
421
  tellObserver?.close();
343
422
  }
@@ -1,15 +1,97 @@
1
- import { executeProjectionGeneration } from "./projection-generation-execution.js";
2
- import { spawnProjectionGenerationRunner } from "./projection-generation-process.js";
1
+ import { prepareProjectionGenerationExecution } from "./projection-generation-execution.js";
2
+ import { PROJECTION_RUNNER_BOOTSTRAP_ENV, } from "./projection-generation-process.js";
3
3
  import { runProjectionGeneration } from "./projection-generation-runner.js";
4
+ import { PROJECTION_ADOPTION_TIMEOUT_MS } from "./projection-generation-launcher.js";
5
+ import { startProjectionGeneration } from "./projection-generation-launcher.js";
4
6
  import { snapshotPendingTells } from "./projection-generation-continuation.js";
5
- export async function runProjectionGenerationRuntime(projectionDirectory, executionId) {
6
- await runProjectionGeneration({
7
- projectionDirectory,
8
- executionId,
9
- execute: (launch) => executeProjectionGeneration(projectionDirectory, launch),
10
- snapshotPendingTells: () => snapshotPendingTells(projectionDirectory),
11
- spawnSuccessor: (successorExecutionId) => {
12
- spawnProjectionGenerationRunner(projectionDirectory, successorExecutionId);
13
- },
7
+ import { persistInterruptWakeFailure, tellProjection } from "../projection-wake.js";
8
+ import { readProjectionIdentity } from "../projection-identity.js";
9
+ import * as path from "node:path";
10
+ import { acquireProjectionRunnerTransferTarget } from "../projection-runner-lock.js";
11
+ function sendBootstrapMessage(message) {
12
+ return new Promise((resolve, reject) => {
13
+ if (!process.send || !process.connected) {
14
+ reject(new Error("projection runner bootstrap channel is unavailable"));
15
+ return;
16
+ }
17
+ process.send(message, (error) => error ? reject(error) : resolve());
18
+ });
19
+ }
20
+ function waitForLaunchCommand(executionId, expected) {
21
+ return new Promise((resolve, reject) => {
22
+ const cleanup = () => {
23
+ process.off("message", onMessage);
24
+ process.off("disconnect", onDisconnect);
25
+ };
26
+ const onDisconnect = () => {
27
+ cleanup();
28
+ reject(new Error("projection runner bootstrap channel disconnected before launch authorization"));
29
+ };
30
+ const onMessage = (value) => {
31
+ const command = value;
32
+ if (command.executionId !== executionId)
33
+ return;
34
+ if (expected === "promote-leash" && command.kind !== "promote-leash" && command.kind !== "launch-rejected")
35
+ return;
36
+ if (expected === "launch-authority" && command.kind !== "launch-committed" && command.kind !== "launch-rejected")
37
+ return;
38
+ cleanup();
39
+ resolve(command);
40
+ };
41
+ process.on("message", onMessage);
42
+ process.on("disconnect", onDisconnect);
14
43
  });
15
44
  }
45
+ export async function wakeProjectionAfterInterrupt(projectionDirectory, wake = tellProjection) {
46
+ const pact = readProjectionIdentity(projectionDirectory);
47
+ try {
48
+ await wake(projectionDirectory, `${pact.akuma}/${path.basename(projectionDirectory)}`, undefined);
49
+ }
50
+ catch (error) {
51
+ persistInterruptWakeFailure(projectionDirectory, error);
52
+ throw error;
53
+ }
54
+ }
55
+ export async function runProjectionGenerationRuntime(projectionDirectory, executionId) {
56
+ if (process.env[PROJECTION_RUNNER_BOOTSTRAP_ENV] !== "1") {
57
+ throw new Error("projection runner was not spawned with inherited leash coordination");
58
+ }
59
+ let runnerLock;
60
+ let transferTarget;
61
+ try {
62
+ transferTarget = acquireProjectionRunnerTransferTarget(projectionDirectory, process.platform, PROJECTION_ADOPTION_TIMEOUT_MS);
63
+ await sendBootstrapMessage({ kind: "leash-inherited", executionId });
64
+ const promotion = await waitForLaunchCommand(executionId, "promote-leash");
65
+ if (promotion.kind === "launch-rejected")
66
+ return;
67
+ runnerLock = transferTarget.promote();
68
+ transferTarget = undefined;
69
+ await sendBootstrapMessage({ kind: "leash-acquired", executionId });
70
+ const command = await waitForLaunchCommand(executionId, "launch-authority");
71
+ if (command.kind === "launch-rejected")
72
+ return;
73
+ process.disconnect?.();
74
+ const ownedLock = runnerLock;
75
+ runnerLock = undefined;
76
+ await runProjectionGeneration({
77
+ projectionDirectory,
78
+ executionId,
79
+ runnerLock: ownedLock,
80
+ prepareExecution: (launch) => prepareProjectionGenerationExecution(projectionDirectory, launch),
81
+ snapshotPendingTells: () => snapshotPendingTells(projectionDirectory),
82
+ startSuccessor: async (successorExecutionId, commitLaunch, launchOwnership) => {
83
+ const started = await startProjectionGeneration(projectionDirectory, successorExecutionId, {
84
+ commitLaunch,
85
+ launchOwnership,
86
+ });
87
+ if (started.status === "failed")
88
+ throw new Error(started.detail);
89
+ },
90
+ wakeAfterInterrupt: () => wakeProjectionAfterInterrupt(projectionDirectory),
91
+ });
92
+ }
93
+ finally {
94
+ runnerLock?.close();
95
+ transferTarget?.close();
96
+ }
97
+ }
@@ -1,4 +1,4 @@
1
- import { PROJECTION_GENERATION_SCHEMA_VERSION, PROJECTION_GENERATION_STORE_FILE, assertDatabaseOpen, closeDatabase, openMutableDatabase, openReadOnlyDatabase, readValidatedHistory, } from "./database.js";
1
+ import { PROJECTION_GENERATION_SCHEMA_VERSION, PROJECTION_GENERATION_STORE_FILE, assertDatabaseOpen, beginGenerationWriteTransaction, closeDatabase, commitGenerationWriteTransaction, openMutableDatabase, openReadOnlyDatabase, readValidatedHistory, rollbackGenerationWriteTransaction, } from "./database.js";
2
2
  import { initializeIdentity as initializeProjectionIdentity, readIdentity as readProjectionIdentity, } from "./identity.js";
3
3
  import * as transitions from "./transitions.js";
4
4
  export { PROJECTION_GENERATION_SCHEMA_VERSION, PROJECTION_GENERATION_STORE_FILE, };
@@ -21,8 +21,18 @@ export class ProjectionGenerationStore {
21
21
  return new ProjectionGenerationStore(databasePath, database, true);
22
22
  }
23
23
  close() {
24
+ rollbackGenerationWriteTransaction(this.#database);
24
25
  closeDatabase(this.#database);
25
26
  }
27
+ beginWriteTransaction() {
28
+ beginGenerationWriteTransaction(this.#database, this.readOnly);
29
+ }
30
+ commitWriteTransaction() {
31
+ commitGenerationWriteTransaction(this.#database);
32
+ }
33
+ rollbackWriteTransaction() {
34
+ rollbackGenerationWriteTransaction(this.#database);
35
+ }
26
36
  readRecords() {
27
37
  assertDatabaseOpen(this.#database);
28
38
  return readValidatedHistory(this.#database).records;
@@ -40,15 +50,27 @@ export class ProjectionGenerationStore {
40
50
  launchIfSettled(input) {
41
51
  return transitions.launchIfSettled(this.#database, this.readOnly, input);
42
52
  }
53
+ failInterruptWakeIfSettled(input) {
54
+ return transitions.failInterruptWakeIfSettled(this.#database, this.readOnly, input);
55
+ }
43
56
  adoptionIfCurrent(input) {
44
57
  return transitions.adoptionIfCurrent(this.#database, this.readOnly, input);
45
58
  }
46
59
  requestKillIfOpen(input) {
47
60
  return transitions.requestKillIfOpen(this.#database, this.readOnly, input);
48
61
  }
62
+ requestInterruptIfOpen(input) {
63
+ return transitions.requestInterruptIfOpen(this.#database, this.readOnly, input);
64
+ }
49
65
  runnerKilledIfRequested(input) {
50
66
  return transitions.runnerKilledIfRequested(this.#database, this.readOnly, input);
51
67
  }
68
+ runnerInterruptedIfRequested(input) {
69
+ return transitions.runnerInterruptedIfRequested(this.#database, this.readOnly, input);
70
+ }
71
+ runnerVerdictIfOpen(input) {
72
+ return transitions.runnerVerdictIfOpen(this.#database, this.readOnly, input);
73
+ }
52
74
  settleKillIfRunnerDead(input) {
53
75
  return transitions.settleKillIfRunnerDead(this.#database, this.readOnly, input);
54
76
  }
@@ -58,6 +80,9 @@ export class ProjectionGenerationStore {
58
80
  supersedeStartupTimeout(input) {
59
81
  return transitions.supersedeStartupTimeout(this.#database, this.readOnly, input);
60
82
  }
83
+ settleStartupFailure(input) {
84
+ return transitions.settleStartupFailure(this.#database, this.readOnly, input);
85
+ }
61
86
  replaceDeadRunner(input) {
62
87
  return transitions.replaceDeadRunner(this.#database, this.readOnly, input);
63
88
  }