@astrosheep/keiyaku 2.9.9 → 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 +2 -1
  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 +6 -1
  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 +3 -2
  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 +9 -16
  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 +3 -5
  87. package/build/cli/subagent-guard.js +1 -1
  88. package/build/cli/types.js +1 -1
  89. package/build/config/env-keys.js +2 -0
  90. package/build/config/env.js +23 -0
  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 +6 -5
  129. package/build/core/projection/index.js +5 -4
  130. package/build/core/projection/projection-activity.js +27 -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 +21 -358
  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 +114 -119
  188. package/build/core/status/lifecycle.js +7 -80
  189. package/build/core/structured-query.js +23 -0
  190. package/build/core/target-ref.js +1 -1
  191. package/build/core/task/board.js +59 -24
  192. package/build/core/task/commands.js +181 -33
  193. package/build/core/task/coordinate.js +56 -0
  194. package/build/core/task/document.js +25 -9
  195. package/build/core/task/index.js +7 -4
  196. package/build/core/task/query.js +17 -27
  197. package/build/core/task/settlement-git.js +164 -179
  198. package/build/core/task/settlement-policy.js +15 -11
  199. package/build/core/task/source-board.js +31 -13
  200. package/build/core/task/task-bind-preparation.js +193 -0
  201. package/build/core/task/task-contract.js +102 -193
  202. package/build/core/task/task-file-transaction.js +171 -0
  203. package/build/core/task/task-git-runtime.js +76 -14
  204. package/build/core/task/task-git-store.js +68 -169
  205. package/build/core/task/task-store-repository.js +13 -6
  206. package/build/core/task/task-worktree.js +6 -33
  207. package/build/core/task/task.js +1 -1
  208. package/build/core/task/tree.js +3 -3
  209. package/build/core/task/validation.js +4 -3
  210. package/build/core/transcripts.js +93 -1
  211. package/build/core/worktree-bootstrap.js +44 -36
  212. package/build/core/worktree-path.js +210 -77
  213. package/build/flow-error.js +7 -7
  214. package/build/generated/version.js +2 -2
  215. package/build/git/core.js +76 -13
  216. package/build/git/refs.js +168 -46
  217. package/build/git/streaming-batch.js +73 -13
  218. package/package.json +1 -1
  219. package/skills/keiyaku/SKILL.md +1 -1
  220. package/skills/keiyaku-akuma/SKILL.md +3 -3
  221. package/skills/keiyaku-task/SKILL.md +4 -2
  222. package/skills/keiyaku-workflow/SKILL.md +88 -203
  223. package/build/core/entry.js +0 -305
  224. package/build/core/ledger-batch.js +0 -194
  225. package/build/core/ledger.js +0 -84
  226. package/build/core/projection/leash.js +0 -87
  227. package/build/core/ref-log.js +0 -119
  228. package/build/core/renew-session.js +0 -128
  229. package/build/core/status/ledger-batch.js +0 -1
  230. package/build/core/status/reconciliation.js +0 -137
@@ -0,0 +1,310 @@
1
+ import * as path from "node:path";
2
+ import { REPOSITORY_DELIVERY_REF_PREFIX, REPOSITORY_LEDGER_REF } from "./codec.js";
3
+ export function nonblank(value) {
4
+ return value.trim().length > 0;
5
+ }
6
+ export function isCanonicalAbsolute(value) {
7
+ return path.isAbsolute(value) && path.resolve(value) === value;
8
+ }
9
+ function isContained(root, candidate) {
10
+ if (!isCanonicalAbsolute(root) || !isCanonicalAbsolute(candidate))
11
+ return false;
12
+ const relative = path.relative(root, candidate);
13
+ return relative !== "" && relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
14
+ }
15
+ function completeObjectId(format, value) {
16
+ return new RegExp(`^[0-9a-f]{${format === "sha1" ? 40 : 64}}$`, "i").test(value);
17
+ }
18
+ function completeHostObjectId(value) {
19
+ return value !== undefined && /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i.test(value);
20
+ }
21
+ function refAction(observation, owner) {
22
+ return {
23
+ refName: observation.refName,
24
+ canonicalRoot: observation.canonicalRoot,
25
+ noSymlink: true,
26
+ objectFormat: observation.objectFormat,
27
+ exactObjectId: observation.exactObjectId,
28
+ owner,
29
+ };
30
+ }
31
+ function pathAction(observation, owner) {
32
+ return {
33
+ canonicalRoot: observation.canonicalRoot,
34
+ absoluteRealpath: observation.absoluteRealpath,
35
+ noSymlink: true,
36
+ owner,
37
+ };
38
+ }
39
+ function refusal(observation, reason) {
40
+ return {
41
+ observation,
42
+ classification: "unknown",
43
+ migration: { kind: "refuse-preserve", reason },
44
+ nuke: { kind: "refuse-preserve", reason },
45
+ };
46
+ }
47
+ function validRefObservation(observation) {
48
+ if (!isCanonicalAbsolute(observation.canonicalRoot))
49
+ return "ref observation lacks a canonical repository root";
50
+ if (!observation.noSymlink)
51
+ return "ref observation traverses a symlink";
52
+ if (!completeObjectId(observation.objectFormat, observation.exactObjectId)) {
53
+ return `ref observation lacks a complete ${observation.objectFormat} exact object ID`;
54
+ }
55
+ return null;
56
+ }
57
+ function classifyRef(observation, authority) {
58
+ const invalid = validRefObservation(observation);
59
+ if (invalid)
60
+ return refusal(observation, invalid);
61
+ if (!isCanonicalAbsolute(authority.canonicalRoot) || observation.canonicalRoot !== authority.canonicalRoot) {
62
+ return refusal(observation, "ref observation does not belong to the authority repository root");
63
+ }
64
+ const candidates = [];
65
+ if (observation.refName === REPOSITORY_LEDGER_REF) {
66
+ const action = refAction(observation, { kind: "repository-ledger" });
67
+ candidates.push({
68
+ observation,
69
+ classification: "repository-ledger-ref",
70
+ migration: { kind: "preserve" },
71
+ nuke: { kind: "delete-ref-exact-oid", ...action },
72
+ });
73
+ }
74
+ const deliveryPrefix = REPOSITORY_DELIVERY_REF_PREFIX;
75
+ if (observation.refName.startsWith(deliveryPrefix)) {
76
+ const contractId = observation.refName.slice(deliveryPrefix.length);
77
+ const contract = authority.fold.contracts.get(contractId);
78
+ const bindObjectId = contract?.entryObjectIds[0];
79
+ if (contract && contract.contract === contractId && contract.bind.contract === contractId &&
80
+ !contractId.includes("/") && completeHostObjectId(bindObjectId)) {
81
+ const action = refAction(observation, { kind: "contract-delivery", contractId, bindObjectId });
82
+ candidates.push({
83
+ observation,
84
+ classification: "contract-delivery-ref",
85
+ migration: { kind: "preserve" },
86
+ nuke: { kind: "delete-ref-exact-oid", ...action },
87
+ });
88
+ }
89
+ }
90
+ for (const legacy of authority.legacyRefs) {
91
+ if (!legacy.closureVerified || !nonblank(legacy.legacyIdentity))
92
+ continue;
93
+ if (legacy.canonicalRoot !== observation.canonicalRoot || legacy.refName !== observation.refName ||
94
+ legacy.objectFormat !== observation.objectFormat || legacy.exactObjectId !== observation.exactObjectId)
95
+ continue;
96
+ const action = refAction(observation, { kind: "legacy-ref", legacyIdentity: legacy.legacyIdentity });
97
+ candidates.push({
98
+ observation,
99
+ classification: "legacy-ref",
100
+ migration: { kind: "retire-after-closure-cas", ready: legacy.migrationReady, ...action },
101
+ nuke: { kind: "delete-ref-exact-oid", ...action },
102
+ });
103
+ }
104
+ if (candidates.length === 0)
105
+ return refusal(observation, "ref has no unique typed owner relationship");
106
+ if (candidates.length > 1)
107
+ return refusal(observation, "ref ownership is ambiguous");
108
+ return candidates[0];
109
+ }
110
+ function worktreeOwner(registration, authority) {
111
+ if (registration.canonicalRoot !== authority.canonicalRoot || !isCanonicalAbsolute(registration.canonicalRoot) ||
112
+ !isContained(registration.canonicalRoot, registration.canonicalRealpath) ||
113
+ !nonblank(registration.registrationIdentity))
114
+ return null;
115
+ const prefix = REPOSITORY_DELIVERY_REF_PREFIX;
116
+ if (!registration.branch.startsWith(prefix))
117
+ return null;
118
+ const contractId = registration.branch.slice(prefix.length);
119
+ if (!contractId || contractId.includes("/"))
120
+ return null;
121
+ const contract = authority.fold.contracts.get(contractId);
122
+ if (!contract || contract.bind.contract !== contractId || contract.bind.data.workspace !== "worktree")
123
+ return null;
124
+ const bindObjectId = contract.entryObjectIds[0];
125
+ if (!completeHostObjectId(bindObjectId))
126
+ return null;
127
+ const expectedRealpath = path.join(registration.canonicalRoot, ".keiyaku", "wt", contract.bind.data.place);
128
+ if (registration.canonicalRealpath !== expectedRealpath)
129
+ return null;
130
+ return {
131
+ kind: "contract-worktree",
132
+ contractId,
133
+ bindObjectId,
134
+ branch: registration.branch,
135
+ registeredWorktreeRealpath: registration.canonicalRealpath,
136
+ registrationIdentity: registration.registrationIdentity,
137
+ };
138
+ }
139
+ function provenRenderingOwner(observation, authority, worktree) {
140
+ if (authority.canonicalRoot !== observation.canonicalRoot || authority.absoluteRealpath !== observation.absoluteRealpath ||
141
+ authority.noSymlink !== true || authority.gitTracking !== "untracked" ||
142
+ observation.gitTracking !== authority.gitTracking ||
143
+ !nonblank(authority.immutableFileIdentity) || observation.immutableFileIdentity !== authority.immutableFileIdentity ||
144
+ authority.contractId !== worktree.contractId || authority.bindObjectId !== worktree.bindObjectId ||
145
+ authority.branch !== worktree.branch || authority.registeredWorktreeRealpath !== worktree.registeredWorktreeRealpath ||
146
+ authority.registrationIdentity !== worktree.registrationIdentity)
147
+ return null;
148
+ const expectedPath = authority.rendering === "contract-view"
149
+ ? path.join(worktree.registeredWorktreeRealpath, ".keiyaku", "KEIYAKU.md")
150
+ : path.join(worktree.registeredWorktreeRealpath, ".gitignore");
151
+ if (authority.absoluteRealpath !== expectedPath)
152
+ return null;
153
+ return { ...worktree, kind: "worktree-rendering", rendering: authority.rendering };
154
+ }
155
+ function residueCandidates(observation, authority, registrations) {
156
+ const candidates = [];
157
+ for (const root of authority.projectionResidueRoots) {
158
+ if (root.canonicalRoot !== observation.canonicalRoot || !nonblank(root.runtimeIdentity) ||
159
+ !isContained(root.canonicalRoot, root.projectionRootRealpath))
160
+ continue;
161
+ const relative = path.relative(root.projectionRootRealpath, observation.absoluteRealpath);
162
+ const match = /^([a-zA-Z0-9]+(?:[.-][a-zA-Z0-9]+)*)\/\.mint-([0-9a-f]{8})$/.exec(relative.split(path.sep).join("/"));
163
+ if (!match)
164
+ continue;
165
+ const owner = {
166
+ kind: "generated-residue",
167
+ family: "projection-mint",
168
+ runtimeIdentity: root.runtimeIdentity,
169
+ projectionRootRealpath: root.projectionRootRealpath,
170
+ akuma: match[1],
171
+ };
172
+ const action = pathAction(observation, owner);
173
+ candidates.push({ classification: "generated-residue", migration: { kind: "cleanup-generated-residue", ...action }, nuke: { kind: "cleanup-generated-residue", ...action } });
174
+ }
175
+ for (const root of authority.projectionAliasResidueRoots) {
176
+ if (root.canonicalRoot !== observation.canonicalRoot || !nonblank(root.runtimeIdentity) ||
177
+ !isContained(root.canonicalRoot, root.projectionAliasRootRealpath))
178
+ continue;
179
+ const relative = path.relative(root.projectionAliasRootRealpath, observation.absoluteRealpath);
180
+ if (!/^\.tmp\.[0-9a-f]{8}$/.test(relative))
181
+ continue;
182
+ const owner = {
183
+ kind: "generated-residue",
184
+ family: "projection-alias-temp",
185
+ runtimeIdentity: root.runtimeIdentity,
186
+ projectionAliasRootRealpath: root.projectionAliasRootRealpath,
187
+ };
188
+ const action = pathAction(observation, owner);
189
+ candidates.push({ classification: "generated-residue", migration: { kind: "cleanup-generated-residue", ...action }, nuke: { kind: "cleanup-generated-residue", ...action } });
190
+ }
191
+ for (const registration of registrations) {
192
+ const relative = path.relative(registration.registeredWorktreeRealpath, observation.absoluteRealpath).split(path.sep).join("/");
193
+ if (!/^\.keiyaku\/\.KEIYAKU\.md\.[0-9]+\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.tmp$/.test(relative))
194
+ continue;
195
+ const { kind: _kind, ...worktree } = registration;
196
+ const owner = { kind: "generated-residue", family: "worktree-rendering-temp", ...worktree };
197
+ const action = pathAction(observation, owner);
198
+ candidates.push({ classification: "generated-residue", migration: { kind: "cleanup-generated-residue", ...action }, nuke: { kind: "cleanup-generated-residue", ...action } });
199
+ }
200
+ return candidates;
201
+ }
202
+ function classifyPath(observation, authority) {
203
+ if (!isCanonicalAbsolute(authority.canonicalRoot) || observation.canonicalRoot !== authority.canonicalRoot) {
204
+ return refusal(observation, "path observation does not belong to the authority repository root");
205
+ }
206
+ if (!isCanonicalAbsolute(observation.canonicalRoot) || !isContained(observation.canonicalRoot, observation.absoluteRealpath)) {
207
+ return refusal(observation, "path observation is not a canonical contained realpath");
208
+ }
209
+ if (!observation.noSymlink)
210
+ return refusal(observation, "path observation traverses a symlink");
211
+ const registrations = authority.worktrees.flatMap((registration) => {
212
+ const owner = worktreeOwner(registration, authority);
213
+ return owner === null ? [] : [owner];
214
+ });
215
+ const candidates = [];
216
+ for (const owner of registrations) {
217
+ if (owner.registeredWorktreeRealpath === observation.absoluteRealpath) {
218
+ const action = pathAction(observation, owner);
219
+ candidates.push({ classification: "contract-worktree", migration: { kind: "preserve" }, nuke: { kind: "remove-proven-worktree", ...action } });
220
+ }
221
+ for (const rendering of authority.generatedWorktreeRenderings) {
222
+ const renderingOwner = provenRenderingOwner(observation, rendering, owner);
223
+ if (renderingOwner === null)
224
+ continue;
225
+ const action = pathAction(observation, renderingOwner);
226
+ candidates.push({
227
+ classification: "worktree-rendering",
228
+ migration: { kind: "preserve" },
229
+ nuke: { kind: "delete-proven-path", ...action },
230
+ ignorePattern: rendering.rendering === "contract-view" ? ".keiyaku/KEIYAKU.md" : ".gitignore",
231
+ });
232
+ }
233
+ }
234
+ for (const runtime of authority.runtimePaths) {
235
+ if (runtime.canonicalRoot !== observation.canonicalRoot || runtime.absoluteRealpath !== observation.absoluteRealpath ||
236
+ !isContained(runtime.canonicalRoot, runtime.absoluteRealpath) || !nonblank(runtime.runtimeIdentity))
237
+ continue;
238
+ const owner = { kind: "runtime-path", runtimeKind: runtime.runtimeKind, runtimeIdentity: runtime.runtimeIdentity };
239
+ const action = pathAction(observation, owner);
240
+ candidates.push({
241
+ classification: "runtime-path",
242
+ migration: runtime.migration === "retire-after-hard-cut"
243
+ ? { kind: "retire-after-hard-cut", ready: runtime.migrationReady === true, ...action }
244
+ : { kind: "preserve" },
245
+ nuke: { kind: "delete-proven-path", ...action },
246
+ });
247
+ }
248
+ candidates.push(...residueCandidates(observation, authority, registrations));
249
+ if (candidates.length === 0)
250
+ return refusal(observation, "path has no unique typed owner relationship");
251
+ if (candidates.length > 1)
252
+ return refusal(observation, "path ownership is ambiguous");
253
+ const candidate = candidates[0];
254
+ return { observation, ...candidate };
255
+ }
256
+ export function classifyRepositoryInventory(observations, authority) {
257
+ return observations.map((observation) => {
258
+ switch (observation.kind) {
259
+ case "ref": return classifyRef(observation, authority);
260
+ case "path": return classifyPath(observation, authority);
261
+ case "excluded-user-data":
262
+ return { observation, classification: "excluded-user-data", migration: { kind: "preserve" }, nuke: { kind: "preserve" } };
263
+ case "unknown": return refusal(observation, observation.reason);
264
+ }
265
+ });
266
+ }
267
+ export function createRepositoryInventoryManifest(rows, authority, receipt) {
268
+ return { authority, receipt, rows };
269
+ }
270
+ export function inventoryIgnorePatterns(rows) {
271
+ return rows.flatMap((row) => row.ignorePattern === undefined ? [] : [row.ignorePattern]).sort();
272
+ }
273
+ function foldedWorktreeCoordinate(model) {
274
+ const bind = model.history.find(({ entry }) => entry.kind === "bind")?.entry;
275
+ if (!bind || bind.kind !== "bind")
276
+ throw new Error(`contract ${model.contract} has no accepted bind coordinate`);
277
+ return bind.data.workspace === "here"
278
+ ? { workspace: "here" }
279
+ : { workspace: "worktree", place: bind.data.place };
280
+ }
281
+ function sameWorktree(left, right) {
282
+ if (left.workspace !== right.workspace)
283
+ return false;
284
+ if (left.workspace === "here")
285
+ return true;
286
+ return right.workspace === "worktree" && left.place === right.place;
287
+ }
288
+ /** Nonterminal contracts that physically occupy the repository-root carrier. */
289
+ export function activeWorkspaceHereContracts(read) {
290
+ return read.contracts
291
+ .filter((candidate) => (!["claimed", "forfeited"].includes(candidate.phase)
292
+ && foldedWorktreeCoordinate(candidate).workspace === "here"))
293
+ .map(({ contract }) => contract)
294
+ .sort();
295
+ }
296
+ /** Runtime ignore projection for one worktree from a single typed repository fold. */
297
+ export function inventoryIgnorePatternsForWorktree(read, targetLifecycle) {
298
+ if (read.observedLedgerHead !== targetLifecycle.observedLedgerHead) {
299
+ throw new Error("narrow-ignore inventory requires one repository-ledger observation");
300
+ }
301
+ const acceptedTarget = read.contracts.find(({ contract }) => contract === targetLifecycle.contract);
302
+ if (!acceptedTarget)
303
+ throw new Error(`contract ${targetLifecycle.contract} is absent from narrow-ignore inventory`);
304
+ const target = foldedWorktreeCoordinate(acceptedTarget);
305
+ const required = target.workspace === "here"
306
+ ? activeWorkspaceHereContracts(read).length > 0
307
+ : read.contracts.some((candidate) => (!["claimed", "forfeited"].includes(candidate.phase)
308
+ && sameWorktree(foldedWorktreeCoordinate(candidate), target)));
309
+ return required ? [".gitignore", ".keiyaku/KEIYAKU.md"] : [];
310
+ }
@@ -0,0 +1,125 @@
1
+ import { foldRepositoryLedger } from "./fold-repository.js";
2
+ const VIRTUAL_JOIN_OBJECT_ID = "publication-admission:join";
3
+ const CURRENT_CODEC_PHASE = "repository-ledger-transaction-v1";
4
+ function typedRoot(traversal) {
5
+ const roots = [...traversal.nodes.values()].filter((node) => node.type !== "lifecycle" && node.ledgerParents.length === 0);
6
+ return roots.length === 1 && roots[0]?.type === "transaction" && roots[0].transaction.verb === "bind"
7
+ ? roots[0].objectId
8
+ : null;
9
+ }
10
+ export function cleanTip(head, traversal) {
11
+ const fold = foldRepositoryLedger(traversal);
12
+ const root = typedRoot(traversal);
13
+ if (traversal.diseases.length > 0
14
+ || fold.diseases.length > 0
15
+ || fold.isolatedContracts.size > 0
16
+ || root === null) {
17
+ return {
18
+ status: "refused",
19
+ code: "LEDGER_CLOSURE_DISEASE",
20
+ localTip: null,
21
+ remoteTip: null,
22
+ cause: "ledger tip does not have complete clean typed closure",
23
+ diagnostics: fold.diseases,
24
+ };
25
+ }
26
+ return { status: "clean", head, traversal, fold, root, phase: CURRENT_CODEC_PHASE };
27
+ }
28
+ function sideOnlyLifecycle(side, other) {
29
+ const rows = new Map();
30
+ for (const objectId of side.order) {
31
+ if (other.nodes.has(objectId))
32
+ continue;
33
+ const node = side.nodes.get(objectId);
34
+ if (node?.type !== "transaction")
35
+ continue;
36
+ for (const entryObjectId of node.entryObjectIds) {
37
+ const entryNode = side.nodes.get(entryObjectId);
38
+ if (entryNode?.type !== "lifecycle")
39
+ continue;
40
+ const contractRows = rows.get(entryNode.entry.contract) ?? [];
41
+ contractRows.push(entryNode);
42
+ rows.set(entryNode.entry.contract, contractRows);
43
+ }
44
+ }
45
+ return rows;
46
+ }
47
+ function virtualJoinTraversal(remote, local, join) {
48
+ const nodes = new Map();
49
+ const order = [];
50
+ const add = (traversal) => {
51
+ for (const objectId of traversal.order) {
52
+ if (nodes.has(objectId))
53
+ continue;
54
+ const node = traversal.nodes.get(objectId);
55
+ if (node !== undefined)
56
+ nodes.set(objectId, node);
57
+ order.push(objectId);
58
+ }
59
+ };
60
+ add(remote.traversal);
61
+ add(local.traversal);
62
+ nodes.set(VIRTUAL_JOIN_OBJECT_ID, {
63
+ type: "join",
64
+ objectId: VIRTUAL_JOIN_OBJECT_ID,
65
+ join,
66
+ ledgerParents: [remote.head, local.head],
67
+ evidenceParent: null,
68
+ });
69
+ order.push(VIRTUAL_JOIN_OBJECT_ID);
70
+ return {
71
+ head: VIRTUAL_JOIN_OBJECT_ID,
72
+ coordinates: local.traversal.coordinates,
73
+ nodes,
74
+ order,
75
+ diseases: [],
76
+ };
77
+ }
78
+ export function admitJoin(remote, local, join) {
79
+ if (remote.root !== local.root) {
80
+ return {
81
+ status: "refused",
82
+ code: "LEDGER_DIVERGENCE",
83
+ localTip: local.head,
84
+ remoteTip: remote.head,
85
+ cause: `typed ledger roots differ (${local.root} local, ${remote.root} remote)`,
86
+ };
87
+ }
88
+ if (remote.phase !== local.phase) {
89
+ return {
90
+ status: "refused",
91
+ code: "LEDGER_PHASE_MISMATCH",
92
+ localTip: local.head,
93
+ remoteTip: remote.head,
94
+ cause: `typed ledger codec phases differ (${local.phase} local, ${remote.phase} remote)`,
95
+ };
96
+ }
97
+ const remoteOnly = sideOnlyLifecycle(remote.traversal, local.traversal);
98
+ const localOnly = sideOnlyLifecycle(local.traversal, remote.traversal);
99
+ const racedContract = [...remoteOnly.keys()].sort().find((contract) => localOnly.has(contract));
100
+ if (racedContract !== undefined) {
101
+ return {
102
+ status: "refused",
103
+ code: "LEDGER_ENTRY_RACE",
104
+ localTip: local.head,
105
+ remoteTip: remote.head,
106
+ contract: racedContract,
107
+ localEntry: localOnly.get(racedContract).at(-1).objectId,
108
+ remoteEntry: remoteOnly.get(racedContract).at(-1).objectId,
109
+ cause: `contract ${racedContract} has divergent admitted lifecycle entries`,
110
+ };
111
+ }
112
+ const combined = virtualJoinTraversal(remote, local, join);
113
+ const folded = foldRepositoryLedger(combined);
114
+ if (folded.diseases.length > 0 || folded.isolatedContracts.size > 0) {
115
+ return {
116
+ status: "refused",
117
+ code: "LEDGER_SEMANTIC_COLLISION",
118
+ localTip: local.head,
119
+ remoteTip: remote.head,
120
+ cause: "joined ledger tips produce semantic collisions",
121
+ diagnostics: folded.diseases,
122
+ };
123
+ }
124
+ return { status: "admitted", combined };
125
+ }