@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
@@ -1,119 +0,0 @@
1
- import { createGit, errorContainsAnyPattern, MISSING_HEAD_PATTERNS, parseRevListOutput, wrapGitError } from "../git/core.js";
2
- const EMPTY_TREE_HASH = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
3
- const MISSING_REF_SENTINEL = "0".repeat(40);
4
- /** Resolve a ref log head, or null when the ref is missing. */
5
- export async function resolveRefLogHead(cwd, ref, options = {}) {
6
- const git = createGit(cwd);
7
- const peel = options.peel ?? "commit";
8
- const target = peel === "commit" ? `${ref}^{commit}` : ref;
9
- try {
10
- return (await git.raw(["rev-parse", "--verify", target])).trim();
11
- }
12
- catch (error) {
13
- if (errorContainsAnyPattern(error, MISSING_HEAD_PATTERNS))
14
- return null;
15
- throw wrapGitError(`rev-parse ${ref}`, error, cwd);
16
- }
17
- }
18
- /** Read the raw commit message bytes used by ref-log parsers (`show %B`). */
19
- export async function readRefLogCommitMessage(cwd, commit) {
20
- const git = createGit(cwd);
21
- try {
22
- return await git.raw(["show", "-s", "--format=%B", commit]);
23
- }
24
- catch (error) {
25
- throw wrapGitError(`show commit message ${commit}`, error, cwd);
26
- }
27
- }
28
- /**
29
- * List commits reachable from `head` oldest-first.
30
- * `errorLabel` is the ref or range name used in wrapGitError text.
31
- */
32
- export async function listRefLogCommits(cwd, head, errorLabel = head) {
33
- const git = createGit(cwd);
34
- try {
35
- return parseRevListOutput(await git.raw(["rev-list", "--reverse", head]));
36
- }
37
- catch (error) {
38
- throw wrapGitError(`rev-list ${errorLabel}`, error, cwd);
39
- }
40
- }
41
- /**
42
- * Create an empty-tree commit for a ref log entry.
43
- * Callers own subject/body byte layout so message formats stay domain-local.
44
- */
45
- export async function createRefLogCommit(cwd, input) {
46
- const git = createGit(cwd);
47
- const args = ["commit-tree", EMPTY_TREE_HASH];
48
- if (input.parentHead)
49
- args.push("-p", input.parentHead);
50
- args.push("-m", input.subject, "-m", input.body);
51
- try {
52
- return (await git.raw(args)).trim();
53
- }
54
- catch (error) {
55
- throw wrapGitError(`commit-tree ${input.errorLabel ?? input.subject}`, error, cwd);
56
- }
57
- }
58
- /**
59
- * Compare-and-swap a ref to `newHead`.
60
- * Returns false on CAS failure; callers own re-resolve / degrade policy.
61
- */
62
- export async function casUpdateRefLog(cwd, ref, newHead, expectedOld) {
63
- const git = createGit(cwd);
64
- const oldValue = expectedOld ?? MISSING_REF_SENTINEL;
65
- try {
66
- await git.raw(["update-ref", ref, newHead, oldValue]);
67
- return true;
68
- }
69
- catch {
70
- return false;
71
- }
72
- }
73
- async function readRefLogAt(cwd, ref, head, parse) {
74
- const commits = await listRefLogCommits(cwd, head, ref);
75
- const entries = [];
76
- for (const commit of commits) {
77
- entries.push(parse(await readRefLogCommitMessage(cwd, commit)));
78
- }
79
- return entries;
80
- }
81
- export async function readRefLog(cwd, ref, parse) {
82
- const head = await resolveRefLogHead(cwd, ref);
83
- if (!head)
84
- return [];
85
- return readRefLogAt(cwd, ref, head, parse);
86
- }
87
- export async function appendRefLogWithRetry(cwd, ref, subjectFor, parse, build, options = {}) {
88
- const maxRetries = options.maxRetries ?? 3;
89
- let currentHead = await resolveRefLogHead(cwd, ref);
90
- const startedMissing = currentHead === null;
91
- for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
92
- const entries = currentHead ? await readRefLogAt(cwd, ref, currentHead, parse) : [];
93
- const entry = await build(entries);
94
- if (entry === null)
95
- return { ok: false, reason: "aborted", currentHead };
96
- const newHead = await createRefLogCommit(cwd, {
97
- subject: subjectFor(entry),
98
- body: JSON.stringify(entry, null, 2),
99
- parentHead: currentHead,
100
- });
101
- const casOk = await casUpdateRefLog(cwd, ref, newHead, currentHead);
102
- if (casOk)
103
- return { ok: true, head: newHead };
104
- // Intentional degrade: re-resolve after CAS loss; missing-ref mid-flight is sticky
105
- // when the ref existed at start (do not resurrect).
106
- const resolved = await resolveRefLogHead(cwd, ref);
107
- if (resolved === null) {
108
- if (!startedMissing)
109
- return { ok: false, reason: "missing-ref" };
110
- currentHead = null;
111
- }
112
- else {
113
- currentHead = resolved;
114
- }
115
- }
116
- if (currentHead === null)
117
- return { ok: false, reason: "missing-ref" };
118
- return { ok: false, reason: "conflict", currentHead };
119
- }
@@ -1,128 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import * as fs from "node:fs/promises";
3
- import * as path from "node:path";
4
- import { FlowError } from "../flow-error.js";
5
- import { syncDirectory } from "../fs/durability.js";
6
- import { deleteRecoveryRef } from "../git/refs.js";
7
- import { KEIYAKU_FILE } from "../keiyaku.js";
8
- import { renderContractView } from "./contract-view.js";
9
- import { readLedger } from "./ledger.js";
10
- import { latestBase } from "./status/drift.js";
11
- import { findRegisteredContractWorktreePath } from "./worktree-path.js";
12
- const RENEW_SESSION_DIRECTORY = path.join(".keiyaku", "recovery");
13
- const RENEW_SESSION_FILE = "renew-session.json";
14
- function isSha(value) {
15
- return typeof value === "string" && /^[0-9a-f]{40}$/.test(value);
16
- }
17
- function parseSession(raw) {
18
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
19
- throw new FlowError("INTERNAL_STATE", "renew recovery session is malformed");
20
- }
21
- const value = raw;
22
- const keys = Object.keys(value).sort();
23
- const expected = ["commission", "newBase", "oldBase", "oldHead", "recoveryRef"];
24
- if (keys.length !== expected.length || keys.some((key, index) => key !== expected[index])) {
25
- throw new FlowError("INTERNAL_STATE", "renew recovery session has unknown or missing fields");
26
- }
27
- if (typeof value.commission !== "string" || !value.commission ||
28
- !isSha(value.oldBase) || !isSha(value.oldHead) || !isSha(value.newBase) ||
29
- typeof value.recoveryRef !== "string" || !value.recoveryRef) {
30
- throw new FlowError("INTERNAL_STATE", "renew recovery session has invalid coordinates");
31
- }
32
- return value;
33
- }
34
- async function sessionPath(cwd, commission) {
35
- const worktree = await findRegisteredContractWorktreePath(cwd, commission);
36
- return worktree ? path.join(worktree, RENEW_SESSION_DIRECTORY, RENEW_SESSION_FILE) : undefined;
37
- }
38
- export async function readRenewRecoverySession(cwd, commission) {
39
- const filePath = await sessionPath(cwd, commission);
40
- if (!filePath)
41
- return null;
42
- try {
43
- return parseSession(JSON.parse(await fs.readFile(filePath, "utf8")));
44
- }
45
- catch (error) {
46
- if (error.code === "ENOENT")
47
- return null;
48
- if (error instanceof FlowError)
49
- throw error;
50
- throw new FlowError("INTERNAL_STATE", `cannot read renew recovery session for ${commission}`, { cause: error });
51
- }
52
- }
53
- export async function writeRenewRecoverySession(cwd, session) {
54
- const filePath = await sessionPath(cwd, session.commission);
55
- if (!filePath) {
56
- throw new FlowError("INTERNAL_STATE", `renew --resolve requires ${session.commission}'s registered contract worktree`);
57
- }
58
- const existing = await readRenewRecoverySession(cwd, session.commission);
59
- if (existing) {
60
- throw new FlowError("RENEW_SESSION_OPEN", `renew recovery session is already open for ${session.commission}`, {
61
- facts: { kind: "renew_session", commission: session.commission, nextAction: "continue-or-abort" },
62
- });
63
- }
64
- const directory = path.dirname(filePath);
65
- await fs.mkdir(directory, { recursive: true });
66
- const temporary = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
67
- await fs.writeFile(temporary, `${JSON.stringify(session)}\n`, { encoding: "utf8", flag: "wx" });
68
- await fs.rename(temporary, filePath);
69
- await syncDirectory(directory);
70
- return filePath;
71
- }
72
- export async function removeRenewRecoverySession(cwd, commission) {
73
- const filePath = await sessionPath(cwd, commission);
74
- if (!filePath)
75
- return;
76
- try {
77
- await fs.unlink(filePath);
78
- await syncDirectory(path.dirname(filePath));
79
- }
80
- catch (error) {
81
- if (error.code !== "ENOENT")
82
- throw error;
83
- }
84
- }
85
- function publishedRenewForSession(ledger, session) {
86
- const tail = ledger.entries.at(-1);
87
- if (session.commission !== ledger.contractId ||
88
- tail?.kind !== "renew" ||
89
- tail.data.oldBase !== session.oldBase ||
90
- tail.data.oldHead !== session.oldHead ||
91
- tail.data.newBase !== session.newBase ||
92
- latestBase(ledger.entries) !== session.newBase)
93
- return null;
94
- const previous = ledger.entries.at(-2);
95
- return {
96
- renew: tail,
97
- seal: previous?.kind === "seal" && previous.data.sealedBy === "renew" ? previous : null,
98
- };
99
- }
100
- export async function convergeRenewRecoverySession(cwd, commission) {
101
- const session = await readRenewRecoverySession(cwd, commission);
102
- if (!session)
103
- return { state: "none" };
104
- const ledger = await readLedger(cwd, commission);
105
- if (!ledger)
106
- return { state: "live", session };
107
- const published = publishedRenewForSession(ledger, session);
108
- if (!published)
109
- return { state: "live", session };
110
- const worktree = await findRegisteredContractWorktreePath(cwd, commission);
111
- if (!worktree) {
112
- throw new FlowError("INTERNAL_STATE", `registered contract worktree disappeared for spent renew session ${commission}`);
113
- }
114
- await fs.writeFile(path.join(worktree, KEIYAKU_FILE), renderContractView({ id: commission, entries: ledger.entries }), "utf8");
115
- const recoveryCleanup = await deleteRecoveryRef(cwd, session.recoveryRef, session.oldHead);
116
- await removeRenewRecoverySession(cwd, commission);
117
- return { state: "spent", session, ledger, ...published, recoveryCleanup };
118
- }
119
- export async function assertNoRenewRecoverySession(cwd, commission) {
120
- const state = await convergeRenewRecoverySession(cwd, commission);
121
- if (state.state === "none")
122
- return null;
123
- if (state.state === "spent")
124
- return state;
125
- throw new FlowError("RENEW_SESSION_OPEN", `renew recovery session is open for ${commission}`, {
126
- facts: { kind: "renew_session", commission, nextAction: "continue-or-abort" },
127
- });
128
- }
@@ -1 +0,0 @@
1
- export { readAllLedgerSnapshots as readStatusLedgers, } from "../ledger-batch.js";
@@ -1,137 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { createGit, parseRevListOutput, wrapGitError } from "../../git/core.js";
4
- import { stableRepoRoot } from "../worktree-path.js";
5
- function isClaimReceipt(value) {
6
- if (!value || typeof value !== "object")
7
- return false;
8
- const receipt = value;
9
- return receipt.kind === "receipt" && [receipt.oldHead, receipt.newHead, receipt.contract].every((field) => typeof field === "string" && field.length > 0);
10
- }
11
- function isClaimReceiptBaseline(value) {
12
- if (!value || typeof value !== "object")
13
- return false;
14
- const baseline = value;
15
- if (baseline.kind !== "baseline" || typeof baseline.head !== "string" || typeof baseline.by !== "string" || baseline.by.length === 0) {
16
- return false;
17
- }
18
- if (typeof baseline.establishedAt !== "string" || !baseline.establishedAt.endsWith("Z"))
19
- return false;
20
- const at = Date.parse(baseline.establishedAt);
21
- return Number.isFinite(at) && new Date(at).toISOString() === baseline.establishedAt;
22
- }
23
- async function readClaimReceipts(cwd) {
24
- const receiptsPath = path.join(await stableRepoRoot(cwd), ".keiyaku", "claim-receipts.jsonl");
25
- try {
26
- const content = fs.readFileSync(receiptsPath, "utf8");
27
- return content
28
- .split("\n")
29
- .map((line, index) => ({ line, index: index + 1 }))
30
- .filter(({ line }) => line.trim().length > 0)
31
- .map(({ line, index }) => {
32
- try {
33
- const parsed = JSON.parse(line);
34
- return isClaimReceipt(parsed) || isClaimReceiptBaseline(parsed)
35
- ? { line: index, record: parsed }
36
- : { line: index, issue: { line: index, reason: "malformed" } };
37
- }
38
- catch {
39
- return { line: index, issue: { line: index, reason: "malformed" } };
40
- }
41
- });
42
- }
43
- catch {
44
- return [];
45
- }
46
- }
47
- export async function computeClaimReconciliation(cwd, defaultBranch) {
48
- if (!defaultBranch)
49
- return null;
50
- const receiptLines = await readClaimReceipts(cwd);
51
- if (receiptLines.length === 0)
52
- return null;
53
- const git = createGit(cwd);
54
- let firstParentHistory;
55
- try {
56
- firstParentHistory = parseRevListOutput(await git.raw(["rev-list", "--first-parent", defaultBranch.head]));
57
- }
58
- catch (error) {
59
- throw wrapGitError(`rev-list --first-parent ${defaultBranch.head}`, error, cwd);
60
- }
61
- const firstParentSet = new Set(firstParentHistory);
62
- let baselineIndex = -1;
63
- for (let index = receiptLines.length - 1; index >= 0; index -= 1) {
64
- const record = receiptLines[index].record;
65
- if (record?.kind === "baseline" && firstParentSet.has(record.head)) {
66
- baselineIndex = index;
67
- break;
68
- }
69
- }
70
- if (baselineIndex < 0)
71
- return null;
72
- const baseline = receiptLines[baselineIndex].record;
73
- const issues = receiptLines.slice(baselineIndex + 1).flatMap((line) => {
74
- if (line.issue)
75
- return [line.issue];
76
- return line.record?.kind === "baseline" ? [{ line: line.line, reason: "non-chain" }] : [];
77
- });
78
- // Only records after the latest valid baseline belong to this fence window.
79
- // Intentional degrade: rev-list failure yields empty segments plus non-chain receipt issues.
80
- let revList;
81
- try {
82
- revList = parseRevListOutput(await git.raw(["rev-list", "--first-parent", "--reverse", `${baseline.head}..${defaultBranch.head}`]));
83
- }
84
- catch {
85
- return {
86
- defaultBranch: defaultBranch.branch,
87
- head: defaultBranch.head,
88
- unclaimedSegments: [],
89
- receiptIssues: [...issues, ...receiptLines.slice(baselineIndex + 1).filter((line) => line.record?.kind === "receipt").map((line) => ({ line: line.line, reason: "non-chain" }))],
90
- };
91
- }
92
- const firstParent = new Map();
93
- let parent = baseline.head;
94
- for (const commit of revList) {
95
- firstParent.set(commit, parent);
96
- parent = commit;
97
- }
98
- const indexByCommit = new Map(revList.map((commit, index) => [commit, index]));
99
- const claimedHeads = new Set();
100
- let previousReceiptIndex = -1;
101
- for (const line of receiptLines.slice(baselineIndex + 1)) {
102
- if (line.record?.kind !== "receipt")
103
- continue;
104
- const index = indexByCommit.get(line.record.newHead);
105
- if (index === undefined || index <= previousReceiptIndex || firstParent.get(line.record.newHead) !== line.record.oldHead) {
106
- issues.push({ line: line.line, reason: "non-chain" });
107
- continue;
108
- }
109
- previousReceiptIndex = index;
110
- claimedHeads.add(line.record.newHead);
111
- }
112
- // Walk default-branch first-parent edges and group only movement not covered
113
- // by a valid receipt. These are facts, not a judgment about their source.
114
- const segments = [];
115
- let segmentStart = null;
116
- let segmentCount = 0;
117
- for (const commit of revList) {
118
- if (claimedHeads.has(commit)) {
119
- if (segmentStart !== null) {
120
- segments.push({ oldHead: segmentStart, newHead: firstParent.get(commit), commitCount: segmentCount });
121
- segmentStart = null;
122
- segmentCount = 0;
123
- }
124
- }
125
- else {
126
- if (segmentStart === null) {
127
- segmentStart = firstParent.get(commit);
128
- }
129
- segmentCount++;
130
- }
131
- }
132
- // Trailing unclaimed segment up to HEAD
133
- if (segmentStart !== null) {
134
- segments.push({ oldHead: segmentStart, newHead: defaultBranch.head, commitCount: segmentCount });
135
- }
136
- return { defaultBranch: defaultBranch.branch, head: defaultBranch.head, unclaimedSegments: segments, receiptIssues: issues };
137
- }