@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
@@ -2,18 +2,11 @@ import * as fs from "node:fs/promises";
2
2
  import * as os from "node:os";
3
3
  import * as path from "node:path";
4
4
  import { FlowError, asMessage } from "../../flow-error.js";
5
- import { createGit, wrapGitError } from "../../git/core.js";
5
+ import { requireCommitCoordinate } from "../../git/commits.js";
6
+ import { createGit } from "../../git/core.js";
6
7
  import { readStructuredDiff } from "../../git/diff/structured.js";
7
8
  import { abortDetachedRenewChain, buildDetachedRenewChain, renewScratchName } from "../renew-rewrite.js";
8
9
  import { isBootstrapSuccess, runWorktreeBootstrap } from "../worktree-bootstrap.js";
9
- async function resolveObject(cwd, revision, kind) {
10
- try {
11
- return (await createGit(cwd).raw(["rev-parse", "--verify", `${revision}^{${kind}}`])).trim();
12
- }
13
- catch (error) {
14
- throw wrapGitError(`rev-parse ${revision}^{${kind}}`, error, cwd);
15
- }
16
- }
17
10
  export async function createAuditCandidate(input) {
18
11
  const cwd = await fs.mkdtemp(path.join(os.tmpdir(), "keiyaku-audit-"));
19
12
  const git = createGit(input.root);
@@ -30,7 +23,8 @@ export async function createAuditCandidate(input) {
30
23
  const rewrite = await buildDetachedRenewChain({
31
24
  cwd,
32
25
  scratchName,
33
- observedLedgerHead: input.frozen.coordinates.ledger.head,
26
+ observedLedgerHead: input.frozen.repository.observedLedgerHead,
27
+ lifecycleHead: input.frozen.lifecycle.contractHead,
34
28
  deliveryRef: input.frozen.coordinates.delivery.ref,
35
29
  targetRef: input.frozen.coordinates.target.ref,
36
30
  effectiveScope: input.frozen.scope,
@@ -44,8 +38,8 @@ export async function createAuditCandidate(input) {
44
38
  return { cwd, rehearsal: { status: "conflict", paths: [...rewrite.conflictPaths], pathCapture: "captured" }, candidate: null };
45
39
  }
46
40
  await candidateGit.raw(["reset", "--hard", rewrite.newHead]);
47
- const [head, tree] = await Promise.all([resolveObject(cwd, "HEAD", "commit"), resolveObject(cwd, "HEAD", "tree")]);
48
- return { cwd, rehearsal: { status: "clean", oldBase: input.frozen.base, newBase: input.frozen.coordinates.target.head, oldHead: input.frozen.coordinates.delivery.head }, candidate: { base: input.frozen.coordinates.target.head, head, tree, rewritten: true } };
41
+ const coordinate = await requireCommitCoordinate(cwd, "HEAD");
42
+ return { cwd, rehearsal: { status: "clean", oldBase: input.frozen.base, newBase: input.frozen.coordinates.target.head, oldHead: input.frozen.coordinates.delivery.head }, candidate: { base: input.frozen.coordinates.target.head, head: coordinate.commit, tree: coordinate.tree, rewritten: true } };
49
43
  }
50
44
  catch (error) {
51
45
  return { cwd, rehearsal: { status: "unknown", cause: { code: error instanceof FlowError ? error.code : "INTERNAL_STATE", message: asMessage(error) } }, candidate: null };
@@ -59,13 +53,11 @@ export async function readAuditCandidateDiff(candidateResult) {
59
53
  return readStructuredDiff(candidateResult.cwd, candidate.base, candidate.head);
60
54
  }
61
55
  async function readCandidateIntegrity(cwd) {
62
- const git = createGit(cwd);
63
- const [head, tree, status] = await Promise.all([
64
- resolveObject(cwd, "HEAD", "commit"),
65
- resolveObject(cwd, "HEAD", "tree"),
66
- git.raw(["status", "--porcelain=v1", "-z", "--untracked-files=no"]),
56
+ const [coordinate, status] = await Promise.all([
57
+ requireCommitCoordinate(cwd, "HEAD"),
58
+ createGit(cwd).raw(["status", "--porcelain=v1", "-z", "--untracked-files=no"]),
67
59
  ]);
68
- return { head, tree, trackedChanges: status.split("\0").filter(Boolean) };
60
+ return { head: coordinate.commit, tree: coordinate.tree, trackedChanges: status.split("\0").filter(Boolean) };
69
61
  }
70
62
  function preparationPlan(plan) {
71
63
  return { command: [...plan.command], timeoutMs: plan.timeoutMs };
@@ -2,6 +2,7 @@ import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
3
  import { createHash } from "node:crypto";
4
4
  import { FlowError, asMessage } from "../../flow-error.js";
5
+ import { requireCommitCoordinate } from "../../git/commits.js";
5
6
  import { createGit, wrapGitError } from "../../git/core.js";
6
7
  import { effectiveCriteria } from "../amendment-criteria.js";
7
8
  import { hasStrandableProjectionGeneration } from "../projection/index.js";
@@ -9,15 +10,7 @@ import { admitRepositoryLifecycleContract, } from "../repository-ledger/admissio
9
10
  import { lookupCurrentContract, readRepositoryLifecycle, } from "../repository-ledger/read-model.js";
10
11
  import { deriveQueueReadModel, queueMembership } from "../settlement/queue.js";
11
12
  import { findRegisteredContractWorktreePathFromModel } from "../worktree-path.js";
12
- async function resolveObject(cwd, revision, kind) {
13
- const git = createGit(cwd);
14
- try {
15
- return (await git.raw(["rev-parse", "--verify", `${revision}^{${kind}}`])).trim();
16
- }
17
- catch (error) {
18
- throw wrapGitError(`rev-parse ${revision}^{${kind}}`, error, cwd);
19
- }
20
- }
13
+ import { readRemainingTaskNeedsForSettlement } from "../task/settlement-policy.js";
21
14
  export function appendedScopeTerms(model) {
22
15
  return model.history.flatMap(({ entry }) => entry.kind === "amend" && entry.data.scopeDelta ? entry.data.scopeDelta.add : []);
23
16
  }
@@ -63,17 +56,17 @@ async function pinLedgerCoordinates(input) {
63
56
  const bind = model.history.find(({ entry }) => entry.kind === "bind")?.entry;
64
57
  if (!bind || bind.kind !== "bind")
65
58
  throw new FlowError("INTERNAL_STATE", `contract ${input.contractId} is missing folded bind terms`);
66
- const [deliveryHead, deliveryTree, targetHead, targetTree] = await Promise.all([
67
- resolveObject(input.root, model.resources.deliveryRef, "commit"), resolveObject(input.root, model.resources.deliveryRef, "tree"),
68
- resolveObject(input.root, model.pins.target.ref, "commit"), resolveObject(input.root, model.pins.target.ref, "tree"),
59
+ const [delivery, target] = await Promise.all([
60
+ requireCommitCoordinate(input.root, model.resources.deliveryRef),
61
+ requireCommitCoordinate(input.root, model.pins.target.ref),
69
62
  ]);
70
- if (model.pins.target.approvedHead !== null && model.petition.state === "approved" && model.pins.target.approvedHead !== targetHead) {
63
+ if (model.pins.target.approvedHead !== null && model.petition.state === "approved" && model.pins.target.approvedHead !== target.commit) {
71
64
  throw new FlowError("INTERNAL_STATE", `target ref ${model.pins.target.ref} moved from approved petition pin`);
72
65
  }
73
66
  return {
74
67
  criteria: auditCriteria(model), scope: [...model.effectiveScope], appendScope: appendedScopeTerms(model), base: model.pins.base,
75
68
  lifecycle: model, repository,
76
- coordinates: { ledger: { head: repository.observedLedgerHead }, delivery: { ref: model.resources.deliveryRef, head: deliveryHead, tree: deliveryTree }, target: { ref: model.pins.target.ref, head: targetHead, tree: targetTree } },
69
+ coordinates: { ledger: { head: repository.observedLedgerHead }, delivery: { ref: model.resources.deliveryRef, head: delivery.commit, tree: delivery.tree }, target: { ref: model.pins.target.ref, head: target.commit, tree: target.tree } },
77
70
  };
78
71
  }
79
72
  async function deliveryResidue(cwd) {
@@ -123,8 +116,8 @@ async function residueFingerprint(cwd, residue) {
123
116
  return digest.digest("hex");
124
117
  }
125
118
  async function deliveryCoordinate(cwd) {
126
- const [head, tree, residue] = await Promise.all([resolveObject(cwd, "HEAD", "commit"), resolveObject(cwd, "HEAD", "tree"), deliveryResidue(cwd)]);
127
- return { head, tree, residue, residueFingerprint: await residueFingerprint(cwd, residue) };
119
+ const [coordinate, residue] = await Promise.all([requireCommitCoordinate(cwd, "HEAD"), deliveryResidue(cwd)]);
120
+ return { head: coordinate.commit, tree: coordinate.tree, residue, residueFingerprint: await residueFingerprint(cwd, residue) };
128
121
  }
129
122
  async function checkedOutRef(cwd) {
130
123
  try {
@@ -146,7 +139,7 @@ async function receiptsAfterPinnedFence(input) {
146
139
  }
147
140
  async function readReadiness(input) {
148
141
  const model = input.frozen.lifecycle;
149
- const [residue, looseCommits, queue, liveProjection, dependencies] = await Promise.all([
142
+ const [residue, looseCommits, queue, liveProjection, afterDependencies, taskNeeds] = await Promise.all([
150
143
  deliveryResidue(input.deliveryCwd), receiptsAfterPinnedFence({ root: input.root, model, deliveryHead: input.frozen.coordinates.delivery.head }), Promise.resolve(deriveQueueReadModel(input.frozen.repository)),
151
144
  hasStrandableProjectionGeneration({ cwd: input.root, commissionId: input.contractId, deliveryWorktree: input.deliveryCwd, nowMs: Date.now() }),
152
145
  Promise.resolve(model.causal.after.map((id) => {
@@ -154,7 +147,9 @@ async function readReadiness(input) {
154
147
  const state = outcome.status === "healthy" ? outcome.model.phase : outcome.status;
155
148
  return { id, satisfied: outcome.status === "healthy" && (state === "claimed" || state === "forfeited"), state };
156
149
  })),
150
+ readRemainingTaskNeedsForSettlement(input.root, model.causal.needs),
157
151
  ]);
152
+ const dependencies = [...afterDependencies, ...taskNeeds];
158
153
  const membership = queueMembership(queue, input.contractId);
159
154
  return { deliveryResidue: residue, arc: { open: model.phase === "active" ? model.intent.arc : null, max: model.intent.arc, fence: model.pins.delivery, looseCommits }, rewriteGuard: { liveProjection }, dependencies, petition: { state: model.phase, queue: membership ? { position: membership.position, length: membership.queueLength, head: membership.queueHead, diagnostics: membership.diagnostics.map((item) => item.kind) } : null } };
160
155
  }
@@ -133,14 +133,16 @@ export async function evaluateVerification(input) {
133
133
  const plan = input.plan;
134
134
  if (plan.length === 0)
135
135
  return { status: "not_configured", results: [] };
136
- const executed = await runVerificationPlan({ cwd: input.cwd, candidate: { head: input.candidate.head, tree: input.candidate.tree }, plan, signal: input.signal, coordinationKey: input.coordinationKey, coordinationRoot: input.coordinationRoot });
137
- const results = executed.map((result) => {
136
+ const invocation = await runVerificationPlan({ cwd: input.cwd, candidate: { head: input.candidate.head, tree: input.candidate.tree }, plan, signal: input.signal, root: input.root });
137
+ // runId is addressable immediately; owner await cancels on abort and observes terminal cleanup.
138
+ const completion = await invocation.awaitTerminal();
139
+ const results = completion.results.map((result) => {
138
140
  const common = { source: result.source, declaration: result.declaration, label: result.label, executor: result.executor, command: result.command, argv: [result.command, ...result.args], candidate: result.candidate };
139
141
  if (result.execution.status === "runner_failed")
140
142
  return { ...common, ...result.execution };
141
143
  return { status: "executed", ...common, body: result.shell ? result.command : result.args.at(-1) ?? "", exitCode: result.execution.code, durationMs: result.execution.durationMs, stdout: result.execution.stdout, stderr: result.execution.stderr };
142
144
  });
143
- return { status: "evaluated", results };
145
+ return { status: "evaluated", runId: completion.runId, results };
144
146
  }
145
147
  export async function collectEvidence(input) {
146
148
  const candidate = input.candidateResult.candidate;
@@ -150,5 +152,5 @@ export async function collectEvidence(input) {
150
152
  return null;
151
153
  const diff = input.diff;
152
154
  const scope = await evaluateScope({ cwd: input.candidateResult.cwd, frozen: input.frozen, diff });
153
- return { diff, scope, presentation: presentDiff({ diff, scope, budget: input.diffBudgetBytes, retrievalCoordinates: { from: input.frozen.base, to: input.frozen.coordinates.delivery.head } }), files: fileFacts(diff, scope), verification: input.verificationOverride ?? await evaluateVerification({ cwd: input.candidateResult.cwd, candidate, plan: input.verificationPlan, signal: input.signal, coordinationKey: input.coordinationKey, coordinationRoot: input.coordinationRoot }), candidate };
155
+ return { diff, scope, presentation: presentDiff({ diff, scope, budget: input.diffBudgetBytes, retrievalCoordinates: { from: input.frozen.base, to: input.frozen.coordinates.delivery.head } }), files: fileFacts(diff, scope), verification: input.verificationOverride ?? await evaluateVerification({ cwd: input.candidateResult.cwd, root: input.root, candidate, plan: input.verificationPlan, signal: input.signal }), candidate };
154
156
  }
@@ -3,11 +3,11 @@ import { completeAuditCoordinates, initialCoordinateRace, pinAuditSnapshot, rere
3
3
  import { collectEvidence, resolveAuditVerificationPlan } from "./evidence.js";
4
4
  import { collectFindings, emptyRaceReport } from "./report.js";
5
5
  import { readinessFromFacts } from "./readiness.js";
6
+ import { effectiveVerification } from "../lifecycle-history.js";
6
7
  export async function auditContract(input) {
7
8
  const contractId = input.address.binding.commissionId;
8
9
  const frozenSnapshot = await pinAuditSnapshot({ address: input.address });
9
- const bind = frozenSnapshot.lifecycle.history.find(({ entry }) => entry.kind === "bind")?.entry;
10
- const contractVerification = bind?.kind === "bind" ? bind.data.verification : [];
10
+ const contractVerification = effectiveVerification(frozenSnapshot.lifecycle.history.map(({ entry }) => entry));
11
11
  const admission = {
12
12
  frozen: frozenSnapshot,
13
13
  contractVerification,
@@ -43,7 +43,7 @@ export async function auditContract(input) {
43
43
  }
44
44
  }
45
45
  const diff = await readAuditCandidateDiff(candidateResult);
46
- evidence = await collectEvidence({ candidateResult, frozen, diff, diffBudgetBytes: input.diffBudgetBytes ?? 32_000, verificationPlan, signal: input.signal, coordinationKey: JSON.stringify({ repository: input.address.repo.stableRoot, candidate: candidateResult.candidate, preparation: bind?.kind === "bind" && bind.data.workspace === "worktree" ? bind.data.bootstrap : null, plan: verificationPlan }), coordinationRoot: input.address.repo.stableRoot, ...(verificationOverride ? { verificationOverride } : {}) });
46
+ evidence = await collectEvidence({ candidateResult, frozen, diff, diffBudgetBytes: input.diffBudgetBytes ?? 32_000, verificationPlan, signal: input.signal, root: input.address.repo.stableRoot, ...(verificationOverride ? { verificationOverride } : {}) });
47
47
  verification = evidence?.verification ?? verificationOverride ?? { status: "not_evaluated", reason: candidateResult.rehearsal.status === "conflict" ? "renew_conflict" : "renew_unknown", results: [] };
48
48
  await input.afterEvidence?.();
49
49
  }
@@ -8,7 +8,7 @@ export function collectFindings(input) {
8
8
  if (input.rehearsal.status === "unknown")
9
9
  findings.push({ code: "RENEW_UNKNOWN", detail: `${input.rehearsal.cause.code}: ${input.rehearsal.cause.message}`, nextCommand: `${selector} audit` });
10
10
  for (const row of input.scope?.filter((item) => item.class === "violated") ?? [])
11
- findings.push({ code: "SCOPE_RESIDUE", detail: `${row.path}: ${row.residue.length} unexplained changes`, nextCommand: `${selector} amend --scope-add ${JSON.stringify(row.path)}` });
11
+ findings.push({ code: "SCOPE_RESIDUE", severity: "warning", detail: `${row.path}: ${row.residue.length} unexplained changes`, nextCommand: `${selector} amend --scope-add ${JSON.stringify(row.path)}` });
12
12
  if (input.candidatePreparation.status === "failed")
13
13
  findings.push({ code: "CANDIDATE_PREPARATION_FAILED", detail: `${input.candidatePreparation.cause.code}: ${input.candidatePreparation.cause.message}`, nextCommand: `${selector} audit` });
14
14
  if (input.verification.status === "evaluated") {
@@ -1,22 +1,32 @@
1
1
  import { FlowError } from "../flow-error.js";
2
2
  import { access } from "node:fs/promises";
3
- import { lookupCurrentContract, readRepositoryLifecycle } from "./repository-ledger/read-model.js";
4
- import { withContractCarrierSession } from "./contract-carrier.js";
5
- import { stableRepoRoot, contractWorktreePathFromBind, findRegisteredContractWorktreePath } from "./worktree-path.js";
3
+ import { lookupCurrentContract, readRepositoryLifecycle, } from "./repository-ledger/read-model.js";
4
+ import { withContractCarrierSession, withObservedContractCarrierSession } from "./contract-carrier.js";
5
+ import { contractWorktreePathFromStableRoot, findRegisteredContractWorktreePathFromModel } from "./worktree-path.js";
6
6
  import { defaultCreateWorktree } from "./bind-workspace.js";
7
7
  import { isCommissionId } from "./ids.js";
8
8
  import { REPOSITORY_LEDGER_REF, repositoryDeliveryRef } from "./repository-ledger/codec.js";
9
9
  import { resolveLocalBranchHead } from "../git/refs.js";
10
10
  import { runWorktreeBootstrap, bootstrapEntryDataFromObservation, isBootstrapSuccess } from "./worktree-bootstrap.js";
11
11
  import { committedLifecycleFacts, containedCommittedLifecycleFacts, EffectJournal } from "./outcome-base.js";
12
+ /**
13
+ * Repository admission can only recover a bind whose authority remains active.
14
+ * A terminal contract may retain its historical bootstrap disposition, but its
15
+ * projection is addressed recovery work and must not be adopted by another bind.
16
+ */
17
+ export function requiresBindRecovery(lifecycle) {
18
+ if (lifecycle.phase !== "active")
19
+ return false;
20
+ return lifecycle.bootstrapReplay.state === "pending" || lifecycle.bootstrapReplay.state === "failed-interrupted";
21
+ }
12
22
  export async function publishBindBootstrap(session, entry, expectedLedgerHead) {
13
- return await session.publishAuthoritative({ verb: "bootstrap", admittedLedgerHead: expectedLedgerHead, acceptedLifecycleTail: session.lifecycle.history.at(-1).objectId, entries: [entry] });
23
+ return await session.publishAuthoritative({ verb: "bootstrap", admittedLedgerHead: expectedLedgerHead, acceptedLifecycleTail: session.lifecycle.contractHead, entries: [entry] });
14
24
  }
15
25
  export async function publishBindFailedForfeit(session, entry, delivery, expectedLedgerHead) {
16
26
  return await session.publishAuthoritative({
17
27
  verb: "forfeit",
18
28
  admittedLedgerHead: expectedLedgerHead,
19
- acceptedLifecycleTail: session.lifecycle.history.at(-1).objectId,
29
+ acceptedLifecycleTail: session.lifecycle.contractHead,
20
30
  entries: [entry],
21
31
  delivery: {
22
32
  ref: repositoryDeliveryRef(entry.contract),
@@ -40,7 +50,7 @@ function copyAcceptedBindEntry(entry) {
40
50
  ...(entry.data.taskIds ? { taskIds: [...entry.data.taskIds] } : {}),
41
51
  };
42
52
  return {
43
- v: 2,
53
+ v: 3,
44
54
  kind: "bind",
45
55
  contract: entry.contract,
46
56
  at: entry.at,
@@ -76,18 +86,6 @@ function bootstrapProgressLine(command) {
76
86
  function workspaceBindView(entry) {
77
87
  return entry;
78
88
  }
79
- export async function revalidateBindAuthority(input) {
80
- const [target, delivery] = await Promise.all([
81
- resolveLocalBranchHead(input.cwd, input.target),
82
- resolveLocalBranchHead(input.cwd, input.deliveryRef),
83
- ]);
84
- if (target?.head !== input.targetHead) {
85
- throw new FlowError("CONCURRENT_MODIFICATION", `bind target changed from ${input.targetHead}`);
86
- }
87
- if (delivery !== null) {
88
- throw new FlowError("CONCURRENT_MODIFICATION", `bind delivery ref already exists: ${input.deliveryRef}`);
89
- }
90
- }
91
89
  export function committedOrThrow(result, operation) {
92
90
  if (result.family === "committed")
93
91
  return result;
@@ -163,6 +161,18 @@ function bindSurfaceReceipts(journal) {
163
161
  }
164
162
  return receipts;
165
163
  }
164
+ export async function reconcileCommittedBindFromSnapshot(input, admittedScopeOverlaps, snapshot) {
165
+ const result = await withObservedContractCarrierSession({
166
+ stableRoot: snapshot.stableRoot,
167
+ contractId: input.contractId,
168
+ observation: snapshot.observation,
169
+ signal: input.signal,
170
+ ...(input.terminalWorkspaceCleanup ? { terminalWorkspaceCleanup: input.terminalWorkspaceCleanup } : {}),
171
+ }, async (session) => reconcileBindContractLocked({ ...input, cwd: snapshot.stableRoot }, session, admittedScopeOverlaps));
172
+ if (!("continueAfterFailedBootstrap" in result))
173
+ return result;
174
+ return reconcileCommittedBind(input, admittedScopeOverlaps);
175
+ }
166
176
  /**
167
177
  * Sole replay-only entry for a committed bind. It never allocates identity or
168
178
  * reads draft input; every coordinate is reconstructed from the accepted bind.
@@ -186,11 +196,11 @@ export async function reconcileCommittedBind(input, admittedScopeOverlaps) {
186
196
  throw new FlowError("INTERNAL_STATE", `bind-failed finalization for ${input.contractId} did not settle after bootstrap re-admission`);
187
197
  }
188
198
  async function reconcileBindContractLocked(input, replaySession, admittedScopeOverlaps) {
189
- const stableRoot = await stableRepoRoot(input.cwd);
199
+ const stableRoot = replaySession.stableRoot;
190
200
  if (!isCommissionId(input.contractId)) {
191
201
  throw new FlowError("INVALID_TARGET", `bind replay requires a full canonical contract id; got ${input.contractId}`);
192
202
  }
193
- const initialRead = await readRepositoryLifecycle(stableRoot);
203
+ const initialRead = replaySession.repository;
194
204
  const outcome = lookupCurrentContract(initialRead, input.contractId);
195
205
  if (outcome.status === "absent")
196
206
  throw new FlowError("CONTRACT_NOT_FOUND", `contract ${input.contractId} not found`);
@@ -213,7 +223,7 @@ async function reconcileBindContractLocked(input, replaySession, admittedScopeOv
213
223
  const entry = copyAcceptedBindEntry(bindRow.entry);
214
224
  const workspace = entry.data.workspace;
215
225
  const branch = `keiyaku/${entry.contract}`;
216
- const worktreePath = await contractWorktreePathFromBind(stableRoot, entry.contract, workspaceBindView(entry));
226
+ const worktreePath = contractWorktreePathFromStableRoot(stableRoot, entry.contract, workspaceBindView(entry));
217
227
  const targetRoot = workspace === "here" ? stableRoot : worktreePath;
218
228
  const deps = input.dependencies ?? {};
219
229
  const heads = {
@@ -231,12 +241,21 @@ async function reconcileBindContractLocked(input, replaySession, admittedScopeOv
231
241
  let containedCause;
232
242
  const rememberFailure = (row) => { containedCause ??= effectCause(row); };
233
243
  const scopeOverlaps = [...admittedScopeOverlaps];
244
+ if (input.preparedTask && input.preparedTaskProjectionSucceeded) {
245
+ replaySession.journal.beginDerivedReplay();
246
+ replaySession.journal.record({
247
+ effect: "task-board-projection",
248
+ idempotencyKey: `${entry.contract}:${outcome.model.contractHead}:task-board`,
249
+ status: "succeeded",
250
+ receipt: { surface: "task-board", status: "created", authoritativeHead: outcome.model.contractHead, facts: { taskCount: input.preparedTask.taskIds.length } },
251
+ });
252
+ }
234
253
  const bootstrapPlan = entry.data.workspace === "worktree" ? entry.data.bootstrap : undefined;
235
254
  if (!replayingBindFailedTerminal && bootstrapPlan) {
236
255
  const worktreeReplay = await journal.run({
237
256
  effect: "bootstrap-worktree-prerequisite",
238
257
  run: async () => {
239
- const registered = await findRegisteredContractWorktreePath(stableRoot, entry.contract);
258
+ const registered = await findRegisteredContractWorktreePathFromModel(stableRoot, outcome.model);
240
259
  if (registered === worktreePath)
241
260
  return { status: "already-present", path: worktreePath };
242
261
  try {
@@ -261,7 +280,7 @@ async function reconcileBindContractLocked(input, replaySession, admittedScopeOv
261
280
  : undefined;
262
281
  let publishedFailedBootstrapThroughSession = false;
263
282
  if (bootstrapPlan) {
264
- const locked = lookupCurrentContract(await readRepositoryLifecycle(stableRoot), entry.contract);
283
+ const locked = lookupCurrentContract(replaySession.repository, entry.contract);
265
284
  if (locked.status !== "healthy")
266
285
  throw new FlowError("INTERNAL_STATE", `contract ${entry.contract} became unavailable during bootstrap replay`);
267
286
  const existingBootstrap = locked.model.history.find((row) => row.entry.kind === "bootstrap")?.entry;