@astrosheep/keiyaku 2.9.12 → 2.9.13

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 (212) hide show
  1. package/README.md +22 -18
  2. package/build/.tsbuildinfo +1 -1
  3. package/build/agents/call-terms.js +1 -0
  4. package/build/agents/providers/claude-agent-sdk/adapter.js +2 -2
  5. package/build/cli/commands/akuma/akuma/handler.js +2 -1
  6. package/build/cli/commands/akuma/akuma/meta.js +4 -3
  7. package/build/cli/commands/akuma/catalog.js +2 -0
  8. package/build/cli/commands/akuma/list/handler.js +1 -1
  9. package/build/cli/commands/akuma/list/meta-list.js +12 -0
  10. package/build/cli/commands/akuma/list/meta-ls.js +2 -2
  11. package/build/cli/commands/akuma.js +5 -0
  12. package/build/cli/commands/contract/amend/meta.js +4 -3
  13. package/build/cli/commands/contract/arc/meta.js +2 -0
  14. package/build/cli/commands/contract/audit/handler.js +28 -2
  15. package/build/cli/commands/contract/audit/meta.js +4 -3
  16. package/build/cli/commands/contract/petition/handler.js +2 -1
  17. package/build/cli/commands/contract/petition/meta.js +2 -2
  18. package/build/cli/commands/contract/renew/handler.js +44 -3
  19. package/build/cli/commands/contract/renew/meta.js +5 -4
  20. package/build/cli/commands/metadata.js +12 -9
  21. package/build/cli/commands/projection/call/handler.js +1 -1
  22. package/build/cli/commands/projection/call/meta.js +1 -1
  23. package/build/cli/commands/projection/catalog.js +2 -2
  24. package/build/cli/commands/projection/{revive → fork}/handler.js +10 -5
  25. package/build/cli/commands/projection/fork/meta.js +12 -0
  26. package/build/cli/commands/projection/history/handler.js +46 -2
  27. package/build/cli/commands/projection/history/meta.js +3 -3
  28. package/build/cli/commands/projection/kill/meta.js +2 -2
  29. package/build/cli/commands/projection/status/handler.js +29 -13
  30. package/build/cli/commands/projection/status/meta.js +14 -3
  31. package/build/cli/commands/projection/tell/handler.js +8 -4
  32. package/build/cli/commands/projection/tell/meta.js +1 -1
  33. package/build/cli/commands/projection/wait/handler.js +45 -20
  34. package/build/cli/commands/projection/wait/meta.js +1 -1
  35. package/build/cli/commands/shared.js +1 -1
  36. package/build/cli/commands/system/catalog.js +2 -0
  37. package/build/cli/commands/task/add/handler.js +28 -12
  38. package/build/cli/commands/task/add/meta.js +18 -6
  39. package/build/cli/commands/task/catalog.js +2 -0
  40. package/build/cli/commands/task/compose/handler.js +2 -2
  41. package/build/cli/commands/task/compose/meta.js +2 -2
  42. package/build/cli/commands/task/doctor/handler.js +2 -3
  43. package/build/cli/commands/task/doctor/meta.js +2 -2
  44. package/build/cli/commands/task/done/meta.js +2 -2
  45. package/build/cli/commands/task/drop/handler.js +7 -2
  46. package/build/cli/commands/task/drop/meta.js +6 -3
  47. package/build/cli/commands/task/hold/meta.js +2 -2
  48. package/build/cli/commands/task/log/meta.js +2 -2
  49. package/build/cli/commands/task/ls/meta.js +2 -2
  50. package/build/cli/commands/task/note/handler.js +6 -0
  51. package/build/cli/commands/task/note/meta.js +11 -0
  52. package/build/cli/commands/task/resume/meta.js +2 -2
  53. package/build/cli/commands/task/shared.js +29 -15
  54. package/build/cli/commands/task/show/meta.js +2 -2
  55. package/build/cli/commands/task/start/meta.js +2 -2
  56. package/build/cli/commands/task/stop/meta.js +2 -2
  57. package/build/cli/commands/task/update/meta.js +3 -2
  58. package/build/cli/commands/verification/handler.js +43 -0
  59. package/build/cli/commands/verification/meta.js +13 -0
  60. package/build/cli/completion.js +16 -13
  61. package/build/cli/flags.js +43 -11
  62. package/build/cli/help.js +29 -30
  63. package/build/cli/index.js +6 -5
  64. package/build/cli/parse-flags.js +39 -0
  65. package/build/cli/parse-metadata.js +5 -2
  66. package/build/cli/parse-selectors.js +18 -0
  67. package/build/cli/parse.js +21 -8
  68. package/build/cli/render/arc.js +12 -3
  69. package/build/cli/render/audit.js +2 -2
  70. package/build/cli/render/call.js +4 -4
  71. package/build/cli/render/kanshi.js +122 -85
  72. package/build/cli/render/misc.js +2 -2
  73. package/build/cli/render/petition.js +16 -1
  74. package/build/cli/render/projection-history.js +17 -4
  75. package/build/cli/render/shared.js +5 -5
  76. package/build/cli/render/status-indicator.js +47 -0
  77. package/build/cli/render/status.js +106 -76
  78. package/build/cli/render/success-response.js +20 -5
  79. package/build/cli/render/task.js +6 -5
  80. package/build/cli/render/tell.js +10 -4
  81. package/build/cli/render/tool-command-normalization.js +137 -0
  82. package/build/cli/render/tool-presentation.js +2 -1
  83. package/build/cli/render/verification.js +23 -0
  84. package/build/cli/render/wait.js +101 -33
  85. package/build/cli/types.js +2 -1
  86. package/build/config/akuma-loader.js +21 -1
  87. package/build/config/settings/knobs.js +7 -1
  88. package/build/config/settings/schema.js +10 -5
  89. package/build/core/amend.js +102 -17
  90. package/build/core/arc.js +4 -4
  91. package/build/core/audit/candidate.js +10 -18
  92. package/build/core/audit/coordinates.js +12 -17
  93. package/build/core/audit/evidence.js +6 -4
  94. package/build/core/audit/facade.js +3 -3
  95. package/build/core/audit/report.js +1 -1
  96. package/build/core/bind-reconciliation.js +42 -23
  97. package/build/core/bind.js +182 -81
  98. package/build/core/call/call.js +6 -7
  99. package/build/core/call/context.js +43 -30
  100. package/build/core/call/execution.js +21 -6
  101. package/build/core/call/prompt.js +11 -42
  102. package/build/core/contract-carrier-runtime.js +209 -105
  103. package/build/core/contract-carrier.js +156 -73
  104. package/build/core/contract-view.js +42 -5
  105. package/build/core/contract.js +2 -1
  106. package/build/core/derived-replay.js +168 -35
  107. package/build/core/draft.js +9 -5
  108. package/build/core/forfeit.js +1 -1
  109. package/build/core/lifecycle-history.js +13 -0
  110. package/build/core/lifecycle-recovery.js +32 -27
  111. package/build/core/lifecycle-runner.js +14 -14
  112. package/build/core/log.js +1 -1
  113. package/build/core/outcome-base.js +2 -2
  114. package/build/core/projection/akuma-name.js +43 -0
  115. package/build/core/projection/generation/database.js +141 -79
  116. package/build/core/projection/generation/ledger.js +91 -0
  117. package/build/core/projection/generation/model.js +18 -13
  118. package/build/core/projection/generation/projection-generation-continuation.js +1 -1
  119. package/build/core/projection/generation/projection-generation-doorbell.js +88 -0
  120. package/build/core/projection/generation/projection-generation-execution.js +24 -0
  121. package/build/core/projection/generation/projection-generation-identity.js +4 -0
  122. package/build/core/projection/generation/projection-generation-launcher.js +72 -138
  123. package/build/core/projection/generation/projection-generation-process.js +16 -15
  124. package/build/core/projection/generation/projection-generation-runner.js +125 -79
  125. package/build/core/projection/generation/projection-generation-runtime.js +10 -61
  126. package/build/core/projection/generation/protocol.js +68 -0
  127. package/build/core/projection/generation/store.js +17 -7
  128. package/build/core/projection/generation/transitions.js +14 -14
  129. package/build/core/projection/index.js +5 -5
  130. package/build/core/projection/projection-core.js +6 -2
  131. package/build/core/projection/projection-execution-observer.js +1 -1
  132. package/build/core/projection/projection-history.js +7 -7
  133. package/build/core/projection/projection-kill.js +15 -6
  134. package/build/core/projection/projection-life-observer.js +8 -8
  135. package/build/core/projection/projection-mint.js +6 -8
  136. package/build/core/projection/projection-status-observation.js +77 -8
  137. package/build/core/projection/projection-status.js +151 -4
  138. package/build/core/projection/projection-wait.js +46 -27
  139. package/build/core/projection/projection-wake.js +27 -89
  140. package/build/core/projection/tell/launch-store.js +1 -1
  141. package/build/core/projection/tell/store.js +1 -1
  142. package/build/core/renew-build.js +204 -74
  143. package/build/core/renew-plan.js +34 -9
  144. package/build/core/renew-rewrite.js +56 -15
  145. package/build/core/renew.js +39 -35
  146. package/build/core/repository-ledger/accepted-fold-read.js +6 -3
  147. package/build/core/repository-ledger/codec.js +11 -5
  148. package/build/core/repository-ledger/current-state-store.js +13 -16
  149. package/build/core/repository-ledger/fold-repository.js +5 -6
  150. package/build/core/repository-ledger/identity.js +60 -0
  151. package/build/core/repository-ledger/inventory.js +3 -3
  152. package/build/core/repository-ledger/read-model.js +54 -6
  153. package/build/core/repository-ledger/write-transaction.js +9 -4
  154. package/build/core/run-control/connection-bound-close.js +41 -0
  155. package/build/core/run-control/detached-lease-bootstrap.js +208 -0
  156. package/build/core/run-control/detached-process.js +14 -0
  157. package/build/core/run-control/index.js +6 -0
  158. package/build/core/{process-group.js → run-control/process-tree.js} +1 -1
  159. package/build/core/run-control/runner-lease.js +172 -0
  160. package/build/core/{sqlite-process-lifetime-lock.js → run-control/sqlite-process-lifetime-lock.js} +4 -24
  161. package/build/core/seal.js +44 -32
  162. package/build/core/settlement/claim-delivery.js +22 -9
  163. package/build/core/settlement/claim.js +8 -6
  164. package/build/core/settlement/petition-claim-gates.js +9 -8
  165. package/build/core/settlement/petition-head-guard.js +5 -6
  166. package/build/core/settlement/petition-preview.js +21 -5
  167. package/build/core/settlement/petition.js +51 -17
  168. package/build/core/settlement/settlement.js +20 -15
  169. package/build/core/settlement/verdict.js +2 -2
  170. package/build/core/settlement/verification.js +492 -364
  171. package/build/core/status/board.js +184 -75
  172. package/build/core/status/drift.js +8 -10
  173. package/build/core/task/board.js +1 -0
  174. package/build/core/task/commands.js +25 -0
  175. package/build/core/task/compose.js +3 -2
  176. package/build/core/task/document.js +102 -39
  177. package/build/core/task/index.js +3 -3
  178. package/build/core/task/settlement-git.js +1 -4
  179. package/build/core/task/settlement-policy.js +27 -14
  180. package/build/core/task/source-board.js +1 -0
  181. package/build/core/task/task-bind-preparation.js +59 -9
  182. package/build/core/task/task-contract.js +2 -2
  183. package/build/core/task/task-store-repository.js +1 -0
  184. package/build/core/task/task.js +2 -2
  185. package/build/core/transcripts.js +4 -4
  186. package/build/core/verification-declaration.js +95 -0
  187. package/build/core/worktree-bootstrap.js +1 -1
  188. package/build/core/worktree-path.js +7 -11
  189. package/build/flow-error.js +9 -1
  190. package/build/generated/version.js +2 -2
  191. package/build/git/branches.js +41 -70
  192. package/build/git/commits.js +92 -21
  193. package/build/git/core.js +9 -161
  194. package/build/git/diff/preview.js +4 -4
  195. package/build/git/diff/read.js +4 -4
  196. package/build/git/diff/structured.js +5 -5
  197. package/build/git/process.js +229 -0
  198. package/build/git/refs.js +30 -5
  199. package/build/git/staging.js +39 -115
  200. package/build/git/streaming-batch.js +9 -16
  201. package/build/git/worktree.js +104 -91
  202. package/build/index.js +7 -0
  203. package/package.json +1 -1
  204. package/skills/keiyaku-akuma/SKILL.md +21 -20
  205. package/skills/keiyaku-task/SKILL.md +38 -4
  206. package/skills/keiyaku-workflow/SKILL.md +51 -11
  207. package/build/cli/commands/projection/revive/meta.js +0 -12
  208. package/build/core/projection/projection-life-protocol.js +0 -115
  209. package/build/core/projection/projection-runner-lock.js +0 -211
  210. package/build/core/projection/tell/database.js +0 -127
  211. package/build/core/settlement/verification-coordination.js +0 -305
  212. package/build/core/settlement/verification-supervisor.js +0 -275
@@ -1,75 +1,80 @@
1
1
  import * as fs from "node:fs/promises";
2
+ import * as path from "node:path";
2
3
  import { FlowError } from "../flow-error.js";
4
+ import { isCommitAncestor, readCommitCoordinate } from "../git/commits.js";
5
+ import { runGitProcess } from "../git/core.js";
3
6
  import { replayCommittedDerivedState } from "./derived-replay.js";
4
7
  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
8
  import { resolveAcceptedLifecycleTransaction } from "./repository-ledger/accepted-tail.js";
8
9
  import { publishAuthoritativeTransaction } from "./repository-ledger/write-transaction.js";
9
- import { cleanupContractWorkspace, contractWorktreePathFromBind, observeTerminalDeliveryRef, resolveContractCarrierTarget, stableRepoRoot } from "./worktree-path.js";
10
+ import { cleanupContractWorkspace, contractWorktreePathFromStableRoot, observeTerminalDeliveryRef, resolveContractCarrierTarget, stableRepoRoot } from "./worktree-path.js";
10
11
  import { defaultCreateWorktree } from "./bind-workspace.js";
11
12
  import { replayClaimTargetWorktree } from "./settlement/claim-delivery.js";
13
+ import { lifecycleTransactionId } from "./repository-ledger/identity.js";
12
14
  let acceptedBind, acquireGate, carrierMatches, checkCancellation;
13
- let observe, observedLedgerHead, projectRenewDeliveryWorktree, publishCarrier;
14
- let readCarrierIdentity, reconcileFoldedDeliveryWorktree, releaseGate, resolveCommitOrNull, retireTerminalHereCarrier;
15
+ let expectedCarrierIdentity, observe, observedLedgerHead, publishCarrier;
16
+ let verifyCarrier, reconcileFoldedDeliveryWorktree, releaseGate, retireTerminalHereCarrier;
15
17
  export function installCarrierRuntimePorts(ports) {
16
- ({ acceptedBind, acquireGate, carrierMatches, checkCancellation, observe, observedLedgerHead,
17
- projectRenewDeliveryWorktree, publishCarrier, readCarrierIdentity, reconcileFoldedDeliveryWorktree,
18
- releaseGate, resolveCommitOrNull, retireTerminalHereCarrier } = ports);
18
+ ({ acceptedBind, acquireGate, carrierMatches, checkCancellation, expectedCarrierIdentity, observe, observedLedgerHead,
19
+ publishCarrier, verifyCarrier, reconcileFoldedDeliveryWorktree,
20
+ releaseGate, retireTerminalHereCarrier } = ports);
19
21
  }
20
22
  export async function withContractCarrierSession(input, action) {
21
23
  const stableRoot = await stableRepoRoot(input.cwd);
22
24
  if (input.mode === "observe-only") {
23
25
  checkCancellation(input.signal);
24
26
  const { repository, lifecycle, target, pins } = await observe(stableRoot, input.contractId);
25
- const projectedCarrier = target.status === "available" ? await readCarrierIdentity(target.path) : null;
27
+ const projectedCarrier = target.status === "available" ? await verifyCarrier({ target: target.path, lifecycle }) : undefined;
26
28
  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));
29
+ ? { status: projectedCarrier && carrierMatches(projectedCarrier) ? "current" : "stale", contractHead: lifecycle.contractHead, target: target.path }
30
+ : { status: "target-unavailable", contractHead: lifecycle.contractHead, reason: target.reason };
31
+ return action(createSession(input.mode, stableRoot, input.contractId, repository, lifecycle, target, pins, initialCarrier, input.signal, input.contractViewWriter, input.renewDeliveryProjector, input.terminalWorkspaceCleanup, input.transactionRunner));
30
32
  }
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.
33
+ // Publication coordination is scoped to the contract's proven physical target.
34
34
  const preGateObserved = await observe(stableRoot, input.contractId);
35
- const gate = await acquireGate(stableRoot, input.signal);
35
+ const gate = await acquireGate(stableRoot, input.contractId, preGateObserved.target.status === "available" ? preGateObserved.target.path : `unavailable:${input.contractId}`, input.signal);
36
36
  try {
37
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;
38
+ const { repository, lifecycle, target, pins } = preGateObserved;
39
+ const projectedCarrier = target.status === "available" ? await verifyCarrier({ target: target.path, lifecycle }) : undefined;
43
40
  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 };
41
+ ? { status: projectedCarrier && carrierMatches(projectedCarrier) ? "current" : "stale", contractHead: lifecycle.contractHead, target: target.path }
42
+ : { status: "target-unavailable", contractHead: lifecycle.contractHead, reason: target.reason };
46
43
  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);
44
+ const session = createSession(input.mode, stableRoot, input.contractId, repository, lifecycle, target, pins, initialCarrier, input.signal, input.contractViewWriter, input.renewDeliveryProjector, input.terminalWorkspaceCleanup, input.transactionRunner);
48
45
  return await action(session);
49
46
  }
50
47
  finally {
51
48
  releaseGate(gate);
52
49
  }
53
50
  }
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`);
51
+ /** Bind-only carrier admission from the immutable Observe snapshot. */
52
+ export async function withObservedContractCarrierSession(input, action) {
53
+ checkCancellation(input.signal);
54
+ const gate = await acquireGate(input.stableRoot, input.contractId, input.observation.target.status === "available" ? input.observation.target.path : `unavailable:${input.contractId}`, input.signal);
55
+ try {
56
+ const { repository, lifecycle, target, pins } = input.observation;
57
+ const projectedCarrier = target.status === "available" ? await verifyCarrier({ target: target.path, lifecycle }) : undefined;
58
+ const initialCarrier = target.status === "available"
59
+ ? { status: projectedCarrier && carrierMatches(projectedCarrier) ? "current" : "stale", contractHead: lifecycle.contractHead, target: target.path }
60
+ : { status: "target-unavailable", contractHead: lifecycle.contractHead, reason: target.reason };
61
+ return await action(createSession("mutation", input.stableRoot, input.contractId, repository, lifecycle, target, pins, initialCarrier, input.signal, input.contractViewWriter, undefined, input.terminalWorkspaceCleanup, undefined));
62
+ }
63
+ finally {
64
+ releaseGate(gate);
65
+ }
66
+ }
67
+ function createSession(mode, stableRoot, contractId, repository, lifecycle, target, pins, initialCarrier, signal, contractViewWriter, renewDeliveryProjector, terminalWorkspaceCleanup, transactionRunner) {
68
+ const admittedLedgerHead = pins.repositoryLedgerHead;
69
+ const admittedLifecycleTail = lifecycle.contractHead;
59
70
  let currentLifecycle = lifecycle;
71
+ let currentObservation = { repository, lifecycle, target, pins };
60
72
  let authoritativeTransactionSelected = false;
61
73
  let effectsStarted = false;
62
74
  let committedResult;
63
- const journal = new EffectJournal(`${contractId}:${lifecycle.observedLedgerHead}`);
75
+ const journal = new EffectJournal(`${contractId}:${lifecycle.contractHead}`);
64
76
  async function finalizeCommitted(input) {
65
77
  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
78
  let resolvedTransaction;
74
79
  await journal.run({
75
80
  effect: "committed-ledger-reread",
@@ -81,15 +86,15 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
81
86
  if (accepted.status !== "accepted")
82
87
  throw new FlowError("COMMITTED_LEDGER_REREAD_FAILED", `accepted ledger transaction ${input.head} could not be reread (${accepted.reason})`);
83
88
  resolvedTransaction = accepted;
84
- return { head: observed.lifecycle.observedLedgerHead };
89
+ return { head: observed.lifecycle.contractHead };
85
90
  },
86
91
  });
87
92
  journal.beginDerivedReplay();
88
93
  const carrier = input.carrier === "retire"
89
- ? { status: "retired", ledgerHead: input.head, reason: "terminal" }
94
+ ? { status: "retired", contractHead: currentLifecycle.contractHead, reason: "terminal" }
90
95
  : target.status === "available"
91
- ? { status: "stale", ledgerHead: currentLifecycle.observedLedgerHead, target: target.path }
92
- : { status: "target-unavailable", ledgerHead: currentLifecycle.observedLedgerHead, reason: target.reason };
96
+ ? { status: "stale", contractHead: currentLifecycle.contractHead, target: target.path }
97
+ : { status: "target-unavailable", contractHead: currentLifecycle.contractHead, reason: target.reason };
93
98
  committedResult = {
94
99
  status: "committed",
95
100
  head: input.head,
@@ -100,32 +105,35 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
100
105
  };
101
106
  return committedResult;
102
107
  }
103
- async function publishRepository(transaction, additionalReplaySteps = []) {
108
+ async function publishRepository(transaction) {
104
109
  if (mode !== "mutation")
105
110
  throw new FlowError("INTERNAL_STATE", `carrier ${mode} session cannot publish repository ledger facts`);
106
111
  if (!effectsStarted)
107
112
  checkCancellation(signal);
108
113
  let replayed;
109
114
  let carrier = target.status === "available"
110
- ? { status: "stale", ledgerHead: currentLifecycle.observedLedgerHead, target: target.path }
111
- : { status: "target-unavailable", ledgerHead: currentLifecycle.observedLedgerHead, reason: target.reason };
115
+ ? { status: "stale", contractHead: currentLifecycle.contractHead, target: target.path }
116
+ : { status: "target-unavailable", contractHead: currentLifecycle.contractHead, reason: target.reason };
112
117
  const replayPlan = {
113
118
  steps: [
114
- ...additionalReplaySteps,
115
119
  {
116
120
  effect: "committed-ledger-reread",
117
- dependencies: additionalReplaySteps.map((step) => step.effect),
121
+ dependencies: [],
118
122
  run: async ({ ledgerHead }) => {
119
123
  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})`);
124
+ if (replayed.repository.observedLedgerHead !== ledgerHead) {
125
+ throw new FlowError("COMMITTED_LEDGER_REREAD_FAILED", `committed ledger head ${ledgerHead} could not be reread (observed ${replayed.repository.observedLedgerHead})`);
122
126
  }
123
127
  currentLifecycle = replayed.lifecycle;
128
+ currentObservation = replayed;
124
129
  },
125
130
  },
126
131
  ],
127
132
  };
128
- const publication = await publishAuthoritativeTransaction(stableRoot, transaction, { replayPlan });
133
+ const publication = await publishAuthoritativeTransaction(stableRoot, transaction, {
134
+ replayPlan,
135
+ ...(transactionRunner ? { transactionRunner } : {}),
136
+ });
129
137
  const result = publication.write;
130
138
  if (result.family === "refused-before-effects") {
131
139
  return {
@@ -166,9 +174,7 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
166
174
  authorityProof: result.authorityProof,
167
175
  },
168
176
  });
169
- const replayDependencyEffects = {
170
- "committed-ledger-reread": additionalReplaySteps.at(-1)?.effect ?? ledgerCasKey,
171
- };
177
+ const replayDependencyEffects = { "committed-ledger-reread": ledgerCasKey };
172
178
  const replayKeys = new Map();
173
179
  for (const effect of result.replay.effects) {
174
180
  const idempotencyKey = `${contractId}:${result.ledgerHead}:${effect.effect}`;
@@ -190,7 +196,7 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
190
196
  }
191
197
  const observedTransaction = resolveAcceptedLifecycleTransaction({
192
198
  read: replayed?.repository ?? (await observe(stableRoot, contractId)).repository,
193
- transactionObjectId: result.ledgerHead,
199
+ transactionObjectId: lifecycleTransactionId(result.ledgerHead),
194
200
  expectedContract: contractId,
195
201
  });
196
202
  if (observedTransaction.status !== "accepted") {
@@ -198,7 +204,7 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
198
204
  }
199
205
  committedResult = {
200
206
  status: "committed",
201
- head: result.ledgerHead,
207
+ head: lifecycleTransactionId(result.ledgerHead),
202
208
  transaction: observedTransaction,
203
209
  carrier,
204
210
  effects: journal.all(),
@@ -207,13 +213,16 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
207
213
  return committedResult;
208
214
  }
209
215
  async function replayDerivedState() {
210
- const observed = await observe(stableRoot, contractId);
216
+ const observed = currentObservation.lifecycle.contractHead === currentLifecycle.contractHead
217
+ ? currentObservation
218
+ : await observe(stableRoot, contractId);
219
+ currentObservation = observed;
211
220
  currentLifecycle = observed.lifecycle;
212
221
  const bind = acceptedBind(observed.lifecycle);
213
222
  let committedTarget = observed.target;
214
223
  let materialized = {
215
224
  status: "failed",
216
- ledgerHead: observed.lifecycle.observedLedgerHead,
225
+ contractHead: observed.lifecycle.contractHead,
217
226
  cause: "carrier projection was not attempted",
218
227
  };
219
228
  await replayCommittedDerivedState({
@@ -227,7 +236,7 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
227
236
  committedTarget = await resolveContractCarrierTarget(stableRoot, contractId, bind);
228
237
  let created = false;
229
238
  if (committedTarget.status !== "available" && bind.data.workspace === "worktree") {
230
- const worktreePath = await contractWorktreePathFromBind(stableRoot, contractId, bind);
239
+ const worktreePath = contractWorktreePathFromStableRoot(stableRoot, contractId, bind);
231
240
  try {
232
241
  await fs.access(worktreePath);
233
242
  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" } });
@@ -248,11 +257,23 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
248
257
  }
249
258
  if (committedTarget.status !== "available")
250
259
  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 };
260
+ return { status: created ? "created" : "already-present", target: committedTarget };
261
+ },
262
+ reconcileRenewDelivery: async ({ lifecycle: accepted, target: acceptedTarget, transaction }) => {
263
+ if (acceptedTarget.status !== "available")
264
+ throw new FlowError("CARRIER_TARGET_UNAVAILABLE", acceptedTarget.reason);
265
+ const renew = transaction.entries.find((entry) => entry.kind === "renew");
266
+ if (!renew || renew.kind !== "renew") {
267
+ throw new FlowError("INTERNAL_STATE", `accepted renew transaction ${transaction.transactionObjectId} has no renew entry`);
268
+ }
269
+ if (renewDeliveryProjector) {
270
+ await renewDeliveryProjector(acceptedTarget, renew.data.oldHead, renew.data.newHead);
271
+ return "created";
272
+ }
273
+ return reconcileFoldedDeliveryWorktree({ targetRoot: acceptedTarget.root, lifecycle: accepted });
253
274
  },
254
275
  materializeContractView: async ({ lifecycle: accepted, target: acceptedTarget }) => {
255
- const projected = await convergeCarrier(stableRoot, contractId, observed.repository, accepted, acceptedTarget, contractViewWriter);
276
+ const projected = await convergeCarrier(stableRoot, contractId, accepted, acceptedTarget, contractViewWriter);
256
277
  materialized = projected;
257
278
  if (projected.status === "failed")
258
279
  throw new FlowError("CARRIER_MATERIALIZATION_FAILED", projected.cause);
@@ -263,14 +284,122 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
263
284
  retireContractView: async ({ lifecycle: accepted, target: acceptedTarget }) => {
264
285
  return retireTerminalHereCarrier(accepted, acceptedTarget);
265
286
  },
287
+ expectedCarrierIdentity,
288
+ inspectSurface: async ({ obligation }) => {
289
+ switch (obligation.surface) {
290
+ case "carrier": {
291
+ if (committedTarget.status !== "available")
292
+ return null;
293
+ const verification = await verifyCarrier({ target: committedTarget.path, lifecycle: observed.lifecycle });
294
+ return verification.status === "current" ? verification.actual : null;
295
+ }
296
+ case "carrier-retirement": {
297
+ if (committedTarget.status !== "available")
298
+ return obligation.expected;
299
+ try {
300
+ await fs.access(committedTarget.path);
301
+ return null;
302
+ }
303
+ catch (error) {
304
+ return error instanceof Error && "code" in error && error.code === "ENOENT" ? obligation.expected : null;
305
+ }
306
+ }
307
+ case "narrow-ignore": {
308
+ if (committedTarget.status !== "available")
309
+ return null;
310
+ const expected = JSON.parse(obligation.expected.inventory);
311
+ const bytes = expected.length === 0 ? "" : `${expected.join("\n")}\n`;
312
+ try {
313
+ return await fs.readFile(path.join(committedTarget.root, ".gitignore"), "utf8") === bytes ? obligation.expected : null;
314
+ }
315
+ catch {
316
+ return null;
317
+ }
318
+ }
319
+ case "worktree": {
320
+ if (observed.lifecycle.phase === "claimed" || observed.lifecycle.phase === "forfeited") {
321
+ return committedTarget.workspace === "here" || committedTarget.status === "unavailable"
322
+ ? obligation.expected
323
+ : null;
324
+ }
325
+ if (committedTarget.status !== "available")
326
+ return null;
327
+ const [head, clean] = await Promise.all([
328
+ readCommitCoordinate(committedTarget.root, "HEAD"),
329
+ runGitProcess(committedTarget.root, ["status", "--porcelain=v1", "--untracked-files=no"]),
330
+ ]);
331
+ const expectedHead = obligation.expected.deliveryHead;
332
+ return head.status === "ok" && clean.status === 0 && !clean.error && head.coordinate.commit === expectedHead && clean.stdout === ""
333
+ ? obligation.expected
334
+ : null;
335
+ }
336
+ case "renew-delivery": {
337
+ if (committedTarget.status !== "available")
338
+ return null;
339
+ const [head, clean] = await Promise.all([
340
+ readCommitCoordinate(committedTarget.root, "HEAD"),
341
+ runGitProcess(committedTarget.root, ["status", "--porcelain=v1", "--untracked-files=no"]),
342
+ ]);
343
+ const expectedHead = obligation.expected.newHead;
344
+ return head.status === "ok" && clean.status === 0 && !clean.error && head.coordinate.commit === expectedHead && clean.stdout === ""
345
+ ? obligation.expected
346
+ : null;
347
+ }
348
+ case "delivery-ref": {
349
+ const projection = await observeTerminalDeliveryRef(stableRoot, contractId);
350
+ return projection.status === "aligned" ? obligation.expected : null;
351
+ }
352
+ case "target-worktree": {
353
+ const expected = obligation.expected;
354
+ const listed = await runGitProcess(stableRoot, ["worktree", "list", "--porcelain", "-z"]);
355
+ if (listed.error || listed.status !== 0)
356
+ return null;
357
+ let worktree;
358
+ for (const field of listed.stdout.split("\0")) {
359
+ if (field.startsWith("worktree "))
360
+ worktree = field.slice("worktree ".length);
361
+ if (field === `branch ${expected.target}` && worktree) {
362
+ const [head, clean, target] = await Promise.all([
363
+ readCommitCoordinate(worktree, "HEAD"),
364
+ runGitProcess(worktree, ["status", "--porcelain=v1", "--untracked-files=no"]),
365
+ readCommitCoordinate(stableRoot, expected.target),
366
+ ]);
367
+ return head.status === "ok"
368
+ && clean.status === 0 && !clean.error && clean.stdout === ""
369
+ && target.status === "ok" && head.coordinate.commit === target.coordinate.commit
370
+ ? obligation.expected
371
+ : null;
372
+ }
373
+ }
374
+ return obligation.expected;
375
+ }
376
+ case "task-board": {
377
+ // An open bind projects the exact bind-base task bytes into the
378
+ // index and working tree. Terminal task settlement has distinct
379
+ // target/ref semantics and is deliberately reconciled below.
380
+ if (observed.lifecycle.phase === "claimed" || observed.lifecycle.phase === "forfeited")
381
+ return null;
382
+ const bind = acceptedBind(observed.lifecycle);
383
+ const taskIds = obligation.expected.taskIds;
384
+ const paths = taskIds.map((taskId) => `.keiyaku/tasks/${taskId}.md`);
385
+ const [cached, worktree] = await Promise.all([
386
+ runGitProcess(stableRoot, ["diff", "--quiet", "--cached", bind.data.base, "--", ...paths]),
387
+ runGitProcess(stableRoot, ["diff", "--quiet", "--", ...paths]),
388
+ ]);
389
+ return cached.status === 0 && !cached.error && worktree.status === 0 && !worktree.error
390
+ ? obligation.expected
391
+ : null;
392
+ }
393
+ }
394
+ },
266
395
  replayClaimTarget: (landing) => replayClaimTargetWorktree(stableRoot, landing),
267
396
  cleanupWorkspace: terminalWorkspaceCleanup ?? cleanupContractWorkspace,
268
397
  observeDeliveryRef: observeTerminalDeliveryRef,
269
398
  },
270
399
  });
271
- const carrierProjection = journal.all().find((row) => row.effect === "carrier-projection");
400
+ const carrierProjection = journal.all().find((row) => row.effect === "carrier-retirement-projection");
272
401
  if (["claimed", "forfeited"].includes(observed.lifecycle.phase) && carrierProjection?.status === "succeeded") {
273
- materialized = { status: "retired", ledgerHead: observed.lifecycle.observedLedgerHead, reason: "terminal" };
402
+ materialized = { status: "retired", contractHead: observed.lifecycle.contractHead, reason: "terminal" };
274
403
  }
275
404
  return materialized;
276
405
  }
@@ -302,6 +431,7 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
302
431
  checkCancellation(signal);
303
432
  const observed = await observe(stableRoot, contractId);
304
433
  currentLifecycle = observed.lifecycle;
434
+ currentObservation = observed;
305
435
  if (!effectsStarted)
306
436
  checkCancellation(signal);
307
437
  return { lifecycle: observed.lifecycle, repository: observed.repository, target: observed.target, pins: observed.pins };
@@ -311,6 +441,7 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
311
441
  checkCancellation(signal);
312
442
  const observed = await observe(stableRoot, contractId);
313
443
  currentLifecycle = observed.lifecycle;
444
+ currentObservation = observed;
314
445
  if (!effectsStarted)
315
446
  checkCancellation(signal);
316
447
  return { lifecycle: observed.lifecycle, repository: observed.repository, target: observed.target, pins: observed.pins };
@@ -339,15 +470,7 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
339
470
  throw new FlowError("CONCURRENT_MODIFICATION", `authoritative transaction ledger CAS does not match immutable carrier admission ${contractId}`);
340
471
  if (transaction.acceptedLifecycleTail !== admittedLifecycleTail)
341
472
  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);
473
+ return publishRepository(transaction);
351
474
  },
352
475
  async replayCommitted(input) {
353
476
  if (mode !== "mutation")
@@ -357,35 +480,21 @@ function createSession(mode, stableRoot, contractId, repository, lifecycle, targ
357
480
  if (accepted.status !== "accepted")
358
481
  throw new FlowError("CONCURRENT_MODIFICATION", `cannot replay unproved transaction ${input.transactionObjectId}`);
359
482
  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)) {
483
+ const deliveryDescendsFromRenew = renew && observed.pins.delivery.head
484
+ ? await isCommitAncestor(stableRoot, renew.data.newHead, observed.pins.delivery.head)
485
+ : false;
486
+ if (renew && (observed.lifecycle.pins.base !== renew.data.newBase
487
+ || observed.lifecycle.pins.delivery !== renew.data.newHead
488
+ || !deliveryDescendsFromRenew)) {
361
489
  throw new FlowError("CONCURRENT_MODIFICATION", `accepted renew transaction ${input.transactionObjectId} no longer matches current coordinates`);
362
490
  }
363
491
  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
- });
492
+ return finalizeCommitted({ head: input.transactionObjectId, carrier: input.carrier });
369
493
  },
370
494
  };
371
495
  }
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" };
496
+ async function convergeCarrier(stableRoot, contractId, lifecycle, target, contractViewWriter) {
497
+ return publishCarrier({ stableRoot, contractId, lifecycle, target, contractViewWriter });
389
498
  }
390
499
  export async function convergeContractCarrier(input) {
391
500
  return withContractCarrierSession({ ...input, mode: "observe-only" }, async (session) => ({ lifecycle: session.lifecycle, repository: session.repository, target: session.target, carrier: session.initialCarrier }));
@@ -394,15 +503,10 @@ export async function convergeContractCarrier(input) {
394
503
  export async function reconcileContractCarrierProjection(input) {
395
504
  checkCancellation(input.signal);
396
505
  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
- };
506
+ return withContractCarrierSession({ cwd: stableRoot, contractId: input.contractId, mode: "mutation", signal: input.signal, contractViewWriter: input.contractViewWriter }, async (session) => ({
507
+ lifecycle: session.lifecycle,
508
+ repository: session.repository,
509
+ target: session.target,
510
+ carrier: await convergeCarrier(stableRoot, input.contractId, session.lifecycle, session.target, input.contractViewWriter),
511
+ }));
408
512
  }