@astrosheep/keiyaku 2.9.8 → 2.9.10

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 (230) hide show
  1. package/README.md +50 -182
  2. package/build/.tsbuildinfo +1 -1
  3. package/build/agents/harness/control-types.js +18 -1
  4. package/build/agents/harness/index.js +2 -0
  5. package/build/agents/harness/outcome-error.js +81 -0
  6. package/build/agents/harness/projection.js +10 -7
  7. package/build/agents/harness/router.js +5 -1
  8. package/build/agents/providers/claude-agent-sdk/adapter.js +18 -4
  9. package/build/agents/providers/claude-agent-sdk/registration.js +11 -1
  10. package/build/agents/providers/claude-agent-sdk/session.js +19 -5
  11. package/build/agents/providers/codex-app-server/adapter.js +11 -6
  12. package/build/agents/providers/codex-app-server/events.js +67 -6
  13. package/build/agents/providers/codex-app-server/registration.js +11 -1
  14. package/build/agents/providers/opencode-sdk/adapter.js +8 -1
  15. package/build/agents/providers/opencode-sdk/events.js +24 -2
  16. package/build/agents/providers/opencode-sdk/registration.js +11 -1
  17. package/build/agents/providers/pi/checkpoint.js +9 -1
  18. package/build/agents/providers/pi/events.js +2 -0
  19. package/build/agents/providers/pi/registration.js +14 -1
  20. package/build/cli/commands/akuma/akuma/handler.js +1 -1
  21. package/build/cli/commands/akuma/akuma/meta.js +3 -3
  22. package/build/cli/commands/akuma/catalog.js +2 -2
  23. package/build/cli/commands/akuma/{view → show}/handler.js +5 -5
  24. package/build/cli/commands/akuma/{view → show}/meta.js +3 -3
  25. package/build/cli/commands/akuma.js +1 -1
  26. package/build/cli/commands/contract/amend/handler.js +5 -4
  27. package/build/cli/commands/contract/arc/handler.js +5 -4
  28. package/build/cli/commands/contract/audit/handler.js +2 -2
  29. package/build/cli/commands/contract/bind/handler.js +12 -3
  30. package/build/cli/commands/contract/bind/meta.js +45 -15
  31. package/build/cli/commands/contract/forfeit/handler.js +9 -5
  32. package/build/cli/commands/contract/log/handler.js +2 -2
  33. package/build/cli/commands/contract/petition/handler.js +34 -7
  34. package/build/cli/commands/contract/renew/handler.js +7 -7
  35. package/build/cli/commands/contract/renew/meta.js +3 -3
  36. package/build/cli/commands/metadata.js +10 -6
  37. package/build/cli/commands/projection/call/meta.js +1 -1
  38. package/build/cli/commands/projection/catalog.js +2 -0
  39. package/build/cli/commands/projection/history/handler.js +21 -0
  40. package/build/cli/commands/projection/history/meta.js +9 -0
  41. package/build/cli/commands/projection/shared.js +3 -4
  42. package/build/cli/commands/projection/status/handler.js +3 -3
  43. package/build/cli/commands/projection/wait/handler.js +20 -11
  44. package/build/cli/commands/task/add/handler.js +14 -5
  45. package/build/cli/commands/task/add/meta.js +2 -2
  46. package/build/cli/commands/task/catalog.js +4 -0
  47. package/build/cli/commands/task/done/handler.js +1 -4
  48. package/build/cli/commands/task/done/meta.js +1 -1
  49. package/build/cli/commands/task/drop/meta.js +1 -1
  50. package/build/cli/commands/task/hold/handler.js +3 -0
  51. package/build/cli/commands/task/hold/meta.js +8 -0
  52. package/build/cli/commands/task/log/handler.js +1 -1
  53. package/build/cli/commands/task/log/meta.js +2 -2
  54. package/build/cli/commands/task/resume/handler.js +3 -0
  55. package/build/cli/commands/task/resume/meta.js +8 -0
  56. package/build/cli/commands/task/shared.js +39 -14
  57. package/build/cli/commands/task/show/handler.js +1 -1
  58. package/build/cli/commands/task/show/meta.js +1 -1
  59. package/build/cli/commands/task/start/meta.js +1 -1
  60. package/build/cli/commands/task/stop/handler.js +1 -4
  61. package/build/cli/commands/task/stop/meta.js +1 -1
  62. package/build/cli/commands/task/update/handler.js +11 -3
  63. package/build/cli/commands/task/update/meta.js +3 -3
  64. package/build/cli/completion.js +3 -2
  65. package/build/cli/flags.js +51 -14
  66. package/build/cli/help.js +4 -1
  67. package/build/cli/index.js +40 -25
  68. package/build/cli/parse-flags.js +40 -7
  69. package/build/cli/parse-metadata.js +5 -2
  70. package/build/cli/parse-selectors.js +2 -3
  71. package/build/cli/parse.js +40 -6
  72. package/build/cli/projection-address.js +12 -10
  73. package/build/cli/render/amendment-warning.js +8 -0
  74. package/build/cli/render/arc.js +6 -2
  75. package/build/cli/render/audit.js +400 -81
  76. package/build/cli/render/kanshi.js +242 -79
  77. package/build/cli/render/lifecycle.js +56 -0
  78. package/build/cli/render/misc.js +24 -28
  79. package/build/cli/render/petition.js +5 -0
  80. package/build/cli/render/projection-activity.js +65 -29
  81. package/build/cli/render/projection-history.js +64 -0
  82. package/build/cli/render/shared.js +23 -9
  83. package/build/cli/render/status.js +13 -35
  84. package/build/cli/render/task.js +51 -26
  85. package/build/cli/render/terminal-failure.js +3 -1
  86. package/build/cli/render/wait.js +45 -69
  87. package/build/cli/subagent-guard.js +1 -1
  88. package/build/cli/types.js +1 -1
  89. package/build/config/env-keys.js +3 -1
  90. package/build/config/env.js +24 -1
  91. package/build/config/settings/schema.js +3 -1
  92. package/build/core/address-carrier.js +89 -0
  93. package/build/core/addressing.js +255 -196
  94. package/build/core/amend.js +115 -38
  95. package/build/core/amendment-criteria.js +17 -0
  96. package/build/core/amendment-warning.js +10 -0
  97. package/build/core/arc.js +184 -83
  98. package/build/core/audit/candidate.js +110 -9
  99. package/build/core/audit/coordinates.js +120 -65
  100. package/build/core/audit/evidence.js +19 -23
  101. package/build/core/audit/facade.js +34 -11
  102. package/build/core/audit/report.js +5 -5
  103. package/build/core/bind-reconciliation.js +390 -0
  104. package/build/core/bind-workspace.js +12 -20
  105. package/build/core/bind.js +242 -273
  106. package/build/core/call/call.js +1 -0
  107. package/build/core/call/context.js +46 -71
  108. package/build/core/call/prompt.js +6 -3
  109. package/build/core/context.js +49 -35
  110. package/build/core/contract-carrier-runtime.js +408 -0
  111. package/build/core/contract-carrier.js +270 -0
  112. package/build/core/contract-view.js +33 -27
  113. package/build/core/contract.js +5 -1
  114. package/build/core/derived-replay.js +207 -0
  115. package/build/core/draft.js +125 -57
  116. package/build/core/forfeit.js +113 -56
  117. package/build/core/identifier-slug.js +63 -13
  118. package/build/core/ids.js +4 -4
  119. package/build/core/lifecycle-history.js +35 -0
  120. package/build/core/lifecycle-recovery.js +117 -0
  121. package/build/core/lifecycle-runner.js +377 -0
  122. package/build/core/log.js +37 -11
  123. package/build/core/outcome-base.js +181 -0
  124. package/build/core/process-group.js +139 -0
  125. package/build/core/projection/generation/database.js +82 -7
  126. package/build/core/projection/generation/model.js +9 -36
  127. package/build/core/projection/generation/projection-generation-continuation.js +10 -22
  128. package/build/core/projection/generation/projection-generation-execution.js +13 -6
  129. package/build/core/projection/index.js +5 -4
  130. package/build/core/projection/projection-activity.js +29 -8
  131. package/build/core/projection/projection-alias.js +23 -10
  132. package/build/core/projection/projection-core.js +21 -7
  133. package/build/core/projection/projection-execution-observer.js +91 -39
  134. package/build/core/projection/projection-history.js +474 -0
  135. package/build/core/projection/projection-runner-lock.js +12 -0
  136. package/build/core/projection/projection-status-observation.js +365 -0
  137. package/build/core/projection/projection-status.js +35 -337
  138. package/build/core/projection/projection-terminal-failure.js +13 -8
  139. package/build/core/projection/projection-wait.js +7 -2
  140. package/build/core/projection/tell/launch-store.js +244 -0
  141. package/build/core/projection/tell/model.js +3 -3
  142. package/build/core/projection/tell/store.js +70 -118
  143. package/build/core/registry.js +22 -87
  144. package/build/core/renew-build.js +424 -0
  145. package/build/core/renew-plan.js +239 -0
  146. package/build/core/renew-rewrite.js +77 -0
  147. package/build/core/renew.js +295 -520
  148. package/build/core/repository-ledger/accepted-fold-read.js +19 -0
  149. package/build/core/repository-ledger/accepted-tail.js +116 -0
  150. package/build/core/repository-ledger/admission.js +38 -0
  151. package/build/core/repository-ledger/authoritative-transaction.js +1 -0
  152. package/build/core/repository-ledger/claim-evidence.js +147 -0
  153. package/build/core/repository-ledger/codec.js +297 -0
  154. package/build/core/repository-ledger/current-state-store.js +853 -0
  155. package/build/core/repository-ledger/fold-repository.js +375 -0
  156. package/build/core/repository-ledger/fold.js +248 -0
  157. package/build/core/repository-ledger/git-object-store.js +243 -0
  158. package/build/core/repository-ledger/history-read.js +15 -0
  159. package/build/core/repository-ledger/incremental-read.js +153 -0
  160. package/build/core/repository-ledger/index.js +15 -0
  161. package/build/core/repository-ledger/inventory-plan.js +75 -0
  162. package/build/core/repository-ledger/inventory.js +310 -0
  163. package/build/core/repository-ledger/publication-admission.js +125 -0
  164. package/build/core/repository-ledger/publication.js +353 -0
  165. package/build/core/repository-ledger/read-model.js +469 -0
  166. package/build/core/repository-ledger/transaction-membership.js +78 -0
  167. package/build/core/repository-ledger/traversal.js +404 -0
  168. package/build/core/repository-ledger/write-transaction.js +339 -0
  169. package/build/core/seal.js +168 -161
  170. package/build/core/settlement/claim-delivery.js +42 -215
  171. package/build/core/settlement/claim.js +235 -153
  172. package/build/core/settlement/index.js +4 -4
  173. package/build/core/settlement/petition-claim-gates.js +52 -33
  174. package/build/core/settlement/petition-forfeit.js +12 -5
  175. package/build/core/settlement/petition-head-guard.js +8 -5
  176. package/build/core/settlement/petition-preview.js +68 -56
  177. package/build/core/settlement/petition-window.js +50 -0
  178. package/build/core/settlement/petition.js +252 -131
  179. package/build/core/settlement/queue-read-model.js +72 -95
  180. package/build/core/settlement/queue-seat-allocation.js +35 -126
  181. package/build/core/settlement/queue.js +1 -1
  182. package/build/core/settlement/settlement.js +172 -80
  183. package/build/core/settlement/verdict.js +112 -66
  184. package/build/core/settlement/verification-coordination.js +305 -0
  185. package/build/core/settlement/verification-supervisor.js +275 -0
  186. package/build/core/settlement/verification.js +398 -31
  187. package/build/core/status/board.js +125 -127
  188. package/build/core/status/lifecycle.js +7 -80
  189. package/build/core/structured-query.js +23 -0
  190. package/build/core/target-ref.js +1 -1
  191. package/build/core/task/board.js +59 -24
  192. package/build/core/task/commands.js +181 -33
  193. package/build/core/task/coordinate.js +56 -0
  194. package/build/core/task/document.js +25 -9
  195. package/build/core/task/index.js +7 -4
  196. package/build/core/task/query.js +17 -27
  197. package/build/core/task/settlement-git.js +164 -179
  198. package/build/core/task/settlement-policy.js +15 -11
  199. package/build/core/task/source-board.js +31 -13
  200. package/build/core/task/task-bind-preparation.js +193 -0
  201. package/build/core/task/task-contract.js +102 -193
  202. package/build/core/task/task-file-transaction.js +171 -0
  203. package/build/core/task/task-git-runtime.js +76 -14
  204. package/build/core/task/task-git-store.js +68 -169
  205. package/build/core/task/task-store-repository.js +13 -6
  206. package/build/core/task/task-worktree.js +6 -33
  207. package/build/core/task/task.js +1 -1
  208. package/build/core/task/tree.js +3 -3
  209. package/build/core/task/validation.js +4 -3
  210. package/build/core/transcripts.js +93 -1
  211. package/build/core/worktree-bootstrap.js +44 -36
  212. package/build/core/worktree-path.js +210 -77
  213. package/build/flow-error.js +7 -7
  214. package/build/generated/version.js +2 -2
  215. package/build/git/core.js +76 -13
  216. package/build/git/refs.js +168 -46
  217. package/build/git/streaming-batch.js +73 -13
  218. package/package.json +1 -1
  219. package/skills/keiyaku/SKILL.md +1 -1
  220. package/skills/keiyaku-akuma/SKILL.md +3 -3
  221. package/skills/keiyaku-task/SKILL.md +4 -2
  222. package/skills/keiyaku-workflow/SKILL.md +88 -203
  223. package/build/core/entry.js +0 -305
  224. package/build/core/ledger-batch.js +0 -194
  225. package/build/core/ledger.js +0 -84
  226. package/build/core/projection/leash.js +0 -87
  227. package/build/core/ref-log.js +0 -119
  228. package/build/core/renew-session.js +0 -128
  229. package/build/core/status/ledger-batch.js +0 -1
  230. package/build/core/status/reconciliation.js +0 -137
@@ -0,0 +1,408 @@
1
+ import * as fs from "node:fs/promises";
2
+ import { FlowError } from "../flow-error.js";
3
+ import { replayCommittedDerivedState } from "./derived-replay.js";
4
+ import { EffectJournal } from "./outcome-base.js";
5
+ import { readRepositoryLifecycle } from "./repository-ledger/read-model.js";
6
+ import { REPOSITORY_LEDGER_REF } from "./repository-ledger/codec.js";
7
+ import { resolveAcceptedLifecycleTransaction } from "./repository-ledger/accepted-tail.js";
8
+ import { publishAuthoritativeTransaction } from "./repository-ledger/write-transaction.js";
9
+ import { cleanupContractWorkspace, contractWorktreePathFromBind, observeTerminalDeliveryRef, resolveContractCarrierTarget, stableRepoRoot } from "./worktree-path.js";
10
+ import { defaultCreateWorktree } from "./bind-workspace.js";
11
+ import { replayClaimTargetWorktree } from "./settlement/claim-delivery.js";
12
+ let acceptedBind, acquireGate, carrierMatches, checkCancellation;
13
+ let observe, observedLedgerHead, projectRenewDeliveryWorktree, publishCarrier;
14
+ let readCarrierIdentity, reconcileFoldedDeliveryWorktree, releaseGate, resolveCommitOrNull, retireTerminalHereCarrier;
15
+ export function installCarrierRuntimePorts(ports) {
16
+ ({ acceptedBind, acquireGate, carrierMatches, checkCancellation, observe, observedLedgerHead,
17
+ projectRenewDeliveryWorktree, publishCarrier, readCarrierIdentity, reconcileFoldedDeliveryWorktree,
18
+ releaseGate, resolveCommitOrNull, retireTerminalHereCarrier } = ports);
19
+ }
20
+ export async function withContractCarrierSession(input, action) {
21
+ const stableRoot = await stableRepoRoot(input.cwd);
22
+ if (input.mode === "observe-only") {
23
+ checkCancellation(input.signal);
24
+ const { repository, lifecycle, target, pins } = await observe(stableRoot, input.contractId);
25
+ const projectedCarrier = target.status === "available" ? await readCarrierIdentity(target.path) : null;
26
+ const initialCarrier = target.status === "available"
27
+ ? { status: carrierMatches(projectedCarrier, input.contractId, lifecycle.observedLedgerHead) ? "current" : "stale", ledgerHead: lifecycle.observedLedgerHead, target: target.path }
28
+ : { status: "target-unavailable", ledgerHead: lifecycle.observedLedgerHead, reason: target.reason };
29
+ return action(createSession(input.mode, stableRoot, input.contractId, repository, lifecycle, target, pins, initialCarrier, input.signal, input.contractViewWriter, input.renewDeliveryProjector, input.terminalWorkspaceCleanup));
30
+ }
31
+ // Closure/fold observation can be comparatively expensive. Do it before the
32
+ // repository-wide carrier gate, then admit the snapshot only if the sole
33
+ // authoritative coordinate still names the same ledger tip under the gate.
34
+ const preGateObserved = await observe(stableRoot, input.contractId);
35
+ const gate = await acquireGate(stableRoot, input.signal);
36
+ try {
37
+ checkCancellation(input.signal);
38
+ const observedHead = await resolveCommitOrNull(stableRoot, REPOSITORY_LEDGER_REF);
39
+ const { repository, lifecycle, target, pins } = observedHead === preGateObserved.lifecycle.observedLedgerHead
40
+ ? preGateObserved
41
+ : await observe(stableRoot, input.contractId);
42
+ const projectedCarrier = target.status === "available" ? await readCarrierIdentity(target.path) : null;
43
+ const initialCarrier = target.status === "available"
44
+ ? { status: carrierMatches(projectedCarrier, input.contractId, lifecycle.observedLedgerHead) ? "current" : "stale", ledgerHead: lifecycle.observedLedgerHead, target: target.path }
45
+ : { status: "target-unavailable", ledgerHead: lifecycle.observedLedgerHead, reason: target.reason };
46
+ checkCancellation(input.signal);
47
+ const session = createSession(input.mode, stableRoot, input.contractId, repository, lifecycle, target, pins, initialCarrier, input.signal, input.contractViewWriter, input.renewDeliveryProjector, input.terminalWorkspaceCleanup);
48
+ return await action(session);
49
+ }
50
+ finally {
51
+ releaseGate(gate);
52
+ }
53
+ }
54
+ function createSession(mode, stableRoot, contractId, repository, lifecycle, target, pins, initialCarrier, signal, contractViewWriter, renewDeliveryProjector, terminalWorkspaceCleanup) {
55
+ const admittedLedgerHead = lifecycle.observedLedgerHead;
56
+ const admittedLifecycleTail = lifecycle.history.at(-1)?.objectId;
57
+ if (!admittedLifecycleTail)
58
+ throw new FlowError("INTERNAL_STATE", `carrier admission ${contractId} has no accepted lifecycle tail`);
59
+ let currentLifecycle = lifecycle;
60
+ let authoritativeTransactionSelected = false;
61
+ let effectsStarted = false;
62
+ let committedResult;
63
+ const journal = new EffectJournal(`${contractId}:${lifecycle.observedLedgerHead}`);
64
+ async function finalizeCommitted(input) {
65
+ effectsStarted = true;
66
+ if (input.renewProjection) {
67
+ await journal.run({
68
+ effect: "delivery-worktree-projection",
69
+ idempotencyKey: `${contractId}:${input.head}:delivery-worktree-projection`,
70
+ run: async () => (renewDeliveryProjector ?? projectRenewDeliveryWorktree)(input.renewProjection.target, input.renewProjection.oldHead, input.renewProjection.newHead),
71
+ });
72
+ }
73
+ let resolvedTransaction;
74
+ await journal.run({
75
+ effect: "committed-ledger-reread",
76
+ idempotencyKey: `${contractId}:${input.head}:ledger-reread`,
77
+ run: async () => {
78
+ const observed = await observe(stableRoot, contractId);
79
+ currentLifecycle = observed.lifecycle;
80
+ const accepted = resolveAcceptedLifecycleTransaction({ read: observed.repository, transactionObjectId: input.head, expectedContract: contractId });
81
+ if (accepted.status !== "accepted")
82
+ throw new FlowError("COMMITTED_LEDGER_REREAD_FAILED", `accepted ledger transaction ${input.head} could not be reread (${accepted.reason})`);
83
+ resolvedTransaction = accepted;
84
+ return { head: observed.lifecycle.observedLedgerHead };
85
+ },
86
+ });
87
+ journal.beginDerivedReplay();
88
+ const carrier = input.carrier === "retire"
89
+ ? { status: "retired", ledgerHead: input.head, reason: "terminal" }
90
+ : target.status === "available"
91
+ ? { status: "stale", ledgerHead: currentLifecycle.observedLedgerHead, target: target.path }
92
+ : { status: "target-unavailable", ledgerHead: currentLifecycle.observedLedgerHead, reason: target.reason };
93
+ committedResult = {
94
+ status: "committed",
95
+ head: input.head,
96
+ transaction: resolvedTransaction ?? (() => { throw new FlowError("COMMITTED_LEDGER_REREAD_FAILED", `accepted ledger transaction ${input.head} was not reread`); })(),
97
+ carrier,
98
+ effects: journal.all(),
99
+ cancellationObserved: effectsStarted && (signal?.aborted ?? false),
100
+ };
101
+ return committedResult;
102
+ }
103
+ async function publishRepository(transaction, additionalReplaySteps = []) {
104
+ if (mode !== "mutation")
105
+ throw new FlowError("INTERNAL_STATE", `carrier ${mode} session cannot publish repository ledger facts`);
106
+ if (!effectsStarted)
107
+ checkCancellation(signal);
108
+ let replayed;
109
+ let carrier = target.status === "available"
110
+ ? { status: "stale", ledgerHead: currentLifecycle.observedLedgerHead, target: target.path }
111
+ : { status: "target-unavailable", ledgerHead: currentLifecycle.observedLedgerHead, reason: target.reason };
112
+ const replayPlan = {
113
+ steps: [
114
+ ...additionalReplaySteps,
115
+ {
116
+ effect: "committed-ledger-reread",
117
+ dependencies: additionalReplaySteps.map((step) => step.effect),
118
+ run: async ({ ledgerHead }) => {
119
+ replayed = await observe(stableRoot, contractId);
120
+ if (replayed.lifecycle.observedLedgerHead !== ledgerHead) {
121
+ throw new FlowError("COMMITTED_LEDGER_REREAD_FAILED", `committed ledger head ${ledgerHead} could not be reread (observed ${replayed.lifecycle.observedLedgerHead})`);
122
+ }
123
+ currentLifecycle = replayed.lifecycle;
124
+ },
125
+ },
126
+ ],
127
+ };
128
+ const publication = await publishAuthoritativeTransaction(stableRoot, transaction, { replayPlan });
129
+ const result = publication.write;
130
+ if (result.family === "refused-before-effects") {
131
+ return {
132
+ status: "refused-before-effects",
133
+ reason: "conflict",
134
+ currentHead: observedLedgerHead(result.observations),
135
+ code: result.code,
136
+ cause: result.cause,
137
+ observations: result.observations,
138
+ effects: [],
139
+ };
140
+ }
141
+ if (result.family === "indeterminate") {
142
+ effectsStarted = true;
143
+ const currentHead = observedLedgerHead(result.observations);
144
+ journal.record({
145
+ effect: "ledger-cas",
146
+ idempotencyKey: `${contractId}:${result.builtLedgerHead}:ledger-cas`,
147
+ status: "failed",
148
+ cause: {
149
+ code: result.code,
150
+ message: result.cause,
151
+ facts: { builtHead: result.builtLedgerHead, currentHead },
152
+ },
153
+ });
154
+ return { status: "indeterminate", builtHead: result.builtLedgerHead, currentHead, effects: journal.all() };
155
+ }
156
+ effectsStarted = true;
157
+ const accepted = publication.builtTail.entries.at(-1);
158
+ const ledgerCasKey = `${contractId}:${result.ledgerHead}:ledger-cas`;
159
+ journal.record({
160
+ effect: "ledger-cas",
161
+ idempotencyKey: ledgerCasKey,
162
+ status: "succeeded",
163
+ receipt: {
164
+ head: result.ledgerHead,
165
+ entryKind: accepted.entry.kind,
166
+ authorityProof: result.authorityProof,
167
+ },
168
+ });
169
+ const replayDependencyEffects = {
170
+ "committed-ledger-reread": additionalReplaySteps.at(-1)?.effect ?? ledgerCasKey,
171
+ };
172
+ const replayKeys = new Map();
173
+ for (const effect of result.replay.effects) {
174
+ const idempotencyKey = `${contractId}:${result.ledgerHead}:${effect.effect}`;
175
+ replayKeys.set(effect.effect, idempotencyKey);
176
+ const dependencyEffect = replayDependencyEffects[effect.effect];
177
+ const dependency = dependencyEffect === ledgerCasKey ? ledgerCasKey : dependencyEffect ? replayKeys.get(dependencyEffect) : undefined;
178
+ const row = effect.status === "succeeded"
179
+ ? { effect: effect.effect, idempotencyKey, ...(dependency ? { dependency } : {}), status: "succeeded" }
180
+ : effect.status === "failed"
181
+ ? { effect: effect.effect, idempotencyKey, ...(dependency ? { dependency } : {}), status: "failed", cause: effect.cause }
182
+ : {
183
+ effect: effect.effect,
184
+ idempotencyKey,
185
+ dependency: replayKeys.get(effect.dependency) ?? effect.dependency,
186
+ status: "not-attempted",
187
+ cause: { code: "DEPENDENCY_FAILED", message: `dependency ${effect.dependency} did not succeed` },
188
+ };
189
+ journal.record(row);
190
+ }
191
+ const observedTransaction = resolveAcceptedLifecycleTransaction({
192
+ read: replayed?.repository ?? (await observe(stableRoot, contractId)).repository,
193
+ transactionObjectId: result.ledgerHead,
194
+ expectedContract: contractId,
195
+ });
196
+ if (observedTransaction.status !== "accepted") {
197
+ throw new FlowError("COMMITTED_LEDGER_REREAD_FAILED", `accepted ledger transaction ${result.ledgerHead} could not be reread (${observedTransaction.reason})`);
198
+ }
199
+ committedResult = {
200
+ status: "committed",
201
+ head: result.ledgerHead,
202
+ transaction: observedTransaction,
203
+ carrier,
204
+ effects: journal.all(),
205
+ cancellationObserved: signal?.aborted ?? false,
206
+ };
207
+ return committedResult;
208
+ }
209
+ async function replayDerivedState() {
210
+ const observed = await observe(stableRoot, contractId);
211
+ currentLifecycle = observed.lifecycle;
212
+ const bind = acceptedBind(observed.lifecycle);
213
+ let committedTarget = observed.target;
214
+ let materialized = {
215
+ status: "failed",
216
+ ledgerHead: observed.lifecycle.observedLedgerHead,
217
+ cause: "carrier projection was not attempted",
218
+ };
219
+ await replayCommittedDerivedState({
220
+ cwd: stableRoot,
221
+ repository: observed.repository,
222
+ lifecycle: observed.lifecycle,
223
+ target: committedTarget,
224
+ journal,
225
+ adapters: {
226
+ reconcileActiveWorktree: async () => {
227
+ committedTarget = await resolveContractCarrierTarget(stableRoot, contractId, bind);
228
+ let created = false;
229
+ if (committedTarget.status !== "available" && bind.data.workspace === "worktree") {
230
+ const worktreePath = await contractWorktreePathFromBind(stableRoot, contractId, bind);
231
+ try {
232
+ await fs.access(worktreePath);
233
+ throw Object.assign(new Error(`worktree path ${worktreePath} is occupied by an unowned filesystem entry`), { code: "BIND_WORKTREE_PATH_OCCUPIED", facts: { path: worktreePath, occupant: "unowned-filesystem-entry" } });
234
+ }
235
+ catch (error) {
236
+ if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT")
237
+ throw error;
238
+ }
239
+ await defaultCreateWorktree({
240
+ cwd: stableRoot,
241
+ id: contractId,
242
+ branch: `keiyaku/${contractId}`,
243
+ base: bind.data.base,
244
+ worktreePath,
245
+ });
246
+ committedTarget = await resolveContractCarrierTarget(stableRoot, contractId, bind);
247
+ created = true;
248
+ }
249
+ if (committedTarget.status !== "available")
250
+ throw new FlowError("CARRIER_TARGET_UNAVAILABLE", committedTarget.reason);
251
+ const renewed = await reconcileFoldedDeliveryWorktree({ targetRoot: committedTarget.root, lifecycle: observed.lifecycle });
252
+ return { status: created || renewed === "created" ? "created" : "already-present", target: committedTarget };
253
+ },
254
+ materializeContractView: async ({ lifecycle: accepted, target: acceptedTarget }) => {
255
+ const projected = await convergeCarrier(stableRoot, contractId, observed.repository, accepted, acceptedTarget, contractViewWriter);
256
+ materialized = projected;
257
+ if (projected.status === "failed")
258
+ throw new FlowError("CARRIER_MATERIALIZATION_FAILED", projected.cause);
259
+ if (projected.status === "target-unavailable")
260
+ throw new FlowError("CARRIER_TARGET_UNAVAILABLE", projected.reason);
261
+ return projected.status === "current" ? "already-present" : "created";
262
+ },
263
+ retireContractView: async ({ lifecycle: accepted, target: acceptedTarget }) => {
264
+ return retireTerminalHereCarrier(accepted, acceptedTarget);
265
+ },
266
+ replayClaimTarget: (landing) => replayClaimTargetWorktree(stableRoot, landing),
267
+ cleanupWorkspace: terminalWorkspaceCleanup ?? cleanupContractWorkspace,
268
+ observeDeliveryRef: observeTerminalDeliveryRef,
269
+ },
270
+ });
271
+ const carrierProjection = journal.all().find((row) => row.effect === "carrier-projection");
272
+ if (["claimed", "forfeited"].includes(observed.lifecycle.phase) && carrierProjection?.status === "succeeded") {
273
+ materialized = { status: "retired", ledgerHead: observed.lifecycle.observedLedgerHead, reason: "terminal" };
274
+ }
275
+ return materialized;
276
+ }
277
+ return {
278
+ mode,
279
+ stableRoot,
280
+ contractId,
281
+ lifecycle,
282
+ repository,
283
+ target,
284
+ initialCarrier,
285
+ pins,
286
+ journal,
287
+ async completeReplay(_carrierMode = "project") {
288
+ if (!committedResult)
289
+ throw new FlowError("INTERNAL_STATE", `contract ${contractId} has no committed mutation to replay`);
290
+ const materialized = await replayDerivedState();
291
+ committedResult = {
292
+ ...committedResult,
293
+ carrier: materialized,
294
+ effects: journal.all(),
295
+ cancellationObserved: effectsStarted && (signal?.aborted ?? false),
296
+ };
297
+ return materialized;
298
+ },
299
+ replayDerivedState,
300
+ async reread() {
301
+ if (!effectsStarted)
302
+ checkCancellation(signal);
303
+ const observed = await observe(stableRoot, contractId);
304
+ currentLifecycle = observed.lifecycle;
305
+ if (!effectsStarted)
306
+ checkCancellation(signal);
307
+ return { lifecycle: observed.lifecycle, repository: observed.repository, target: observed.target, pins: observed.pins };
308
+ },
309
+ async revalidate() {
310
+ if (!effectsStarted)
311
+ checkCancellation(signal);
312
+ const observed = await observe(stableRoot, contractId);
313
+ currentLifecycle = observed.lifecycle;
314
+ if (!effectsStarted)
315
+ checkCancellation(signal);
316
+ return { lifecycle: observed.lifecycle, repository: observed.repository, target: observed.target, pins: observed.pins };
317
+ },
318
+ beginEffects() {
319
+ if (!effectsStarted)
320
+ checkCancellation(signal);
321
+ effectsStarted = true;
322
+ },
323
+ effectsStarted() {
324
+ return effectsStarted;
325
+ },
326
+ cancellationObserved() {
327
+ return effectsStarted && (signal?.aborted ?? false);
328
+ },
329
+ committedResult() {
330
+ return committedResult;
331
+ },
332
+ async publishAuthoritative(transaction) {
333
+ if (authoritativeTransactionSelected)
334
+ throw new FlowError("INTERNAL_STATE", `carrier session ${contractId} already selected an authoritative transaction`);
335
+ authoritativeTransactionSelected = true;
336
+ if (transaction.entries.some((entry) => entry.contract !== contractId))
337
+ throw new FlowError("INTERNAL_STATE", `authoritative transaction does not belong to carrier session ${contractId}`);
338
+ if (transaction.admittedLedgerHead !== admittedLedgerHead)
339
+ throw new FlowError("CONCURRENT_MODIFICATION", `authoritative transaction ledger CAS does not match immutable carrier admission ${contractId}`);
340
+ if (transaction.acceptedLifecycleTail !== admittedLifecycleTail)
341
+ throw new FlowError("CONCURRENT_MODIFICATION", `authoritative transaction lifecycle tail does not match immutable carrier admission ${contractId}`);
342
+ const accepted = transaction.entries.at(-1);
343
+ const replaySteps = transaction.verb === "renew" && accepted.kind === "renew" && "delivery" in transaction
344
+ ? [{
345
+ effect: "delivery-worktree-projection",
346
+ dependencies: [],
347
+ run: async () => (renewDeliveryProjector ?? projectRenewDeliveryWorktree)(target, transaction.delivery.expectedOid, accepted.data.newHead),
348
+ }]
349
+ : [];
350
+ return publishRepository(transaction, replaySteps);
351
+ },
352
+ async replayCommitted(input) {
353
+ if (mode !== "mutation")
354
+ throw new FlowError("INTERNAL_STATE", `carrier ${mode} session cannot replay committed ledger facts`);
355
+ const observed = await observe(stableRoot, contractId);
356
+ const accepted = resolveAcceptedLifecycleTransaction({ read: observed.repository, transactionObjectId: input.transactionObjectId, expectedContract: contractId });
357
+ if (accepted.status !== "accepted")
358
+ throw new FlowError("CONCURRENT_MODIFICATION", `cannot replay unproved transaction ${input.transactionObjectId}`);
359
+ const renew = accepted.entries.find((entry) => entry.kind === "renew");
360
+ if (renew && (observed.lifecycle.pins.base !== renew.data.newBase || observed.lifecycle.pins.delivery !== renew.data.newHead || observed.pins.delivery.head !== renew.data.newHead || observed.pins.target.head !== renew.data.newBase)) {
361
+ throw new FlowError("CONCURRENT_MODIFICATION", `accepted renew transaction ${input.transactionObjectId} no longer matches current coordinates`);
362
+ }
363
+ currentLifecycle = observed.lifecycle;
364
+ return finalizeCommitted({
365
+ head: input.transactionObjectId,
366
+ ...(renew ? { renewProjection: { target: observed.target, oldHead: renew.data.oldHead, newHead: renew.data.newHead } } : {}),
367
+ carrier: input.carrier,
368
+ });
369
+ },
370
+ };
371
+ }
372
+ async function convergeCarrier(stableRoot, contractId, initialRepository, initialLifecycle, initialTarget, contractViewWriter) {
373
+ let repository = initialRepository;
374
+ let lifecycle = initialLifecycle;
375
+ let target = initialTarget;
376
+ for (let attempt = 0; attempt < 4; attempt += 1) {
377
+ const published = await publishCarrier({ stableRoot, contractId, repository, lifecycle, target, contractViewWriter });
378
+ if (published.status !== "failed")
379
+ return published;
380
+ const activeHead = (await readRepositoryLifecycle(stableRoot)).observedLedgerHead;
381
+ if (activeHead === lifecycle.observedLedgerHead)
382
+ return published;
383
+ const observed = await observe(stableRoot, contractId);
384
+ repository = observed.repository;
385
+ lifecycle = observed.lifecycle;
386
+ target = observed.target;
387
+ }
388
+ return { status: "failed", ledgerHead: lifecycle.observedLedgerHead, ...(target.status === "available" ? { target: target.path } : {}), cause: "carrier convergence did not reach a stable ledger head" };
389
+ }
390
+ export async function convergeContractCarrier(input) {
391
+ return withContractCarrierSession({ ...input, mode: "observe-only" }, async (session) => ({ lifecycle: session.lifecycle, repository: session.repository, target: session.target, carrier: session.initialCarrier }));
392
+ }
393
+ /** Carrier surface step for the shared reconciler while it owns the carrier gate. */
394
+ export async function reconcileContractCarrierProjection(input) {
395
+ checkCancellation(input.signal);
396
+ const stableRoot = await stableRepoRoot(input.cwd);
397
+ const observed = await observe(stableRoot, input.contractId);
398
+ const carrier = await convergeCarrier(stableRoot, input.contractId, observed.repository, observed.lifecycle, observed.target, input.contractViewWriter);
399
+ const reconciled = carrier.ledgerHead === observed.lifecycle.observedLedgerHead
400
+ ? observed
401
+ : await observe(stableRoot, input.contractId);
402
+ return {
403
+ lifecycle: reconciled.lifecycle,
404
+ repository: reconciled.repository,
405
+ target: reconciled.target,
406
+ carrier,
407
+ };
408
+ }