@astrosheep/keiyaku 2.9.9 → 2.9.11

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 +93 -174
  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,89 @@
1
+ import * as path from "node:path";
2
+ import { FlowError } from "../flow-error.js";
3
+ const ADDRESS_CARRIER = Symbol.for("keiyaku.address-carrier");
4
+ export class AddressResolutionError extends FlowError {
5
+ attempt;
6
+ constructor(code, message, attempt, options) {
7
+ super(code, message, options);
8
+ this.name = "AddressResolutionError";
9
+ this.attempt = attempt;
10
+ }
11
+ }
12
+ class AddressedFlowError extends FlowError {
13
+ [ADDRESS_CARRIER];
14
+ constructor(error, carrier) {
15
+ super(error.code, error.message, {
16
+ cause: error,
17
+ review: error.review,
18
+ facts: error.facts,
19
+ });
20
+ this.name = error.name;
21
+ this[ADDRESS_CARRIER] = carrier;
22
+ }
23
+ }
24
+ class AddressedError extends Error {
25
+ [ADDRESS_CARRIER];
26
+ constructor(error, carrier) {
27
+ super(error instanceof Error ? error.message : String(error), { cause: error });
28
+ this.name = error instanceof Error ? error.name : "Error";
29
+ this[ADDRESS_CARRIER] = carrier;
30
+ }
31
+ }
32
+ export function attachAddressCarrier(error, carrier) {
33
+ if (addressCarrierFromError(error))
34
+ return error;
35
+ return error instanceof FlowError
36
+ ? new AddressedFlowError(error, carrier)
37
+ : new AddressedError(error, carrier);
38
+ }
39
+ export function addressCarrierFromError(error) {
40
+ const seen = new Set();
41
+ let current = error;
42
+ while (((typeof current === "object" && current !== null) || typeof current === "function") && !seen.has(current)) {
43
+ seen.add(current);
44
+ if (current instanceof AddressResolutionError)
45
+ return current.attempt;
46
+ const addressed = current;
47
+ if (addressed[ADDRESS_CARRIER])
48
+ return addressed[ADDRESS_CARRIER];
49
+ current = addressed.cause;
50
+ }
51
+ return undefined;
52
+ }
53
+ export async function withResolvedAddress(address, operation) {
54
+ try {
55
+ return await operation();
56
+ }
57
+ catch (error) {
58
+ throw attachAddressCarrier(error, address);
59
+ }
60
+ }
61
+ export function buildResolutionAttempt(input) {
62
+ return {
63
+ repo: input.repo,
64
+ workdir: path.resolve(input.workdir),
65
+ expected: "commission",
66
+ ...(input.spelling ? { spelling: input.spelling } : {}),
67
+ ...(input.actual !== undefined ? { actual: input.actual } : {}),
68
+ candidates: [...(input.candidates ?? [])],
69
+ inference: [...(input.inference ?? [])],
70
+ };
71
+ }
72
+ export function buildObjectResolutionAttempt(input) {
73
+ return {
74
+ repo: input.repo,
75
+ workdir: path.resolve(input.workdir),
76
+ expected: input.expected,
77
+ supplied: input.supplied,
78
+ ...(input.actual !== undefined ? { actual: input.actual } : {}),
79
+ ...(input.scope ? { scope: input.scope } : {}),
80
+ candidates: [...(input.candidates ?? [])],
81
+ inference: [...(input.inference ?? [])],
82
+ };
83
+ }
84
+ export function buildArtifactResolutionAttempt(input) {
85
+ return {
86
+ ...buildObjectResolutionAttempt({ ...input, expected: "artifact" }),
87
+ expected: "artifact",
88
+ };
89
+ }
@@ -1,12 +1,14 @@
1
1
  import * as path from "node:path";
2
2
  import { FlowError } from "../flow-error.js";
3
3
  import { errorContainsAnyPattern, NOT_GIT_REPOSITORY_PATTERNS } from "../git/core.js";
4
- import { commissionSlug, formatCommissionCoordinate, isCommissionId } from "./ids.js";
5
- import { readAllLedgerSnapshots, readLedgerSnapshot } from "./ledger-batch.js";
4
+ import { commissionSlug, formatCommissionCoordinate } from "./ids.js";
5
+ import { readRepositoryLifecycle, } from "./repository-ledger/read-model.js";
6
+ import { admitRepositoryLifecycleContract, } from "./repository-ledger/admission.js";
6
7
  import { isResponseArtifactId } from "./response-artifact-id.js";
7
- import { activeContractBoundByEngineWorktree, stableRepoRoot } from "./worktree-path.js";
8
- import { assertNoRenewRecoverySession } from "./renew-session.js";
9
- import { deriveContractState, isTerminalState, } from "./status/lifecycle.js";
8
+ import { currentContractBoundByWorktreeWitness, stableRepoRoot } from "./worktree-path.js";
9
+ import { isTerminalState, } from "./status/lifecycle.js";
10
+ import { AddressResolutionError, } from "./address-carrier.js";
11
+ export { AddressResolutionError, addressCarrierFromError, attachAddressCarrier, buildArtifactResolutionAttempt, buildObjectResolutionAttempt, buildResolutionAttempt, withResolvedAddress, } from "./address-carrier.js";
10
12
  const AMBIGUOUS_OBJECTIVE_MAX_CHARS = 60;
11
13
  function normalizeAddressQuery(raw) {
12
14
  const value = raw.startsWith("@") ? raw.slice(1) : raw;
@@ -15,88 +17,6 @@ function normalizeAddressQuery(raw) {
15
17
  }
16
18
  return value.trim();
17
19
  }
18
- const ADDRESS_CARRIER = Symbol.for("keiyaku.address-carrier");
19
- class AddressedFlowError extends FlowError {
20
- [ADDRESS_CARRIER];
21
- constructor(error, carrier) {
22
- super(error.code, error.message, {
23
- cause: error,
24
- review: error.review,
25
- facts: error.facts,
26
- });
27
- this.name = error.name;
28
- this[ADDRESS_CARRIER] = carrier;
29
- }
30
- }
31
- class AddressedError extends Error {
32
- [ADDRESS_CARRIER];
33
- constructor(error, carrier) {
34
- super(error instanceof Error ? error.message : String(error), { cause: error });
35
- this.name = error instanceof Error ? error.name : "Error";
36
- this[ADDRESS_CARRIER] = carrier;
37
- }
38
- }
39
- export function attachAddressCarrier(error, carrier) {
40
- if (addressCarrierFromError(error))
41
- return error;
42
- return error instanceof FlowError
43
- ? new AddressedFlowError(error, carrier)
44
- : new AddressedError(error, carrier);
45
- }
46
- export function addressCarrierFromError(error) {
47
- const seen = new Set();
48
- let current = error;
49
- while (((typeof current === "object" && current !== null) || typeof current === "function") && !seen.has(current)) {
50
- seen.add(current);
51
- if (current instanceof AddressResolutionError)
52
- return current.attempt;
53
- const addressed = current;
54
- if (addressed[ADDRESS_CARRIER])
55
- return addressed[ADDRESS_CARRIER];
56
- current = addressed.cause;
57
- }
58
- return undefined;
59
- }
60
- export async function withResolvedAddress(address, operation) {
61
- try {
62
- return await operation();
63
- }
64
- catch (error) {
65
- throw attachAddressCarrier(error, address);
66
- }
67
- }
68
- export function buildResolutionAttempt(input) {
69
- return {
70
- repo: input.repo,
71
- workdir: path.resolve(input.workdir),
72
- expected: "commission",
73
- ...(input.spelling ? { spelling: input.spelling } : {}),
74
- ...(input.actual !== undefined ? { actual: input.actual } : {}),
75
- candidates: [...(input.candidates ?? [])],
76
- inference: [...(input.inference ?? [])],
77
- };
78
- }
79
- export function buildObjectResolutionAttempt(input) {
80
- return {
81
- repo: input.repo,
82
- workdir: path.resolve(input.workdir),
83
- expected: input.expected,
84
- supplied: input.supplied,
85
- ...(input.actual !== undefined ? { actual: input.actual } : {}),
86
- ...(input.scope ? { scope: input.scope } : {}),
87
- candidates: [...(input.candidates ?? [])],
88
- inference: [...(input.inference ?? [])],
89
- };
90
- }
91
- export function buildArtifactResolutionAttempt(input) {
92
- return {
93
- ...buildObjectResolutionAttempt({
94
- ...input,
95
- expected: "artifact",
96
- }),
97
- expected: "artifact",
98
- };
99
- }
100
20
  function commissionSelectorAsTyped(query, source) {
101
21
  if (source === "at-address")
102
22
  return `@${query}`;
@@ -104,63 +24,163 @@ function commissionSelectorAsTyped(query, source) {
104
24
  return `--contract ${query}`;
105
25
  return query;
106
26
  }
107
- export class AddressResolutionError extends FlowError {
108
- attempt;
109
- constructor(code, message, attempt, options) {
110
- super(code, message, options);
111
- this.name = "AddressResolutionError";
112
- this.attempt = attempt;
27
+ function candidatePlace(candidate) {
28
+ return candidate.model.resources.worktree?.place ?? null;
29
+ }
30
+ function legacyLifecycle(model) {
31
+ switch (model.phase) {
32
+ case "active":
33
+ return model.history.every((row) => row.entry.kind === "bind" || row.entry.kind === "bootstrap")
34
+ ? { state: "bound" }
35
+ : { state: "active" };
36
+ case "sealed":
37
+ return { state: "active" };
38
+ case "awaiting-verdict":
39
+ case "approved":
40
+ return { state: "petitioned" };
41
+ case "claimed":
42
+ return { state: "claimed" };
43
+ case "forfeited":
44
+ return { state: "forfeited" };
113
45
  }
114
46
  }
115
- function candidatePlace(candidate) {
116
- return candidate.bind.data.workspace === "worktree" ? candidate.bind.data.place ?? null : null;
47
+ function commissionAddressCandidates(read) {
48
+ return read.contracts.map((model) => {
49
+ const lifecycle = legacyLifecycle(model);
50
+ return {
51
+ contractId: model.contract,
52
+ model,
53
+ operable: !isTerminalState(lifecycle),
54
+ lifecycle,
55
+ };
56
+ });
117
57
  }
118
- function commissionAddressCandidatesFromLedgers(ledgers) {
119
- return ledgers.flatMap((ledger) => {
120
- const bind = ledger.entries.find((entry) => entry.kind === "bind");
121
- if (!bind || bind.contract !== ledger.contractId)
122
- return [];
123
- const lifecycle = deriveContractState(ledger.entries);
124
- return [{
125
- contractId: ledger.contractId,
126
- bind,
127
- active: lifecycle.state !== "corrupt" && !isTerminalState(lifecycle),
128
- lifecycle,
129
- }];
58
+ export function contractReadOutcomeError(contractId, outcome) {
59
+ return new FlowError("INTERNAL_STATE", `contract ${contractId} is unavailable because repository lifecycle evidence is ${outcome.status}`, {
60
+ facts: {
61
+ kind: "repository_lifecycle_disease",
62
+ contractId,
63
+ status: outcome.status,
64
+ diagnostics: outcome.diagnostics.map((diagnostic) => ({
65
+ objectId: diagnostic.objectId,
66
+ code: diagnostic.code,
67
+ detail: diagnostic.detail,
68
+ ...(diagnostic.contract === undefined ? {} : { contract: diagnostic.contract }),
69
+ })),
70
+ },
130
71
  });
131
72
  }
132
- async function commissionAddressCandidates(cwd, exactContractId) {
133
- if (exactContractId) {
134
- const ledger = await readLedgerSnapshot(cwd, exactContractId);
135
- return ledger ? commissionAddressCandidatesFromLedgers([ledger]) : [];
73
+ function admitContractCandidate(read, candidate) {
74
+ const outcome = admitRepositoryLifecycleContract(read, candidate.contractId);
75
+ if (outcome.status === "healthy")
76
+ return candidate;
77
+ if (outcome.status === "absent") {
78
+ throw new FlowError("INTERNAL_STATE", `contract ${candidate.contractId} disappeared from the repository lifecycle read model`);
79
+ }
80
+ throw contractReadOutcomeError(candidate.contractId, outcome);
81
+ }
82
+ function admittedAddress(read, candidate, address) {
83
+ admitContractCandidate(read, candidate);
84
+ return { address, model: candidate.model };
85
+ }
86
+ function matchedCommissionSpellings(query, candidate) {
87
+ const matches = [];
88
+ if (candidate.contractId === query)
89
+ matches.push("id");
90
+ if (commissionSlug(candidate.contractId) === query)
91
+ matches.push("slug");
92
+ if (candidate.operable && candidatePlace(candidate) === query) {
93
+ matches.push("place");
136
94
  }
137
- const reads = await readAllLedgerSnapshots(cwd);
138
- const failed = reads.find((read) => "error" in read);
139
- if (failed && "error" in failed)
140
- throw failed.error;
141
- return commissionAddressCandidatesFromLedgers(reads.filter((read) => !("error" in read)));
95
+ return matches;
142
96
  }
143
- function matchCommissionAddress(query, candidates) {
144
- return candidates.flatMap((candidate) => {
145
- const matches = [];
146
- if (candidate.contractId === query)
147
- matches.push({ ...candidate, spelling: "id" });
148
- if (commissionSlug(candidate.contractId) === query)
149
- matches.push({ ...candidate, spelling: "slug" });
150
- if (candidate.active && candidatePlace(candidate) === query) {
151
- matches.push({ ...candidate, spelling: "place" });
97
+ function preferredCommissionSpelling(spellings) {
98
+ for (const spelling of ["id", "slug", "place"]) {
99
+ if (spellings.has(spelling))
100
+ return spelling;
101
+ }
102
+ throw new FlowError("INTERNAL_STATE", "contract address identity has no matched spelling");
103
+ }
104
+ function explicitAddressIdentities(read, query) {
105
+ const identities = new Map();
106
+ const upsert = (contractId, spellings, candidate) => {
107
+ if (spellings.length === 0)
108
+ return;
109
+ const current = identities.get(contractId);
110
+ if (current) {
111
+ for (const spelling of spellings)
112
+ current.matchedSpellings.add(spelling);
113
+ if (!current.candidate && candidate)
114
+ current.candidate = candidate;
115
+ return;
152
116
  }
153
- return matches;
154
- });
117
+ identities.set(contractId, {
118
+ contractId,
119
+ matchedSpellings: new Set(spellings),
120
+ ...(candidate ? { candidate } : {}),
121
+ outcome: admitRepositoryLifecycleContract(read, contractId),
122
+ });
123
+ };
124
+ for (const candidate of commissionAddressCandidates(read)) {
125
+ upsert(candidate.contractId, matchedCommissionSpellings(query, candidate), candidate);
126
+ }
127
+ for (const contractId of read.isolatedContracts) {
128
+ const spellings = [];
129
+ if (contractId === query)
130
+ spellings.push("id");
131
+ if (commissionSlug(contractId) === query)
132
+ spellings.push("slug");
133
+ upsert(contractId, spellings);
134
+ }
135
+ return [...identities.values()].sort((left, right) => left.contractId.localeCompare(right.contractId));
136
+ }
137
+ function resolutionCandidateForIdentity(identity) {
138
+ const matchedBy = preferredCommissionSpelling(identity.matchedSpellings);
139
+ if (identity.candidate) {
140
+ const candidate = identity.candidate;
141
+ return {
142
+ commissionId: candidate.contractId,
143
+ matchedBy,
144
+ place: candidatePlace(candidate),
145
+ name: candidate.model.history[0].entry.kind === "bind" ? candidate.model.history[0].entry.data.title : candidate.model.intent.title,
146
+ objective: candidate.model.intent.objective,
147
+ lifecycle: candidate.lifecycle,
148
+ target: { branch: candidate.model.pins.target.ref },
149
+ ...(identity.outcome.status === "diseased" || identity.outcome.status === "isolated" || identity.outcome.status === "indeterminate"
150
+ ? {
151
+ availability: {
152
+ status: identity.outcome.status,
153
+ diagnostics: identity.outcome.diagnostics,
154
+ },
155
+ }
156
+ : {}),
157
+ };
158
+ }
159
+ if (identity.outcome.status === "healthy" || identity.outcome.status === "absent") {
160
+ throw new FlowError("INTERNAL_STATE", `contract ${identity.contractId} matched without a lifecycle model`);
161
+ }
162
+ return {
163
+ commissionId: identity.contractId,
164
+ matchedBy,
165
+ place: null,
166
+ name: identity.contractId,
167
+ objective: `repository lifecycle evidence is ${identity.outcome.status}`,
168
+ lifecycle: { state: "corrupt", at: 0, reason: `repository lifecycle evidence is ${identity.outcome.status}` },
169
+ target: { branch: "" },
170
+ availability: {
171
+ status: identity.outcome.status,
172
+ diagnostics: identity.outcome.diagnostics,
173
+ },
174
+ };
155
175
  }
156
176
  function attemptCandidates(candidates) {
157
177
  return candidates.map((candidate) => ({
158
178
  commissionId: candidate.contractId,
159
179
  place: candidatePlace(candidate),
160
- name: candidate.bind.data.name,
161
- objective: candidate.bind.data.objective,
180
+ name: candidate.model.history[0].entry.kind === "bind" ? candidate.model.history[0].entry.data.title : candidate.model.intent.title,
181
+ objective: candidate.model.intent.objective,
162
182
  lifecycle: candidate.lifecycle,
163
- target: { branch: candidate.bind.data.target },
183
+ target: { branch: candidate.model.pins.target.ref },
164
184
  }));
165
185
  }
166
186
  function truncateObjectiveFirstLine(objective) {
@@ -190,7 +210,7 @@ function buildResolvedAddress(input) {
190
210
  spelling: { asTyped: input.asTyped, source: input.source, matchedBy: input.matchedBy },
191
211
  place: { display: candidatePlace(input.candidate) },
192
212
  lifecycle: input.candidate.lifecycle,
193
- target: { branch: input.candidate.bind.data.target },
213
+ target: { branch: input.candidate.model.pins.target.ref },
194
214
  inference: input.inference,
195
215
  };
196
216
  }
@@ -210,64 +230,83 @@ export function buildUncommissionedRepositoryAddress(input) {
210
230
  inference: input.inference,
211
231
  });
212
232
  }
233
+ export function resolveExplicitContractAddressFromRead(input) {
234
+ const workdir = path.resolve(input.workdir);
235
+ const repo = { kind: "repository", stableRoot: input.stableRoot };
236
+ const query = normalizeAddressQuery(input.raw);
237
+ const source = input.source ?? (input.raw.startsWith("@") ? "at-address" : "explicit-flag");
238
+ const spelling = { asTyped: commissionSelectorAsTyped(query, source), source };
239
+ if (isResponseArtifactId(query)) {
240
+ throw new AddressResolutionError("EMPTY_PARAM", `artifact ${query} is a response artifact; expected a commission address`, { repo, workdir, expected: "commission", spelling, candidates: [], inference: [], actual: "artifact" });
241
+ }
242
+ const identities = explicitAddressIdentities(input.read, query);
243
+ const attempt = {
244
+ repo,
245
+ workdir,
246
+ expected: "commission",
247
+ spelling,
248
+ candidates: identities.map(resolutionCandidateForIdentity),
249
+ inference: [],
250
+ };
251
+ if (identities.length > 1) {
252
+ const detail = attempt.candidates.length > 0
253
+ ? `:\n${attempt.candidates.map((candidate) => ` ${formatAmbiguousCandidate(candidate)}`).join("\n")}`
254
+ : "";
255
+ throw new AddressResolutionError("CONTRACT_AMBIGUOUS", `@${query} is ambiguous${detail}\nuse a full @commission-id`, attempt);
256
+ }
257
+ if (identities.length === 1) {
258
+ const identity = identities[0];
259
+ if (identity.outcome.status !== "healthy") {
260
+ if (identity.outcome.status === "absent") {
261
+ throw new FlowError("INTERNAL_STATE", `contract ${identity.contractId} disappeared from the repository lifecycle read model`);
262
+ }
263
+ throw contractReadOutcomeError(identity.contractId, identity.outcome);
264
+ }
265
+ const candidate = identity.candidate;
266
+ if (!candidate) {
267
+ throw new FlowError("INTERNAL_STATE", `healthy contract ${identity.contractId} matched without a lifecycle model`);
268
+ }
269
+ const matchedBy = preferredCommissionSpelling(identity.matchedSpellings);
270
+ const address = buildResolvedAddress({
271
+ repo,
272
+ workdir,
273
+ candidate,
274
+ matchedBy,
275
+ asTyped: spelling.asTyped,
276
+ source,
277
+ inference: [],
278
+ });
279
+ return { address, model: candidate.model };
280
+ }
281
+ const unknown = admitRepositoryLifecycleContract(input.read, query);
282
+ if (unknown.status === "diseased" || unknown.status === "isolated" || unknown.status === "indeterminate") {
283
+ throw contractReadOutcomeError(query, unknown);
284
+ }
285
+ throw new AddressResolutionError("EMPTY_PARAM", `unknown contract address @${query}`, attempt);
286
+ }
213
287
  export async function resolveContractAddress(cwd, raw, source = raw?.startsWith("@") ? "at-address" : "explicit-flag", effectiveDirectory = cwd) {
214
288
  const workdir = path.resolve(effectiveDirectory);
215
289
  const repo = await resolvedRepository(cwd);
290
+ const read = await readRepositoryLifecycle(repo.stableRoot);
216
291
  if (raw !== undefined) {
217
- const query = normalizeAddressQuery(raw);
218
- const spelling = { asTyped: commissionSelectorAsTyped(query, source), source };
219
- // Lexical artifact fence: no ledger enumeration for rsp_<ULID>.
220
- if (isResponseArtifactId(query)) {
221
- throw new AddressResolutionError("EMPTY_PARAM", `artifact ${query} is a response artifact; expected a commission address`, {
222
- repo,
223
- workdir,
224
- expected: "commission",
225
- spelling,
226
- candidates: [],
227
- inference: [],
228
- actual: "artifact",
229
- });
230
- }
231
- const candidates = await commissionAddressCandidates(repo.stableRoot, isCommissionId(query) ? query : undefined);
232
- const matches = matchCommissionAddress(query, candidates);
233
- const attempt = {
234
- repo,
292
+ return resolveExplicitContractAddressFromRead({
293
+ read,
294
+ stableRoot: repo.stableRoot,
235
295
  workdir,
236
- expected: "commission",
237
- spelling,
238
- candidates: matches.map((candidate) => ({
239
- commissionId: candidate.contractId,
240
- matchedBy: candidate.spelling,
241
- place: candidatePlace(candidate),
242
- name: candidate.bind.data.name,
243
- objective: candidate.bind.data.objective,
244
- lifecycle: candidate.lifecycle,
245
- target: { branch: candidate.bind.data.target },
246
- })),
247
- inference: [],
248
- };
249
- if (matches.length === 1) {
250
- return buildResolvedAddress({
251
- repo,
252
- workdir,
253
- candidate: matches[0],
254
- matchedBy: matches[0].spelling,
255
- asTyped: attempt.spelling.asTyped,
256
- source,
257
- inference: [],
258
- });
259
- }
260
- if (matches.length === 0) {
261
- throw new AddressResolutionError("EMPTY_PARAM", `unknown contract address @${query}`, attempt);
262
- }
263
- throw new AddressResolutionError("CONTRACT_AMBIGUOUS", `@${query} is ambiguous:\n${attempt.candidates.map((candidate) => ` ${formatAmbiguousCandidate(candidate)}`).join("\n")}\nuse a full @commission-id`, attempt);
296
+ raw,
297
+ source,
298
+ }).address;
264
299
  }
265
- const candidates = await commissionAddressCandidates(repo.stableRoot);
300
+ const candidates = commissionAddressCandidates(read);
266
301
  let boundContractId;
267
302
  try {
268
303
  const effectiveRepo = await stableRepoRoot(workdir);
269
304
  if (effectiveRepo === repo.stableRoot) {
270
- boundContractId = await activeContractBoundByEngineWorktree(workdir, effectiveRepo);
305
+ boundContractId = await currentContractBoundByWorktreeWitness({
306
+ cwd: workdir,
307
+ stableRoot: effectiveRepo,
308
+ contracts: candidates.filter((candidate) => candidate.operable).map((candidate) => candidate.model),
309
+ });
271
310
  }
272
311
  }
273
312
  catch (error) {
@@ -275,8 +314,9 @@ export async function resolveContractAddress(cwd, raw, source = raw?.startsWith(
275
314
  throw error;
276
315
  }
277
316
  if (boundContractId) {
278
- const candidate = candidates.find((item) => item.active && item.contractId === boundContractId);
317
+ const candidate = candidates.find((item) => item.operable && item.contractId === boundContractId);
279
318
  if (candidate) {
319
+ admitContractCandidate(read, candidate);
280
320
  return buildResolvedAddress({
281
321
  repo,
282
322
  workdir,
@@ -288,16 +328,16 @@ export async function resolveContractAddress(cwd, raw, source = raw?.startsWith(
288
328
  });
289
329
  }
290
330
  }
291
- const operable = candidates.filter((candidate) => candidate.active);
331
+ const operable = candidates.filter((candidate) => candidate.operable);
292
332
  const attempt = {
293
333
  repo,
294
334
  workdir,
295
335
  expected: "commission",
296
336
  candidates: attemptCandidates(operable),
297
- inference: ["consulted active commission ledgers"],
337
+ inference: ["consulted repository lifecycle ledger"],
298
338
  };
299
339
  if (operable.length === 1) {
300
- const candidate = operable[0];
340
+ const candidate = admitContractCandidate(read, operable[0]);
301
341
  return buildResolvedAddress({
302
342
  repo,
303
343
  workdir,
@@ -317,15 +357,20 @@ export async function resolveContractAddress(cwd, raw, source = raw?.startsWith(
317
357
  * engine-owned worktree. Unlike existing-contract resolution, this never
318
358
  * considers sole-active cardinality.
319
359
  */
320
- export async function resolveCallWorktreeAddress(cwd, effectiveDirectory = cwd) {
321
- const repo = await resolvedRepository(cwd);
322
- const workdir = path.resolve(effectiveDirectory);
360
+ export async function resolveCallWorktreeAddressFromRead(input) {
361
+ const repo = { kind: "repository", stableRoot: input.stableRoot };
362
+ const workdir = path.resolve(input.workdir);
363
+ const candidates = commissionAddressCandidates(input.read).filter((candidate) => candidate.operable);
323
364
  let contractId;
324
365
  try {
325
366
  const effectiveRepo = await stableRepoRoot(workdir);
326
367
  if (effectiveRepo !== repo.stableRoot)
327
368
  return undefined;
328
- contractId = await activeContractBoundByEngineWorktree(workdir, effectiveRepo);
369
+ contractId = await currentContractBoundByWorktreeWitness({
370
+ cwd: workdir,
371
+ stableRoot: effectiveRepo,
372
+ contracts: candidates.map((candidate) => candidate.model),
373
+ });
329
374
  }
330
375
  catch (error) {
331
376
  if (!errorContainsAnyPattern(error, NOT_GIT_REPOSITORY_PATTERNS))
@@ -334,15 +379,29 @@ export async function resolveCallWorktreeAddress(cwd, effectiveDirectory = cwd)
334
379
  }
335
380
  if (!contractId)
336
381
  return undefined;
337
- return await resolveContractAddress(repo.stableRoot, contractId, "worktree-binding", workdir);
382
+ const candidate = candidates.find((item) => item.contractId === contractId);
383
+ if (!candidate)
384
+ return undefined;
385
+ const address = buildResolvedAddress({
386
+ repo,
387
+ workdir,
388
+ candidate,
389
+ matchedBy: "id",
390
+ asTyped: `@${candidate.contractId}`,
391
+ source: "worktree-binding",
392
+ inference: ["verified active engine worktree binding"],
393
+ });
394
+ return admittedAddress(input.read, candidate, address);
395
+ }
396
+ export async function resolveCallWorktreeAddress(cwd, effectiveDirectory = cwd) {
397
+ const repo = await resolvedRepository(cwd);
398
+ const admitted = await resolveCallWorktreeAddressFromRead({
399
+ read: await readRepositoryLifecycle(repo.stableRoot),
400
+ stableRoot: repo.stableRoot,
401
+ workdir: effectiveDirectory,
402
+ });
403
+ return admitted?.address;
338
404
  }
339
405
  export async function resolveExistingContractAddress(cwd, raw, source = raw?.startsWith("@") ? "at-address" : "explicit-flag", effectiveDirectory = cwd) {
340
- const address = await resolveContractAddress(cwd, raw, source, effectiveDirectory);
341
- try {
342
- await assertNoRenewRecoverySession(address.repo.stableRoot, address.binding.commissionId);
343
- }
344
- catch (error) {
345
- throw attachAddressCarrier(error, address);
346
- }
347
- return address;
406
+ return resolveContractAddress(cwd, raw, source, effectiveDirectory);
348
407
  }