@astrosheep/keiyaku 4.5.11 → 4.5.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +135 -377
  2. package/build/integrations/pi/keiyaku.ts +11 -116
  3. package/build/src/akuma/akuma-errors.d.ts +1 -0
  4. package/build/src/akuma/akuma-errors.js +1 -0
  5. package/build/src/akuma/akuma-handle.d.ts +1 -0
  6. package/build/src/akuma/akuma-handle.js +6 -7
  7. package/build/src/akuma/akuma-product.d.ts +6 -8
  8. package/build/src/akuma/akuma-product.js +141 -57
  9. package/build/src/akuma/akuma.d.ts +5 -7
  10. package/build/src/akuma/akuma.js +1 -22
  11. package/build/src/akuma/archetype.d.ts +6 -5
  12. package/build/src/akuma/archetype.js +137 -38
  13. package/build/src/akuma/body.d.ts +5 -5
  14. package/build/src/akuma/body.js +223 -63
  15. package/build/src/akuma/call-input.d.ts +4 -0
  16. package/build/src/akuma/call-input.js +21 -0
  17. package/build/src/akuma/call-request.d.ts +5 -8
  18. package/build/src/akuma/call-request.js +28 -73
  19. package/build/src/akuma/fleet-execution.d.ts +26 -0
  20. package/build/src/akuma/fleet-execution.js +109 -0
  21. package/build/src/{library/fleet-result.d.ts → akuma/fleet-observation.d.ts} +13 -57
  22. package/build/src/{library/fleet-result.js → akuma/fleet-observation.js} +4 -25
  23. package/build/src/akuma/fleet-request.d.ts +122 -0
  24. package/build/src/akuma/fleet-request.js +168 -0
  25. package/build/src/akuma/heart/facts.d.ts +23 -0
  26. package/build/src/akuma/heart/facts.js +7 -0
  27. package/build/src/akuma/heart/index.d.ts +43 -4
  28. package/build/src/akuma/heart/index.js +107 -7
  29. package/build/src/akuma/heart/request-authority.d.ts +2 -0
  30. package/build/src/akuma/heart/request-authority.js +28 -3
  31. package/build/src/akuma/heart/request-rows.d.ts +2 -0
  32. package/build/src/akuma/heart/request-rows.js +24 -5
  33. package/build/src/akuma/heart/rows.d.ts +4 -1
  34. package/build/src/akuma/heart/rows.js +46 -19
  35. package/build/src/akuma/heart/schema.d.ts +1 -1
  36. package/build/src/akuma/heart/schema.js +28 -6
  37. package/build/src/akuma/heart/storage.d.ts +1 -1
  38. package/build/src/akuma/heart/tells.d.ts +29 -0
  39. package/build/src/akuma/heart/tells.js +136 -5
  40. package/build/src/akuma/heart/timeline.js +9 -3
  41. package/build/src/akuma/projection-read.js +36 -3
  42. package/build/src/akuma/projection.js +3 -4
  43. package/build/src/akuma/provider.d.ts +4 -0
  44. package/build/src/akuma/provider.js +56 -3
  45. package/build/src/akuma/providers/claude/index.js +3 -0
  46. package/build/src/akuma/providers/codex-app-server/index.js +14 -12
  47. package/build/src/akuma/providers/pi/index.js +7 -1
  48. package/build/src/akuma/publication.js +65 -49
  49. package/build/src/akuma/request-lifecycle.d.ts +5 -4
  50. package/build/src/akuma/request-lifecycle.js +29 -16
  51. package/build/src/akuma/request-rendezvous.d.ts +15 -12
  52. package/build/src/akuma/request-rendezvous.js +80 -51
  53. package/build/src/akuma/request-serve.d.ts +1 -2
  54. package/build/src/akuma/request-serve.js +94 -39
  55. package/build/src/akuma/request-wire.d.ts +57 -26
  56. package/build/src/akuma/request-wire.js +64 -25
  57. package/build/src/akuma/turn-drive.d.ts +5 -4
  58. package/build/src/akuma/turn-drive.js +27 -11
  59. package/build/src/akuma-body.d.ts +4 -6
  60. package/build/src/akuma-body.js +19 -17
  61. package/build/src/bounded-list.d.ts +8 -0
  62. package/build/src/bounded-list.js +14 -0
  63. package/build/src/cli/accepted.d.ts +1 -1
  64. package/build/src/cli/accepted.js +1 -0
  65. package/build/src/cli/commands/akuma-invoke.js +1 -25
  66. package/build/src/cli/commands/contract-help.d.ts +5 -2
  67. package/build/src/cli/commands/contract-help.js +62 -12
  68. package/build/src/cli/commands/contract-invoke.d.ts +5 -0
  69. package/build/src/cli/commands/contract-invoke.js +26 -1
  70. package/build/src/cli/commands/contract.js +50 -18
  71. package/build/src/cli/commands/task-invoke.js +10 -2
  72. package/build/src/cli/commands/task-query.js +3 -4
  73. package/build/src/cli/coordinates.d.ts +13 -0
  74. package/build/src/cli/coordinates.js +38 -24
  75. package/build/src/cli/draft.d.ts +1 -1
  76. package/build/src/cli/invoke.js +14 -1
  77. package/build/src/cli/render/akuma-activity.d.ts +0 -1
  78. package/build/src/cli/render/akuma-activity.js +5 -38
  79. package/build/src/cli/render/akuma.js +4 -1
  80. package/build/src/cli/render/audit.js +4 -1
  81. package/build/src/cli/render/catalog.js +13 -39
  82. package/build/src/cli/render/contract.js +4 -14
  83. package/build/src/cli/render/kanshi-akuma.js +3 -8
  84. package/build/src/cli/render/kanshi.js +8 -34
  85. package/build/src/cli/render/nuke.js +7 -3
  86. package/build/src/cli/render/receipt.d.ts +1 -0
  87. package/build/src/cli/render/receipt.js +8 -0
  88. package/build/src/cli/render/refusal.js +4 -3
  89. package/build/src/cli/render/task.js +4 -37
  90. package/build/src/cli/render/terminal.d.ts +2 -6
  91. package/build/src/cli/render/terminal.js +4 -9
  92. package/build/src/cli/result.d.ts +2 -1
  93. package/build/src/contract-guidance.js +13 -3
  94. package/build/src/contract-worktree.d.ts +1 -0
  95. package/build/src/contract-worktree.js +26 -2
  96. package/build/src/core/facts/codec.d.ts +2 -1
  97. package/build/src/core/facts/codec.js +3 -0
  98. package/build/src/core/facts/gate.d.ts +2 -1
  99. package/build/src/core/facts/gate.js +63 -0
  100. package/build/src/core/verbs/abandon.d.ts +2 -1
  101. package/build/src/core/verbs/abandon.js +16 -0
  102. package/build/src/core/verbs/amend.d.ts +2 -1
  103. package/build/src/core/verbs/amend.js +20 -0
  104. package/build/src/core/verbs/arc.d.ts +2 -1
  105. package/build/src/core/verbs/arc.js +16 -0
  106. package/build/src/core/verbs/attestation.d.ts +2 -1
  107. package/build/src/core/verbs/attestation.js +16 -0
  108. package/build/src/core/verbs/bind.d.ts +2 -1
  109. package/build/src/core/verbs/bind.js +16 -0
  110. package/build/src/core/verbs/deliver.d.ts +2 -1
  111. package/build/src/core/verbs/deliver.js +16 -0
  112. package/build/src/core/verbs/placement.d.ts +2 -1
  113. package/build/src/core/verbs/placement.js +58 -1
  114. package/build/src/dispatch/association.d.ts +13 -0
  115. package/build/src/dispatch/association.js +20 -0
  116. package/build/src/dispatch/index.d.ts +6 -0
  117. package/build/src/dispatch/index.js +27 -3
  118. package/build/src/git/hooks.d.ts +9 -1
  119. package/build/src/git/hooks.js +79 -1
  120. package/build/src/git/nuke.d.ts +2 -1
  121. package/build/src/git/nuke.js +6 -11
  122. package/build/src/git/private-state-seat.d.ts +26 -1
  123. package/build/src/git/private-state-seat.js +78 -13
  124. package/build/src/git/process.d.ts +2 -0
  125. package/build/src/git/reconcile.d.ts +1 -2
  126. package/build/src/git/reconcile.js +0 -6
  127. package/build/src/git/repository.d.ts +0 -2
  128. package/build/src/git/repository.js +27 -25
  129. package/build/src/git/result-codec.d.ts +29 -0
  130. package/build/src/git/result-codec.js +306 -0
  131. package/build/src/git/target-placement.js +2 -8
  132. package/build/src/git/workspace.d.ts +2 -0
  133. package/build/src/git/workspace.js +4 -0
  134. package/build/src/index.d.ts +1 -0
  135. package/build/src/kanshi/fleet.js +1 -1
  136. package/build/src/kanshi/read.js +55 -28
  137. package/build/src/kanshi/report.d.ts +4 -3
  138. package/build/src/kanshi/select.js +14 -1
  139. package/build/src/library/address.d.ts +1 -0
  140. package/build/src/library/address.js +4 -7
  141. package/build/src/library/akuma-creation.d.ts +5 -3
  142. package/build/src/library/akuma-creation.js +90 -33
  143. package/build/src/library/audit.js +2 -1
  144. package/build/src/library/catalog.d.ts +10 -2
  145. package/build/src/library/catalog.js +65 -25
  146. package/build/src/library/composition.d.ts +1 -0
  147. package/build/src/library/composition.js +1 -0
  148. package/build/src/library/configuration.d.ts +4 -21
  149. package/build/src/library/configuration.js +2 -158
  150. package/build/src/library/continuation.d.ts +20 -1
  151. package/build/src/library/continuation.js +36 -0
  152. package/build/src/library/contract-bind.d.ts +1 -0
  153. package/build/src/library/contract-bind.js +16 -5
  154. package/build/src/library/contract-forwarding.d.ts +2 -2
  155. package/build/src/library/contract-forwarding.js +18 -4
  156. package/build/src/library/contract-handle.js +4 -3
  157. package/build/src/library/contract-operations.d.ts +5 -9
  158. package/build/src/library/contract-operations.js +34 -47
  159. package/build/src/library/contract.d.ts +11 -2
  160. package/build/src/library/contract.js +21 -1
  161. package/build/src/library/delivery.d.ts +4 -1
  162. package/build/src/library/delivery.js +47 -0
  163. package/build/src/library/fleet.d.ts +4 -136
  164. package/build/src/library/fleet.js +31 -315
  165. package/build/src/library/keiyaku.d.ts +7 -3
  166. package/build/src/library/keiyaku.js +1 -0
  167. package/build/src/library/mutation.d.ts +431 -3
  168. package/build/src/library/mutation.js +192 -19
  169. package/build/src/library/nuke.d.ts +7 -1
  170. package/build/src/library/nuke.js +18 -4
  171. package/build/src/library/reconcile.d.ts +14 -0
  172. package/build/src/library/reconcile.js +22 -8
  173. package/build/src/library/refusal.d.ts +9 -1
  174. package/build/src/library/refusal.js +76 -0
  175. package/build/src/library/region.d.ts +0 -8
  176. package/build/src/library/region.js +1 -29
  177. package/build/src/library/result-codec.d.ts +2 -0
  178. package/build/src/library/result-codec.js +12 -0
  179. package/build/src/markdown/lex.js +9 -2
  180. package/build/src/markdown/types.d.ts +2 -1
  181. package/build/src/markdown/types.js +3 -1
  182. package/build/src/plugin/akuma-signals.d.ts +12 -0
  183. package/build/src/plugin/akuma-signals.js +14 -0
  184. package/build/src/plugin/public.d.ts +51 -0
  185. package/build/src/plugin/public.js +1 -0
  186. package/build/src/plugin/runtime.d.ts +14 -0
  187. package/build/src/plugin/runtime.js +357 -0
  188. package/build/src/protocol/amend.js +3 -3
  189. package/build/src/protocol/attempt.d.ts +2 -1
  190. package/build/src/protocol/attempt.js +1 -1
  191. package/build/src/protocol/audit.d.ts +1 -1
  192. package/build/src/protocol/audit.js +1 -1
  193. package/build/src/protocol/bind.d.ts +3 -1
  194. package/build/src/protocol/bind.js +26 -0
  195. package/build/src/protocol/completion.js +0 -1
  196. package/build/src/protocol/deliver.d.ts +1 -1
  197. package/build/src/protocol/deliver.js +136 -57
  198. package/build/src/protocol/intent.d.ts +0 -1
  199. package/build/src/protocol/intent.js +0 -1
  200. package/build/src/protocol/operations.d.ts +9 -2
  201. package/build/src/protocol/operations.js +16 -1
  202. package/build/src/protocol/outcome.d.ts +3 -0
  203. package/build/src/protocol/outcome.js +17 -1
  204. package/build/src/protocol/placement.js +10 -3
  205. package/build/src/protocol/read/status.d.ts +8 -0
  206. package/build/src/protocol/read/status.js +49 -0
  207. package/build/src/protocol/reintegrate.js +10 -3
  208. package/build/src/protocol/result-codec.d.ts +22 -0
  209. package/build/src/protocol/result-codec.js +442 -0
  210. package/build/src/protocol/review.d.ts +1 -1
  211. package/build/src/protocol/review.js +81 -46
  212. package/build/src/protocol/run.d.ts +22 -2
  213. package/build/src/protocol/run.js +119 -29
  214. package/build/src/runtime/proc/lifecycle.js +1 -1
  215. package/build/src/runtime/proc/line-rpc.d.ts +7 -1
  216. package/build/src/runtime/proc/line-rpc.js +49 -9
  217. package/build/src/runtime/proc/run.d.ts +0 -1
  218. package/build/src/runtime/proc/run.js +1 -11
  219. package/build/src/runtime/proc/termination.d.ts +1 -0
  220. package/build/src/runtime/proc/termination.js +63 -18
  221. package/build/src/runtime/proc/windows-launch.exe +0 -0
  222. package/build/src/settings.d.ts +10 -0
  223. package/build/src/settings.js +88 -2
  224. package/build/src/settlement/result-codec.d.ts +2 -0
  225. package/build/src/settlement/result-codec.js +56 -0
  226. package/build/src/settlement/settle.d.ts +5 -12
  227. package/build/src/settlement/settle.js +60 -55
  228. package/build/src/task/board.d.ts +3 -0
  229. package/build/src/task/board.js +22 -4
  230. package/build/src/task/catalog.d.ts +13 -2
  231. package/build/src/task/catalog.js +59 -5
  232. package/build/src/task/compose.d.ts +4 -2
  233. package/build/src/task/compose.js +56 -30
  234. package/build/src/task/created-observation.d.ts +39 -0
  235. package/build/src/task/created-observation.js +26 -0
  236. package/build/src/task/identity.d.ts +6 -0
  237. package/build/src/task/identity.js +48 -7
  238. package/build/src/task/index.d.ts +4 -3
  239. package/build/src/task/index.js +19 -16
  240. package/build/src/task/input.d.ts +3 -1
  241. package/build/src/task/input.js +7 -4
  242. package/build/src/task/lifecycle-operations.d.ts +6 -0
  243. package/build/src/task/lifecycle-operations.js +116 -0
  244. package/build/src/task/mutation-result.d.ts +762 -20
  245. package/build/src/task/mutation-result.js +34 -9
  246. package/build/src/task/mutation.d.ts +4 -9
  247. package/build/src/task/mutation.js +17 -52
  248. package/build/src/task/operation-types.d.ts +114 -0
  249. package/build/src/task/operation-types.js +1 -0
  250. package/build/src/task/operations.d.ts +6 -113
  251. package/build/src/task/operations.js +75 -117
  252. package/build/src/task/query.d.ts +8 -11
  253. package/build/src/task/query.js +7 -14
  254. package/build/src/task/store.d.ts +11 -1
  255. package/build/src/task/store.js +169 -55
  256. package/build/src/task/view.d.ts +4 -0
  257. package/build/src/task/view.js +7 -0
  258. package/build/src/verification/declaration.d.ts +1 -0
  259. package/build/src/verification/declaration.js +18 -1
  260. package/build/src/verification/execution.d.ts +0 -1
  261. package/build/src/verification/execution.js +5 -5
  262. package/build/src/world.d.ts +0 -2
  263. package/build/src/world.js +3 -7
  264. package/package.json +17 -5
  265. package/build/src/cli/square-edge.d.ts +0 -7
  266. package/build/src/cli/square-edge.js +0 -159
  267. package/build/src/git/ref-migration.d.ts +0 -22
  268. package/build/src/git/ref-migration.js +0 -55
  269. package/build/src/library/contract-forwarding-reconciliation-result.d.ts +0 -102
  270. package/build/src/library/contract-forwarding-reconciliation-result.js +0 -92
  271. package/build/src/library/contract-forwarding-result.d.ts +0 -4270
  272. package/build/src/library/contract-forwarding-result.js +0 -521
@@ -11,7 +11,6 @@ function targetLine(row, abbreviations) {
11
11
  : undefined;
12
12
  return [`target ${target}`, ...(lag === undefined ? [] : [lag]), ...targetMovementFacts(row, abbreviations)].join(" · ");
13
13
  }
14
- const CATALOG_VISIBLE_AKUMA = 10;
15
14
  function akumaMark(life) {
16
15
  if (life === "running")
17
16
  return "●";
@@ -23,15 +22,6 @@ function akumaMark(life) {
23
22
  return "!";
24
23
  return "?";
25
24
  }
26
- function akumaUpdatedAt(row) {
27
- const lifeAt = "lifeAt" in row ? row.lifeAt : null;
28
- const activityAt = "lastActivityAt" in row ? row.lastActivityAt : null;
29
- if (lifeAt === null)
30
- return activityAt;
31
- if (activityAt === null)
32
- return lifeAt;
33
- return lifeAt > activityAt ? lifeAt : activityAt;
34
- }
35
25
  function relativeAge(source, observedAt) {
36
26
  if (source === null)
37
27
  return null;
@@ -49,20 +39,9 @@ function relativeAge(source, observedAt) {
49
39
  return `${Math.floor(hours / 24)}d`;
50
40
  }
51
41
  function renderAkumaCatalog(catalog) {
52
- const ordered = [...catalog.rows].sort((left, right) => {
53
- const leftAt = akumaUpdatedAt(left);
54
- const rightAt = akumaUpdatedAt(right);
55
- if (leftAt === rightAt)
56
- return 0;
57
- if (leftAt === null)
58
- return 1;
59
- if (rightAt === null)
60
- return -1;
61
- return leftAt > rightAt ? -1 : 1;
62
- });
63
- const rows = ordered.slice(0, CATALOG_VISIBLE_AKUMA);
42
+ const rows = catalog.rows;
64
43
  const lines = [
65
- `akuma instances ${rows.length} of ${ordered.length} known`,
44
+ `akuma instances ${rows.length} recent`,
66
45
  ...(catalog.archetype === null ? [] : [` scope ${safeText(catalog.archetype)}`]),
67
46
  "",
68
47
  ];
@@ -72,10 +51,8 @@ function renderAkumaCatalog(catalog) {
72
51
  const ages = [relativeAge(lifeAt, catalog.observedAt), relativeAge(activityAt, catalog.observedAt)].filter((age) => age !== null);
73
52
  lines.push(`${akumaMark(row.life)} ${safeText(row.id)} · ${row.life}${ages.length === 0 ? "" : ` · ${ages.join(" · ")}`}`);
74
53
  }
75
- if (rows.length < ordered.length) {
76
- const selector = catalog.archetype === null ? '"aku/*/*"' : `aku/${catalog.archetype}/`;
77
- lines.push("", ` + ${ordered.length - rows.length} more akuma not shown`, ` keiyaku ls ${selector}`);
78
- }
54
+ if (catalog.hasMore)
55
+ lines.push("…");
79
56
  return lines.join("\n");
80
57
  }
81
58
  function catalogMark(row) {
@@ -110,14 +87,10 @@ function renderContractCatalog(catalog) {
110
87
  ...(catalog.state === null ? [] : [catalog.state]),
111
88
  ...catalog.rows.flatMap(gitIdsInRow),
112
89
  ]);
113
- const rows = catalog.rows.filter((row) => row.disposition === "active");
114
- const candidates = rows.filter((row) => row.delivery !== null).length;
115
- const header = [
116
- `${rows.length} active · ${candidates} candidate${candidates === 1 ? "" : "s"}`,
117
- catalog.state === null
118
- ? `observedAt ${catalog.observedAt}`
119
- : `contract state ${displayGitId(catalog.state, abbreviations)} · observedAt ${catalog.observedAt}`,
120
- ];
90
+ const rows = catalog.rows;
91
+ const header = catalog.state === null
92
+ ? `observedAt ${catalog.observedAt}`
93
+ : `contract state ${displayGitId(catalog.state, abbreviations)} · observedAt ${catalog.observedAt}`;
121
94
  const blocks = rows.map((row) => {
122
95
  const lines = [
123
96
  `${catalogMark(row)} ${safeText(row.id)} · ${row.phase} · ${formatAge(row.phaseAt, catalog.observedAt)} · ${safeText(row.title ?? "title unavailable")}`,
@@ -128,13 +101,14 @@ function renderContractCatalog(catalog) {
128
101
  ];
129
102
  return lines.join("\n");
130
103
  });
131
- return [...header, ...(blocks.length === 0 ? [] : ["", ...blocks])].join("\n");
104
+ return [header, ...(blocks.length === 0 ? [] : ["", ...blocks]), ...(catalog.hasMore ? ["…"] : [])].join("\n");
132
105
  }
133
106
  export function renderCatalogText(catalog) {
134
107
  if (catalog.kind === "tasks") {
135
- return catalog.rows
136
- .map((row) => `${safeText(row.id)} - P${row.priority} - ${row.disposition} - ${safeText(row.title)}`)
137
- .join("\n");
108
+ return [
109
+ ...catalog.rows.map((row) => `${safeText(row.id)} - P${row.priority} - ${row.disposition} - ${safeText(row.title)}`),
110
+ ...(catalog.hasMore ? ["…"] : []),
111
+ ].join("\n");
138
112
  }
139
113
  if (catalog.kind === "contracts")
140
114
  return renderContractCatalog(catalog);
@@ -1,5 +1,5 @@
1
1
  import { renderAcceptedAudit } from "./audit.js";
2
- import { appendHookPayload, cleanupLines, hookFailureSummary, leakLines, outcomeLines, receiptPayload, receiptRow, reuseLines, stopLines, titleLines, } from "./receipt.js";
2
+ import { appendHookPayload, cleanupLines, hookFailureSummary, leakLines, outcomeLines, seatCloseLines, receiptPayload, receiptRow, reuseLines, stopLines, titleLines, } from "./receipt.js";
3
3
  import { gitShortStat, renderOpaqueBlock } from "./terminal.js";
4
4
  const HANG = " ";
5
5
  function isRecord(value) {
@@ -11,16 +11,6 @@ function retryLines(detail, indent, columns) {
11
11
  }
12
12
  return renderOpaqueBlock(detail.kind, indent, columns);
13
13
  }
14
- function refMigrationConflictRows(lag, columns) {
15
- const lines = [];
16
- receiptRow(lines, "!", "lag", [
17
- {
18
- text: `ref-migration-conflict legacy=${lag.legacyRef}@${lag.legacyOid} current=${lag.currentRef}@${lag.currentOid}`,
19
- opaque: true,
20
- },
21
- ], columns);
22
- return lines;
23
- }
24
14
  function lagRows(lag, columns) {
25
15
  const lines = [];
26
16
  if (lag.kind === "worktree-retained") {
@@ -59,9 +49,6 @@ function lagRows(lag, columns) {
59
49
  receiptRow(lines, "!", "lag", [{ text: `contract-file-failed ${lag.worktree} ${lag.path}`, opaque: true }], columns);
60
50
  receiptPayload(lines, "diagnostic", lag.diagnostic);
61
51
  }
62
- else if (lag.kind === "ref-migration-conflict") {
63
- return refMigrationConflictRows(lag, columns);
64
- }
65
52
  else {
66
53
  receiptRow(lines, "!", "lag", [{ text: `reconcile-failed ${lag.stage}`, opaque: true }], columns);
67
54
  receiptPayload(lines, "diagnostic", lag.diagnostic);
@@ -144,6 +131,9 @@ function acceptedLagRows(result, columns) {
144
131
  for (const lag of result.settlementLags) {
145
132
  pushBlock(obligations, settlementLagRows(lag, columns));
146
133
  }
134
+ if (result.seatClose !== undefined && result.seatClose.length > 0) {
135
+ pushBlock(obligations, seatCloseLines(result.seatClose, columns));
136
+ }
147
137
  return obligations;
148
138
  }
149
139
  function indentRecord(lines) {
@@ -1,4 +1,3 @@
1
- import { visibleFleetRows } from "../../kanshi/fleet.js";
2
1
  import { boundedActivity, elapsedMilliseconds, entityLines, identityLine, plumbFacts, RECENT_TONE_MS, renderSectionBlock, safeText, tone, } from "./terminal.js";
3
2
  const PLUMB = " │ ";
4
3
  const NARROW_COLUMNS = 72;
@@ -67,7 +66,7 @@ function renderAkuma(report, context) {
67
66
  return ["AKUMA // absent", "", `${PLUMB}akuma absent`];
68
67
  if (section.kind === "failed")
69
68
  return ["AKUMA // unavailable", "", tone(`! ${safeText(section.failure.message)}`, "alert", context.color)];
70
- const rows = visibleFleetRows(section.value.rows);
69
+ const rows = section.value.rows;
71
70
  const rowLines = rows.map((row) => {
72
71
  const statusTone = akumaStatusTone(row, report.observedAt);
73
72
  const mark = statusTone === null ? akumaMark(row.life) : tone(akumaMark(row.life), statusTone, context.color);
@@ -101,15 +100,11 @@ function renderAkuma(report, context) {
101
100
  ? lines
102
101
  : [...lines, ...plumbFacts([boundedActivity(snapshot, context.columns, PLUMB)], context.columns)];
103
102
  });
104
- const header = `AKUMA // ${rows.length} recent · ${section.value.rows.length} known`;
103
+ const header = `AKUMA // ${rows.length} recent`;
105
104
  const rendered = renderSectionBlock({
106
105
  name: "AKUMA",
107
- unit: "akuma",
108
- selector: "aku",
109
- continuation: 'keiyaku ls "aku/*/*"',
110
106
  rows: rowLines,
111
- total: section.value.rows.length,
112
- neutral: true,
107
+ hasMore: section.value.hasMore,
113
108
  });
114
109
  return [header, ...rendered.slice(1)];
115
110
  }
@@ -2,7 +2,6 @@ import { abbreviateGitIds, afterWording, candidateFact, dependentWording, displa
2
2
  import { entityLines, elapsedMilliseconds, identityLine, plumbFacts, RECENT_TONE_MS, renderTextBlock, renderSectionBlock, safeText, tone, } from "./terminal.js";
3
3
  import { akumaMark, endpointFact, formatAge, NARROW_COLUMNS, renderAkuma } from "./kanshi-akuma.js";
4
4
  const PLUMB = " │ ";
5
- const MAX_VISIBLE_ROWS = 10;
6
5
  const REVIEW_ATTENTION_MS = 15 * 60 * 1_000;
7
6
  const PENDING_ATTENTION_MS = 60 * 60 * 1_000;
8
7
  function contractHasError(row) {
@@ -201,6 +200,8 @@ function semanticBlock(name, facts, context) {
201
200
  return lines;
202
201
  }
203
202
  function namespaceTaskFacts(row) {
203
+ if (row.namespaceTasks === undefined)
204
+ return [];
204
205
  if (row.namespaceTasks.kind === "absent")
205
206
  return [];
206
207
  if (row.namespaceTasks.kind === "failed") {
@@ -257,12 +258,6 @@ function candidateFacts(row, abbreviations) {
257
258
  `change ${delivery.integration.changeId}`,
258
259
  ];
259
260
  }
260
- function isTerminalContract(row) {
261
- return row.phase === "claimed" || row.phase === "abandoned";
262
- }
263
- function isTerminalTask(row) {
264
- return row.disposition === "done" || row.disposition === "drop";
265
- }
266
261
  function renderWorldContractRow(row, report, context) {
267
262
  const title = row.title ?? "title unavailable";
268
263
  const contractFacts = [
@@ -287,35 +282,18 @@ function renderWorldContractRow(row, report, context) {
287
282
  context,
288
283
  }).concat(plumbFacts(linkedFacts, context.columns));
289
284
  }
290
- function selectRecentRows(rows, timestamp) {
291
- return [...rows]
292
- .sort((left, right) => {
293
- const leftAt = timestamp(left);
294
- const rightAt = timestamp(right);
295
- if (leftAt === rightAt)
296
- return 0;
297
- return leftAt > rightAt ? -1 : 1;
298
- })
299
- .slice(0, MAX_VISIBLE_ROWS);
300
- }
301
285
  function renderContracts(report, context) {
302
286
  const section = report.contracts;
303
287
  if (section.kind === "absent")
304
288
  return ["CONTRACTS // absent", "", `${PLUMB}contracts absent`];
305
289
  if (section.kind === "failed")
306
290
  return ["CONTRACTS // unavailable", "", tone(`! ${safeText(section.failure.message)}`, "alert", context.color)];
307
- const live = section.value.rows.filter((row) => !isTerminalContract(row));
308
- const rows = selectRecentRows(live, (row) => row.lastJournalAt);
309
- const candidates = live.filter((row) => row.delivery !== null).length;
310
291
  const rendered = renderSectionBlock({
311
292
  name: "CONTRACTS",
312
- unit: "keiyaku",
313
- selector: "kei",
314
- rows: rows.map((row) => renderWorldContractRow(row, report, context)),
315
- total: live.length,
293
+ rows: section.value.rows.map((row) => renderWorldContractRow(row, report, context)),
316
294
  });
317
- const header = `CONTRACTS // ${live.length} live · ${candidates} candidates`;
318
- return [header, ...rendered.slice(1)];
295
+ const header = "CONTRACTS // recent";
296
+ return [header, ...rendered.slice(1), ...(section.value.hasMore === true ? ["…"] : [])];
319
297
  }
320
298
  function renderSelectedContract(report, context) {
321
299
  const section = report.contracts;
@@ -334,8 +312,7 @@ function renderTasks(report, context) {
334
312
  return ["TASKS // absent", "", `${PLUMB}tasks absent`];
335
313
  if (section.kind === "failed")
336
314
  return ["TASKS // unavailable", "", tone(`! ${safeText(section.failure.message)}`, "alert", context.color)];
337
- const live = section.value.rows.filter((row) => !isTerminalTask(row));
338
- const rows = selectRecentRows(live, (row) => row.updatedAt);
315
+ const rows = section.value.rows;
339
316
  const rowLines = rows.map((row) => {
340
317
  const relation = row.contract === undefined ? ["unbound"] : [endpointFact(row.contract.id, row.contract.observed)];
341
318
  const childFacts = row.children === undefined ? [] : [`children ${row.children.live} live · ${row.children.total} total`];
@@ -356,15 +333,12 @@ function renderTasks(report, context) {
356
333
  });
357
334
  });
358
335
  return [
359
- `TASKS // ${live.length} live`,
336
+ "TASKS // recent",
360
337
  ...renderSectionBlock({
361
338
  name: "TASKS",
362
- unit: "task",
363
- selector: "task",
364
- continuation: "keiyaku task ls --world",
365
339
  rows: rowLines,
366
- total: live.length,
367
340
  }).slice(1),
341
+ ...(section.value.hasMore ? ["…"] : []),
368
342
  ];
369
343
  }
370
344
  export function renderKanshiText(report, context = { columns: 80, color: false }, selection = "world") {
@@ -1,7 +1,11 @@
1
1
  export function renderNukeText(result) {
2
- return result.kind === "success"
3
- ? `nuke success ${result.world}`
4
- : `nuke failed ${result.world}\n${result.diagnostic}`;
2
+ const seatClose = result.seatClose === undefined || result.seatClose.length === 0
3
+ ? []
4
+ : result.seatClose.flatMap((lag) => [`lag ${lag.kind}`, `diagnostic ${lag.diagnostic}`]);
5
+ if (result.kind === "success") {
6
+ return [`nuke success ${result.world}`, ...seatClose].join("\n");
7
+ }
8
+ return [`nuke failed ${result.world}`, result.diagnostic, ...seatClose].join("\n");
5
9
  }
6
10
  export function nukeExitCode(result) {
7
11
  return result.kind === "failed" ? 2 : 0;
@@ -17,4 +17,5 @@ export declare function reuseLines(reuse: VerificationReuse | undefined, columns
17
17
  export declare function stopLines(stop: VerificationStop | PlacementStop, columns: number, addressed: string, dependent?: string): readonly string[];
18
18
  export declare function cleanupLines(cleanup: NonNullable<AcceptedDeliverResult["cleanup"]>, columns: number): readonly string[];
19
19
  export declare function leakLines(leak: NonNullable<AcceptedDeliverResult["leak"]>, columns: number): readonly string[];
20
+ export declare function seatCloseLines(seatClose: NonNullable<AcceptedDeliverResult["seatClose"]>, columns: number): readonly string[];
20
21
  export {};
@@ -186,3 +186,11 @@ export function leakLines(leak, columns) {
186
186
  receiptPayload(lines, "diagnostic", leak.diagnostic);
187
187
  return lines;
188
188
  }
189
+ export function seatCloseLines(seatClose, columns) {
190
+ const lines = [];
191
+ for (const lag of seatClose) {
192
+ receiptRow(lines, "!", "lag", [{ text: lag.kind }], columns);
193
+ receiptPayload(lines, "diagnostic", lag.diagnostic);
194
+ }
195
+ return lines;
196
+ }
@@ -28,13 +28,14 @@ function refusalHead(kind, identity, details) {
28
28
  function renderDirtyRefusal(refusal, indent, columns, identity) {
29
29
  const lines = [];
30
30
  wrap(lines, refusalHead(refusal.kind, identity, []), indent, columns);
31
+ wrap(lines, "reason worktree has uncommitted changes", indent, columns);
31
32
  for (const name of ["staged", "unstaged", "untracked", "submodules"]) {
32
33
  lines.push(...collectionLines(name, refusal[name], indent, columns));
33
34
  }
34
35
  wrap(lines, gitShortStat(refusal.shortStat), indent, columns);
35
- if (refusal.option !== undefined) {
36
- wrap(lines, `option ${refusal.option.flag} ${refusal.option.available ? "available" : "unavailable"}`, indent, columns);
37
- }
36
+ wrap(lines, "--include-dirty captures all non-ignored worktree bytes in the candidate", indent, columns);
37
+ if (refusal.option?.available === false)
38
+ wrap(lines, "--include-dirty unavailable while submodules have changes", indent, columns);
38
39
  return lines;
39
40
  }
40
41
  export function renderRefusalFacts(refusal, indent, columns, addressed) {
@@ -87,10 +87,8 @@ function edge(label, ref, mark) {
87
87
  const prefix = mark === undefined ? ` ${label}` : ` ${mark} ${label}`;
88
88
  return `${prefix} ${ref.id} · ${ref.state}`;
89
89
  }
90
- function pageHeading(view, page) {
91
- if (page.truncated)
92
- return `${view} ${page.returned} of ${page.total} · limit ${page.returned}`;
93
- return `${view} ${page.returned}`;
90
+ function pageHeading(view) {
91
+ return view;
94
92
  }
95
93
  function updatedAge(updatedAt) {
96
94
  const elapsed = Math.max(0, performance.timeOrigin + performance.now() - Date.parse(updatedAt));
@@ -126,42 +124,11 @@ function renderListRow(item, columns, omitDisposition) {
126
124
  }
127
125
  return lines;
128
126
  }
129
- function shellQuote(value) {
130
- return `'${value.replaceAll("'", "'\\''")}'`;
131
- }
132
- function recoveryCommand(command, total) {
133
- const parts = ["keiyaku", "task", command.action];
134
- if (command.action === "ls") {
135
- if (command.positionals.length > 0)
136
- parts.push(command.positionals[0]);
137
- if (command.flags.closed === true)
138
- parts.push("--closed");
139
- if (command.flags.all === true)
140
- parts.push("--all");
141
- }
142
- if (command.flags.world === true)
143
- parts.push("--world");
144
- if ((command.action === "ready" || command.action === "blocked") && typeof command.flags.parent === "string") {
145
- parts.push("--parent", command.flags.parent);
146
- }
147
- if (command.action === "query" && typeof command.flags.where === "string")
148
- parts.push("--where", shellQuote(command.flags.where));
149
- if (command.action === "query" && typeof command.flags.sort === "string")
150
- parts.push("--sort", command.flags.sort);
151
- parts.push("--limit", String(total));
152
- return parts.join(" ");
153
- }
154
127
  function renderRows(command, result, columns) {
155
128
  if (result.kind !== "accepted")
156
129
  return renderFailure(command.action, result, columns);
157
130
  const view = command.action === "ls" ? "tasks" : command.action;
158
- const footer = result.value.truncated
159
- ? [
160
- "",
161
- ` + ${result.value.total - result.value.returned} more not shown`,
162
- ` ${recoveryCommand(command, result.value.total)}`,
163
- ]
164
- : [];
131
+ const footer = result.value.hasMore ? ["…"] : [];
165
132
  const scope = command.action === "ls"
166
133
  ? command.flags.world === true
167
134
  ? "world"
@@ -169,7 +136,7 @@ function renderRows(command, result, columns) {
169
136
  ? `namespace ${command.positionals[0].replace(/^task\//u, "").replace(/\/$/u, "") || "root"}`
170
137
  : "current namespace"
171
138
  : undefined;
172
- const heading = scope === undefined ? pageHeading(view, result.value) : `${pageHeading(view, result.value)} · ${scope}`;
139
+ const heading = scope === undefined ? pageHeading(view) : `${pageHeading(view)} · ${scope}`;
173
140
  return [
174
141
  heading,
175
142
  ...result.value.rows.flatMap((item) => renderListRow(item, columns, command.action === "ready")),
@@ -47,13 +47,9 @@ export declare function entityLines(entity: Readonly<{
47
47
  facts: readonly string[];
48
48
  context: TextRenderContext;
49
49
  }>): readonly string[];
50
- export declare function renderSectionBlock({ name, unit, selector, continuation, rows, total, neutral, }: Readonly<{
50
+ export declare function renderSectionBlock({ name, rows, hasMore, }: Readonly<{
51
51
  name: string;
52
- unit: string;
53
- selector: string;
54
- continuation?: string;
55
52
  rows: readonly (readonly string[])[];
56
- total: number;
57
- neutral?: boolean;
53
+ hasMore?: boolean;
58
54
  }>): readonly string[];
59
55
  export {};
@@ -242,14 +242,9 @@ export function entityLines(entity) {
242
242
  ...plumbFacts(facts, context.columns),
243
243
  ];
244
244
  }
245
- export function renderSectionBlock({ name, unit, selector, continuation, rows, total, neutral = false, }) {
246
- const lines = [`[ ${name} ] ${total}${neutral ? ` ${unit}` : " live"}`, "", ...rows.flat()];
247
- if (lines.at(-1) !== "")
248
- lines.push("");
249
- lines.push(rows.length === total
250
- ? ` (all ${total}${neutral ? "" : " live"} ${unit} shown)`
251
- : ` + ${total - rows.length} more${neutral ? "" : " live"} ${unit} not shown`);
252
- if (rows.length !== total)
253
- lines.push(` ${continuation ?? `keiyaku ls ${selector}/`}`);
245
+ export function renderSectionBlock({ name, rows, hasMore = false, }) {
246
+ const lines = [`[ ${name} ]`, "", ...rows.flat()];
247
+ if (hasMore)
248
+ lines.push("…");
254
249
  return lines;
255
250
  }
@@ -27,7 +27,7 @@ export type AcceptedFact = (AcceptedFactEnvelope & Readonly<{
27
27
  }>["data"];
28
28
  }>);
29
29
  export type Lag = ReconcileReport["lag"][number];
30
- type MutationEnvelope = Pick<MutationResult<unknown>, "head" | "settlementLags" | "recoverySnapshot">;
30
+ type MutationEnvelope = Pick<MutationResult<unknown>, "head" | "settlementLags" | "recoverySnapshot" | "seatClose">;
31
31
  export type AcceptedEnvelope = Readonly<{
32
32
  kind: "accepted";
33
33
  contract: ContractId;
@@ -36,6 +36,7 @@ export type AcceptedEnvelope = Readonly<{
36
36
  settlementLags: MutationEnvelope["settlementLags"];
37
37
  recoverySnapshot?: MutationEnvelope["recoverySnapshot"];
38
38
  lag?: readonly [Lag, ...Lag[]];
39
+ seatClose?: MutationEnvelope["seatClose"];
39
40
  }>;
40
41
  export type AcceptedBindResult = AcceptedEnvelope & Readonly<{
41
42
  verb: "bind";
@@ -21,14 +21,16 @@ export const CONTRACT_DELIVERER_SKILL = [
21
21
  "",
22
22
  "`deliver` creates the immutable candidate commit and prepares its integration with the target as it exists for that invocation. It runs or reuses Verification, records the candidate, and requests placement. If the target moves, run `deliver` again; Keiyaku recomputes the current-target integration. A manual rebase is optional candidate shaping, not target refresh.",
23
23
  "",
24
- "When the finished worktree itself is the candidate, use `deliver --include-dirty`; Keiyaku captures its complete non-ignored state without moving this worktree's HEAD or real index.",
24
+ "`keiyaku deliver` submits the current state of the whole worktree as the Contract's candidate.",
25
+ "",
26
+ "When the finished worktree itself is the candidate, use `deliver --include-dirty`; Keiyaku captures its complete non-ignored state without moving this worktree's HEAD or real index. When materializing a conflict, the same flag preserves those bytes as the handoff base before projecting the judged conflict.",
25
27
  "",
26
28
  "```bash",
27
29
  "keiyaku deliver <contract> --include-dirty",
28
- "keiyaku deliver <contract> --materialize-conflict",
30
+ "keiyaku deliver <contract> --materialize-conflict --include-dirty",
29
31
  "```",
30
32
  "",
31
- "If delivery reports an integration conflict, materialize that judged conflict, resolve it in this worktree, then deliver the resolved bytes with `--include-dirty`. Deliver when the candidate is ready and the evidence is in place; delivery records and requests placement, while the Reviewer independently judges any declared review gate.",
33
+ "If delivery reports an integration conflict, materialize that judged conflict with `--include-dirty`; Keiyaku preserves the complete non-ignored worktree bytes as the handoff base before projecting the conflict. Resolve it in this worktree, then deliver the resolved bytes with `--include-dirty`. Deliver when the candidate is ready and the evidence is in place; delivery records and requests placement, while the Reviewer independently judges any declared review gate.",
32
34
  ]
33
35
  .join("\n")
34
36
  .concat("\n");
@@ -61,6 +63,14 @@ export const CONTRACT_REVIEWER_SKILL = [
61
63
  "",
62
64
  "## Review",
63
65
  "",
66
+ "`keiyaku review` records a verdict on the whole Contract.",
67
+ "",
68
+ "Record `--satisfied` when you have verified two things and hold the evidence: the current worktree meets every Criterion, and the result actually achieves what the Objective set out to do. Criteria are the written checks; the Objective is the intent behind them — work that passes every check but misses the intent is not satisfied. If a delivered candidate, prerequisites, and all declared gates are in place, the Contract is claimed immediately and cannot be reopened.",
69
+ "",
70
+ "Record `--unsatisfied` when any Criterion is unmet, the work is incomplete, or the result does not achieve the Objective's intent — and state what is missing.",
71
+ "",
72
+ "If the commission asked for findings rather than a verdict, report them in your reply without running `keiyaku review`.",
73
+ "",
64
74
  "```bash",
65
75
  "keiyaku review <contract> --satisfied --summary <conclusion>",
66
76
  "keiyaku review <contract> --unsatisfied --summary <finding>",
@@ -24,6 +24,7 @@ export type ContractFileLag = Readonly<{
24
24
  path: string;
25
25
  diagnostic: string;
26
26
  }>;
27
+ export declare function decodeContractFileLag(value: unknown): ContractFileLag;
27
28
  export type ContractWorktreeResult = Readonly<{
28
29
  effects: readonly ContractFileEffect[];
29
30
  lag: readonly ContractFileLag[];
@@ -8,6 +8,8 @@ import { worktreeGitDirectory, worktreeRoot } from "./git/repository.js";
8
8
  import { GitPlumbingError, runGit } from "./git/process.js";
9
9
  import { worktreePath } from "./git/workspace.js";
10
10
  import { appointmentFor, placeRegisterPath } from "./workspace-place.js";
11
+ import { repairNamespaceContext } from "./task/context.js";
12
+ import { contractNamespace } from "./task/identity.js";
11
13
  const IGNORE_BYTES = ".gitignore\nKEIYAKU.md\n";
12
14
  const PRIMARY_IGNORE_BYTES = "*\n!settings.json\n!tasks/\n!tasks/**\n";
13
15
  const SEAT_IGNORE_BYTES = ".gitignore\nSKILL.md\n";
@@ -15,6 +17,22 @@ const SEAT_SKILLS = [
15
17
  ["keiyaku-deliver", CONTRACT_DELIVERER_SKILL],
16
18
  ["keiyaku-review", CONTRACT_REVIEWER_SKILL],
17
19
  ];
20
+ export function decodeContractFileLag(value) {
21
+ if (value === null || typeof value !== "object" || Array.isArray(value))
22
+ throw new Error("malformed contract-file lag");
23
+ const object = value;
24
+ if (object.kind !== "contract-file-failed")
25
+ throw new Error("malformed contract-file lag");
26
+ if (Object.keys(object).some((key) => key !== "kind" && key !== "worktree" && key !== "path" && key !== "diagnostic"))
27
+ throw new Error("malformed contract-file lag");
28
+ if (typeof object.worktree !== "string" || object.worktree.trim() === "")
29
+ throw new Error("malformed contract-file lag");
30
+ if (typeof object.path !== "string" || object.path.trim() === "")
31
+ throw new Error("malformed contract-file lag");
32
+ if (typeof object.diagnostic !== "string" || object.diagnostic.trim() === "")
33
+ throw new Error("malformed contract-file lag");
34
+ return { kind: "contract-file-failed", worktree: object.worktree, path: object.path, diagnostic: object.diagnostic };
35
+ }
18
36
  async function isTracked(repository, relativePath) {
19
37
  try {
20
38
  await runGit(repository, ["ls-files", "--error-unmatch", "--", relativePath]);
@@ -110,7 +128,7 @@ async function materializeSeatSkill(repository, worktree, seat, bytes) {
110
128
  await repair(repository, worktree, skill, bytes),
111
129
  ];
112
130
  }
113
- async function materialize(repository, worktree, guidance) {
131
+ async function materialize(repository, worktree, guidance, namespace) {
114
132
  const effects = [];
115
133
  const failed = (target, path, error) => ({
116
134
  effects,
@@ -148,6 +166,12 @@ async function materialize(repository, worktree, guidance) {
148
166
  };
149
167
  }
150
168
  const scoped = { ...repository, effectiveCwd: worktree };
169
+ try {
170
+ await repairNamespaceContext(worktree, namespace);
171
+ }
172
+ catch (error) {
173
+ return failed(worktree, join(worktree, ".keiyaku", "namespace", "current"), error);
174
+ }
151
175
  for (const [relativePath, bytes] of [
152
176
  [".keiyaku/.gitignore", IGNORE_BYTES],
153
177
  [".keiyaku/KEIYAKU.md", guidance],
@@ -186,5 +210,5 @@ export async function projectContractWorktree(repository, state, register, _onCo
186
210
  ],
187
211
  };
188
212
  }
189
- return await materialize(repository, worktreePath(repository, appointed.place), renderContractGuidance(state));
213
+ return await materialize(repository, worktreePath(repository, appointed.place), renderContractGuidance(state), contractNamespace(state.id));
190
214
  }
@@ -1,5 +1,6 @@
1
- import type { JournalEntry } from "./types.js";
1
+ import type { DeliverData, JournalEntry } from "./types.js";
2
2
  /** Validate one in-memory journal value with the persisted-fact authority. */
3
3
  export declare function decodeJournalEntry(value: unknown): JournalEntry;
4
+ export declare function decodeDeliverData(value: unknown): DeliverData;
4
5
  export declare function encodeEntry(entry: JournalEntry): string;
5
6
  export declare function decodeJournal(journal: string): JournalEntry[];
@@ -239,6 +239,9 @@ function validateEntry(value) {
239
239
  export function decodeJournalEntry(value) {
240
240
  return validateEntry(value);
241
241
  }
242
+ export function decodeDeliverData(value) {
243
+ return validateData("deliver", value);
244
+ }
242
245
  function canonicalValue(value) {
243
246
  if (value === null)
244
247
  return "null";
@@ -1,4 +1,4 @@
1
- import type { AttestationEntry, ContractState, Gate } from "./types.js";
1
+ import { type AttestationEntry, type ContractState, type Gate } from "./types.js";
2
2
  export type GateCurrent = Readonly<{
3
3
  kind: "attested";
4
4
  verdict: "satisfied" | "unsatisfied";
@@ -14,6 +14,7 @@ export type GateReport = Readonly<{
14
14
  gate: Gate;
15
15
  current: GateCurrent;
16
16
  }>;
17
+ export declare function decodeGateReport(value: unknown): GateReport;
17
18
  export type GateReports = Readonly<{
18
19
  reports: readonly GateReport[];
19
20
  satisfied: boolean;
@@ -1,4 +1,67 @@
1
1
  import { currentSubjectPredicate } from "../subject.js";
2
+ import { gate } from "./types.js";
3
+ function fail() {
4
+ throw new Error("malformed gate report");
5
+ }
6
+ function object(value) {
7
+ if (value === null || typeof value !== "object" || Array.isArray(value))
8
+ fail();
9
+ return value;
10
+ }
11
+ function decodeAttestedGateCurrent(current) {
12
+ for (const key of Object.keys(current)) {
13
+ if (key !== "kind" && key !== "verdict" && key !== "at" && key !== "summary")
14
+ fail();
15
+ }
16
+ if (current.kind !== "attested")
17
+ fail();
18
+ if (current.verdict !== "satisfied" && current.verdict !== "unsatisfied")
19
+ fail();
20
+ if (typeof current.at !== "string")
21
+ fail();
22
+ return {
23
+ kind: "attested",
24
+ verdict: current.verdict,
25
+ at: current.at,
26
+ ...(current.summary === undefined
27
+ ? {}
28
+ : { summary: typeof current.summary === "string" ? current.summary : fail() }),
29
+ };
30
+ }
31
+ function decodeGateCurrent(value) {
32
+ const current = object(value);
33
+ if (current.kind === "missing") {
34
+ if (Object.keys(current).length !== 1)
35
+ fail();
36
+ return { kind: "missing" };
37
+ }
38
+ if (current.kind === "stale") {
39
+ if (current.priorVerdict !== "satisfied" && current.priorVerdict !== "unsatisfied")
40
+ fail();
41
+ if (Object.keys(current).length !== 2)
42
+ fail();
43
+ return { kind: "stale", priorVerdict: current.priorVerdict };
44
+ }
45
+ return decodeAttestedGateCurrent(current);
46
+ }
47
+ export function decodeGateReport(value) {
48
+ const record = object(value);
49
+ if (!("gate" in record) || !("current" in record))
50
+ fail();
51
+ for (const key of Object.keys(record))
52
+ if (key !== "gate" && key !== "current")
53
+ fail();
54
+ let decodedGate;
55
+ try {
56
+ if (typeof record.gate !== "string")
57
+ fail();
58
+ decodedGate = gate(record.gate);
59
+ }
60
+ catch {
61
+ fail();
62
+ }
63
+ return { gate: decodedGate, current: decodeGateCurrent(record.current) };
64
+ }
2
65
  /** Resolve the latest testimony for each requested gate and current subject. */
3
66
  export function latestCurrentAttestations(state, requested) {
4
67
  const attestations = new Map();