@astrosheep/keiyaku 2.9.8 → 2.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/README.md +50 -182
  2. package/build/.tsbuildinfo +1 -1
  3. package/build/agents/harness/control-types.js +18 -1
  4. package/build/agents/harness/index.js +2 -0
  5. package/build/agents/harness/outcome-error.js +81 -0
  6. package/build/agents/harness/projection.js +10 -7
  7. package/build/agents/harness/router.js +5 -1
  8. package/build/agents/providers/claude-agent-sdk/adapter.js +18 -4
  9. package/build/agents/providers/claude-agent-sdk/registration.js +11 -1
  10. package/build/agents/providers/claude-agent-sdk/session.js +19 -5
  11. package/build/agents/providers/codex-app-server/adapter.js +11 -6
  12. package/build/agents/providers/codex-app-server/events.js +67 -6
  13. package/build/agents/providers/codex-app-server/registration.js +11 -1
  14. package/build/agents/providers/opencode-sdk/adapter.js +8 -1
  15. package/build/agents/providers/opencode-sdk/events.js +24 -2
  16. package/build/agents/providers/opencode-sdk/registration.js +11 -1
  17. package/build/agents/providers/pi/checkpoint.js +9 -1
  18. package/build/agents/providers/pi/events.js +2 -0
  19. package/build/agents/providers/pi/registration.js +14 -1
  20. package/build/cli/commands/akuma/akuma/handler.js +1 -1
  21. package/build/cli/commands/akuma/akuma/meta.js +3 -3
  22. package/build/cli/commands/akuma/catalog.js +2 -2
  23. package/build/cli/commands/akuma/{view → show}/handler.js +5 -5
  24. package/build/cli/commands/akuma/{view → show}/meta.js +3 -3
  25. package/build/cli/commands/akuma.js +1 -1
  26. package/build/cli/commands/contract/amend/handler.js +5 -4
  27. package/build/cli/commands/contract/arc/handler.js +5 -4
  28. package/build/cli/commands/contract/audit/handler.js +2 -2
  29. package/build/cli/commands/contract/bind/handler.js +12 -3
  30. package/build/cli/commands/contract/bind/meta.js +45 -15
  31. package/build/cli/commands/contract/forfeit/handler.js +9 -5
  32. package/build/cli/commands/contract/log/handler.js +2 -2
  33. package/build/cli/commands/contract/petition/handler.js +34 -7
  34. package/build/cli/commands/contract/renew/handler.js +7 -7
  35. package/build/cli/commands/contract/renew/meta.js +3 -3
  36. package/build/cli/commands/metadata.js +10 -6
  37. package/build/cli/commands/projection/call/meta.js +1 -1
  38. package/build/cli/commands/projection/catalog.js +2 -0
  39. package/build/cli/commands/projection/history/handler.js +21 -0
  40. package/build/cli/commands/projection/history/meta.js +9 -0
  41. package/build/cli/commands/projection/shared.js +3 -4
  42. package/build/cli/commands/projection/status/handler.js +3 -3
  43. package/build/cli/commands/projection/wait/handler.js +20 -11
  44. package/build/cli/commands/task/add/handler.js +14 -5
  45. package/build/cli/commands/task/add/meta.js +2 -2
  46. package/build/cli/commands/task/catalog.js +4 -0
  47. package/build/cli/commands/task/done/handler.js +1 -4
  48. package/build/cli/commands/task/done/meta.js +1 -1
  49. package/build/cli/commands/task/drop/meta.js +1 -1
  50. package/build/cli/commands/task/hold/handler.js +3 -0
  51. package/build/cli/commands/task/hold/meta.js +8 -0
  52. package/build/cli/commands/task/log/handler.js +1 -1
  53. package/build/cli/commands/task/log/meta.js +2 -2
  54. package/build/cli/commands/task/resume/handler.js +3 -0
  55. package/build/cli/commands/task/resume/meta.js +8 -0
  56. package/build/cli/commands/task/shared.js +39 -14
  57. package/build/cli/commands/task/show/handler.js +1 -1
  58. package/build/cli/commands/task/show/meta.js +1 -1
  59. package/build/cli/commands/task/start/meta.js +1 -1
  60. package/build/cli/commands/task/stop/handler.js +1 -4
  61. package/build/cli/commands/task/stop/meta.js +1 -1
  62. package/build/cli/commands/task/update/handler.js +11 -3
  63. package/build/cli/commands/task/update/meta.js +3 -3
  64. package/build/cli/completion.js +3 -2
  65. package/build/cli/flags.js +51 -14
  66. package/build/cli/help.js +4 -1
  67. package/build/cli/index.js +40 -25
  68. package/build/cli/parse-flags.js +40 -7
  69. package/build/cli/parse-metadata.js +5 -2
  70. package/build/cli/parse-selectors.js +2 -3
  71. package/build/cli/parse.js +40 -6
  72. package/build/cli/projection-address.js +12 -10
  73. package/build/cli/render/amendment-warning.js +8 -0
  74. package/build/cli/render/arc.js +6 -2
  75. package/build/cli/render/audit.js +400 -81
  76. package/build/cli/render/kanshi.js +242 -79
  77. package/build/cli/render/lifecycle.js +56 -0
  78. package/build/cli/render/misc.js +24 -28
  79. package/build/cli/render/petition.js +5 -0
  80. package/build/cli/render/projection-activity.js +65 -29
  81. package/build/cli/render/projection-history.js +64 -0
  82. package/build/cli/render/shared.js +23 -9
  83. package/build/cli/render/status.js +13 -35
  84. package/build/cli/render/task.js +51 -26
  85. package/build/cli/render/terminal-failure.js +3 -1
  86. package/build/cli/render/wait.js +45 -69
  87. package/build/cli/subagent-guard.js +1 -1
  88. package/build/cli/types.js +1 -1
  89. package/build/config/env-keys.js +3 -1
  90. package/build/config/env.js +24 -1
  91. package/build/config/settings/schema.js +3 -1
  92. package/build/core/address-carrier.js +89 -0
  93. package/build/core/addressing.js +255 -196
  94. package/build/core/amend.js +115 -38
  95. package/build/core/amendment-criteria.js +17 -0
  96. package/build/core/amendment-warning.js +10 -0
  97. package/build/core/arc.js +184 -83
  98. package/build/core/audit/candidate.js +110 -9
  99. package/build/core/audit/coordinates.js +120 -65
  100. package/build/core/audit/evidence.js +19 -23
  101. package/build/core/audit/facade.js +34 -11
  102. package/build/core/audit/report.js +5 -5
  103. package/build/core/bind-reconciliation.js +390 -0
  104. package/build/core/bind-workspace.js +12 -20
  105. package/build/core/bind.js +242 -273
  106. package/build/core/call/call.js +1 -0
  107. package/build/core/call/context.js +46 -71
  108. package/build/core/call/prompt.js +6 -3
  109. package/build/core/context.js +49 -35
  110. package/build/core/contract-carrier-runtime.js +408 -0
  111. package/build/core/contract-carrier.js +270 -0
  112. package/build/core/contract-view.js +33 -27
  113. package/build/core/contract.js +5 -1
  114. package/build/core/derived-replay.js +207 -0
  115. package/build/core/draft.js +125 -57
  116. package/build/core/forfeit.js +113 -56
  117. package/build/core/identifier-slug.js +63 -13
  118. package/build/core/ids.js +4 -4
  119. package/build/core/lifecycle-history.js +35 -0
  120. package/build/core/lifecycle-recovery.js +117 -0
  121. package/build/core/lifecycle-runner.js +377 -0
  122. package/build/core/log.js +37 -11
  123. package/build/core/outcome-base.js +181 -0
  124. package/build/core/process-group.js +139 -0
  125. package/build/core/projection/generation/database.js +82 -7
  126. package/build/core/projection/generation/model.js +9 -36
  127. package/build/core/projection/generation/projection-generation-continuation.js +10 -22
  128. package/build/core/projection/generation/projection-generation-execution.js +13 -6
  129. package/build/core/projection/index.js +5 -4
  130. package/build/core/projection/projection-activity.js +29 -8
  131. package/build/core/projection/projection-alias.js +23 -10
  132. package/build/core/projection/projection-core.js +21 -7
  133. package/build/core/projection/projection-execution-observer.js +91 -39
  134. package/build/core/projection/projection-history.js +474 -0
  135. package/build/core/projection/projection-runner-lock.js +12 -0
  136. package/build/core/projection/projection-status-observation.js +365 -0
  137. package/build/core/projection/projection-status.js +35 -337
  138. package/build/core/projection/projection-terminal-failure.js +13 -8
  139. package/build/core/projection/projection-wait.js +7 -2
  140. package/build/core/projection/tell/launch-store.js +244 -0
  141. package/build/core/projection/tell/model.js +3 -3
  142. package/build/core/projection/tell/store.js +70 -118
  143. package/build/core/registry.js +22 -87
  144. package/build/core/renew-build.js +424 -0
  145. package/build/core/renew-plan.js +239 -0
  146. package/build/core/renew-rewrite.js +77 -0
  147. package/build/core/renew.js +295 -520
  148. package/build/core/repository-ledger/accepted-fold-read.js +19 -0
  149. package/build/core/repository-ledger/accepted-tail.js +116 -0
  150. package/build/core/repository-ledger/admission.js +38 -0
  151. package/build/core/repository-ledger/authoritative-transaction.js +1 -0
  152. package/build/core/repository-ledger/claim-evidence.js +147 -0
  153. package/build/core/repository-ledger/codec.js +297 -0
  154. package/build/core/repository-ledger/current-state-store.js +853 -0
  155. package/build/core/repository-ledger/fold-repository.js +375 -0
  156. package/build/core/repository-ledger/fold.js +248 -0
  157. package/build/core/repository-ledger/git-object-store.js +243 -0
  158. package/build/core/repository-ledger/history-read.js +15 -0
  159. package/build/core/repository-ledger/incremental-read.js +153 -0
  160. package/build/core/repository-ledger/index.js +15 -0
  161. package/build/core/repository-ledger/inventory-plan.js +75 -0
  162. package/build/core/repository-ledger/inventory.js +310 -0
  163. package/build/core/repository-ledger/publication-admission.js +125 -0
  164. package/build/core/repository-ledger/publication.js +353 -0
  165. package/build/core/repository-ledger/read-model.js +469 -0
  166. package/build/core/repository-ledger/transaction-membership.js +78 -0
  167. package/build/core/repository-ledger/traversal.js +404 -0
  168. package/build/core/repository-ledger/write-transaction.js +339 -0
  169. package/build/core/seal.js +168 -161
  170. package/build/core/settlement/claim-delivery.js +42 -215
  171. package/build/core/settlement/claim.js +235 -153
  172. package/build/core/settlement/index.js +4 -4
  173. package/build/core/settlement/petition-claim-gates.js +52 -33
  174. package/build/core/settlement/petition-forfeit.js +12 -5
  175. package/build/core/settlement/petition-head-guard.js +8 -5
  176. package/build/core/settlement/petition-preview.js +68 -56
  177. package/build/core/settlement/petition-window.js +50 -0
  178. package/build/core/settlement/petition.js +252 -131
  179. package/build/core/settlement/queue-read-model.js +72 -95
  180. package/build/core/settlement/queue-seat-allocation.js +35 -126
  181. package/build/core/settlement/queue.js +1 -1
  182. package/build/core/settlement/settlement.js +172 -80
  183. package/build/core/settlement/verdict.js +112 -66
  184. package/build/core/settlement/verification-coordination.js +305 -0
  185. package/build/core/settlement/verification-supervisor.js +275 -0
  186. package/build/core/settlement/verification.js +398 -31
  187. package/build/core/status/board.js +125 -127
  188. package/build/core/status/lifecycle.js +7 -80
  189. package/build/core/structured-query.js +23 -0
  190. package/build/core/target-ref.js +1 -1
  191. package/build/core/task/board.js +59 -24
  192. package/build/core/task/commands.js +181 -33
  193. package/build/core/task/coordinate.js +56 -0
  194. package/build/core/task/document.js +25 -9
  195. package/build/core/task/index.js +7 -4
  196. package/build/core/task/query.js +17 -27
  197. package/build/core/task/settlement-git.js +164 -179
  198. package/build/core/task/settlement-policy.js +15 -11
  199. package/build/core/task/source-board.js +31 -13
  200. package/build/core/task/task-bind-preparation.js +193 -0
  201. package/build/core/task/task-contract.js +102 -193
  202. package/build/core/task/task-file-transaction.js +171 -0
  203. package/build/core/task/task-git-runtime.js +76 -14
  204. package/build/core/task/task-git-store.js +68 -169
  205. package/build/core/task/task-store-repository.js +13 -6
  206. package/build/core/task/task-worktree.js +6 -33
  207. package/build/core/task/task.js +1 -1
  208. package/build/core/task/tree.js +3 -3
  209. package/build/core/task/validation.js +4 -3
  210. package/build/core/transcripts.js +93 -1
  211. package/build/core/worktree-bootstrap.js +44 -36
  212. package/build/core/worktree-path.js +210 -77
  213. package/build/flow-error.js +7 -7
  214. package/build/generated/version.js +2 -2
  215. package/build/git/core.js +76 -13
  216. package/build/git/refs.js +168 -46
  217. package/build/git/streaming-batch.js +73 -13
  218. package/package.json +1 -1
  219. package/skills/keiyaku/SKILL.md +1 -1
  220. package/skills/keiyaku-akuma/SKILL.md +3 -3
  221. package/skills/keiyaku-task/SKILL.md +4 -2
  222. package/skills/keiyaku-workflow/SKILL.md +88 -203
  223. package/build/core/entry.js +0 -305
  224. package/build/core/ledger-batch.js +0 -194
  225. package/build/core/ledger.js +0 -84
  226. package/build/core/projection/leash.js +0 -87
  227. package/build/core/ref-log.js +0 -119
  228. package/build/core/renew-session.js +0 -128
  229. package/build/core/status/ledger-batch.js +0 -1
  230. package/build/core/status/reconciliation.js +0 -137
@@ -0,0 +1,21 @@
1
+ import { readProjectionHistoryAddress, readProjectionHistory, selectProjectionHistory, } from "../../../../core/projection/index.js";
2
+ import { FlowError } from "../../../../flow-error.js";
3
+ import { projectionHistoryJson, renderProjectionHistoryDetail, renderProjectionHistoryList } from "../../../render/projection-history.js";
4
+ import { textResponse } from "../../../render/shared.js";
5
+ import { projectDirectory } from "../../shared.js";
6
+ export const handler = async (_stdin, flags, _signal, positional) => {
7
+ const cwd = await projectDirectory(flags, "history");
8
+ const [address] = positional;
9
+ if (!address) {
10
+ const runs = selectProjectionHistory(await readProjectionHistory(cwd), {
11
+ filters: flags.taskFilters,
12
+ sort: flags.taskSort,
13
+ nowMs: Date.now(),
14
+ });
15
+ return textResponse(flags.json ? projectionHistoryJson(runs) : renderProjectionHistoryList(runs));
16
+ }
17
+ const detail = await readProjectionHistoryAddress(cwd, address);
18
+ if (!detail)
19
+ throw new FlowError("PROJECTION_HISTORY_NOT_FOUND", `history has no retained run for '${address}'`);
20
+ return textResponse(flags.json ? projectionHistoryJson(detail) : renderProjectionHistoryDetail(detail));
21
+ };
@@ -0,0 +1,9 @@
1
+ export const meta = {
2
+ command: "history",
3
+ summary: "Discover retained Akuma runs or inspect one run",
4
+ usage: ["keiyaku history [PROJECTION|ALIAS|ARTIFACT] [-C|--cwd DIR] [--filter EXPR] [--sort KEYS] [--json]"],
5
+ stdin: { mode: "argument" },
6
+ flags: ["cwd", "repo", "filter", "sort", "json"],
7
+ purpose: "Read retained projection history and the public response-file path without reading response content.",
8
+ completionRank: 15,
9
+ };
@@ -1,5 +1,4 @@
1
1
  import { resolveContractAddress, withResolvedAddress, } from "../../../core/addressing.js";
2
- import { projectionDir } from "../../../core/projection/index.js";
3
2
  import { resolveProjectionAddress, } from "../../projection-address.js";
4
3
  import { projectDirectory, repositoryDirectory } from "../shared.js";
5
4
  /** Resolve cwd + optional contract address, then run the verb under that carrier. */
@@ -13,16 +12,16 @@ export async function runProjectionCommand(flags, verb, operation) {
13
12
  const run = () => operation({ cwd, address });
14
13
  return address ? await withResolvedAddress(address, run) : await run();
15
14
  }
16
- export function resolveProjectionCommandAddress(ctx, projectionAddress, verb) {
15
+ export function resolveProjectionCommandAddress(ctx, projectionAddress, verb, coordinate) {
17
16
  return resolveProjectionAddress({
18
17
  cwd: ctx.cwd,
19
18
  address: projectionAddress,
20
19
  scope: ctx.address,
20
+ coordinate,
21
21
  verb,
22
22
  });
23
23
  }
24
24
  export async function resolveProjectionCommandTarget(ctx, projectionAddress, verb) {
25
25
  const projection = await resolveProjectionCommandAddress(ctx, projectionAddress, verb);
26
- const directory = await projectionDir(ctx.cwd, projection.id);
27
- return { ...projection, directory };
26
+ return projection;
28
27
  }
@@ -3,7 +3,7 @@ import { normalizeTargetRef } from "../../../../core/target-ref.js";
3
3
  import { isProjectionId } from "../../../../core/projection/index.js";
4
4
  import { FlowError } from "../../../../flow-error.js";
5
5
  import { renderProjectionFilteredStatus, renderStatusBoard } from "../../../render/status.js";
6
- import { textResponse } from "../../../render/shared.js";
6
+ import { coreTextResponse } from "../../../render/shared.js";
7
7
  import { projectDirectory, repositoryDirectory } from "../../shared.js";
8
8
  export const handler = async (_stdin, flags, _signal, positional) => {
9
9
  const [projectionId] = positional;
@@ -21,6 +21,6 @@ export const handler = async (_stdin, flags, _signal, positional) => {
21
21
  ...(projectionId === undefined ? {} : { projectionId }),
22
22
  });
23
23
  if (projectionId === undefined)
24
- return textResponse(renderStatusBoard(board));
25
- return textResponse(renderProjectionFilteredStatus(board, projectionId));
24
+ return coreTextResponse(renderStatusBoard(board), board);
25
+ return coreTextResponse(renderProjectionFilteredStatus(board, projectionId), board);
26
26
  };
@@ -1,5 +1,4 @@
1
- import { projectionDir } from "../../../../core/projection/index.js";
2
- import { snapshotCommissionProjectionIds, waitForProjection, waitForProjectionSet } from "../../../../core/projection/index.js";
1
+ import { projectionDirAtCoordinate, resolveProjectionCoordinate, snapshotCommissionProjectionIdsAtCoordinate, waitForProjection, waitForProjectionSet, } from "../../../../core/projection/index.js";
3
2
  import { FlowError } from "../../../../flow-error.js";
4
3
  import { prependAddressReceipt } from "../../../render/address.js";
5
4
  import { buildProjectionWaitSetResponse, buildWaitResponse } from "../../../render/wait.js";
@@ -15,30 +14,40 @@ export const handler = async (_stdin, flags, signal, positional) => runProjectio
15
14
  signal,
16
15
  }), { alias: projection.alias }), ctx.address, { projectionId: projection.id, projectionAlias: projection.alias });
17
16
  }
18
- const selected = [];
17
+ const selected = new Map();
18
+ let coordinate;
19
+ if (ctx.address) {
20
+ coordinate = {
21
+ physicalRoot: ctx.address.repo.stableRoot,
22
+ authority: { kind: "repository", stableRoot: ctx.address.repo.stableRoot },
23
+ };
24
+ }
25
+ else {
26
+ coordinate = await resolveProjectionCoordinate(ctx.cwd);
27
+ }
19
28
  if (positional.length === 0) {
20
29
  if (ctx.address?.binding.kind === "commission") {
21
- selected.push(...await snapshotCommissionProjectionIds(ctx.cwd, ctx.address.binding.commissionId));
30
+ for (const id of await snapshotCommissionProjectionIdsAtCoordinate(coordinate, ctx.address.binding.commissionId)) {
31
+ selected.set(id, { id, projectionDirectory: projectionDirAtCoordinate(coordinate, id) });
32
+ }
22
33
  }
23
34
  }
24
35
  else {
25
36
  // Resolve in argument order so multiple bad coordinates have one stable failure.
26
37
  for (const projectionAddress of positional) {
27
- selected.push((await resolveProjectionCommandAddress(ctx, projectionAddress, "wait")).id);
38
+ const projection = await resolveProjectionCommandAddress(ctx, projectionAddress, "wait", coordinate);
39
+ selected.set(projection.id, { id: projection.id, projectionDirectory: projection.directory });
28
40
  }
29
41
  }
30
- const ids = [...new Set(selected)].sort();
31
- if (ids.length === 0) {
42
+ const projections = [...selected.values()].sort((left, right) => left.id.localeCompare(right.id));
43
+ if (projections.length === 0) {
32
44
  throw new FlowError("EMPTY_PARAM", "wait commission snapshot contains no projections");
33
45
  }
34
46
  if (!flags.any && !flags.all) {
35
47
  throw new FlowError("EMPTY_PARAM", WAIT_SET_SELECTION_ERROR);
36
48
  }
37
49
  return prependAddressReceipt(buildProjectionWaitSetResponse(await waitForProjectionSet({
38
- projections: await Promise.all(ids.map(async (id) => ({
39
- id,
40
- projectionDirectory: await projectionDir(ctx.cwd, id),
41
- }))),
50
+ projections,
42
51
  selection: flags.any ? "any" : "all",
43
52
  timeoutMs: flags.timeoutMs,
44
53
  signal,
@@ -1,13 +1,22 @@
1
- import { createTask, resolveTaskCreator, } from "../../../../core/task/index.js";
2
- import { parseTaskAddInput, runTaskMutationCommand } from "../shared.js";
3
- export const handler = async (stdin, flags) => {
1
+ import { createTask, openTaskMutationContext, resolveTaskCreator, } from "../../../../core/task/index.js";
2
+ import { renderTaskMutationRow } from "../../../render/task.js";
3
+ import { textResponse } from "../../../render/shared.js";
4
+ import { effectiveDirectory } from "../../shared.js";
5
+ import { parseTaskAddInput } from "../shared.js";
6
+ export const handler = async (stdin, flags, signal) => {
4
7
  const input = parseTaskAddInput(stdin);
5
- return runTaskMutationCommand(flags, async (repository, cwd) => createTask(repository, {
8
+ const cwd = effectiveDirectory(flags);
9
+ const context = await openTaskMutationContext(cwd, signal);
10
+ const task = await createTask(context.repository, {
6
11
  ...input,
12
+ namespace: context.namespace,
7
13
  creator: await resolveTaskCreator(cwd),
8
14
  ...(flags.taskPri === undefined ? {} : { pri: flags.taskPri }),
9
15
  needs: flags.taskNeeds ?? [],
10
16
  parent: flags.taskParent ?? null,
11
17
  from: flags.taskFrom ?? [],
12
- }, { now: () => new Date() }));
18
+ supersedes: flags.taskSupersedes ?? [],
19
+ relatesTo: flags.taskRelatesTo ?? [],
20
+ }, { now: () => new Date() });
21
+ return textResponse(renderTaskMutationRow(task));
13
22
  };
@@ -1,4 +1,4 @@
1
- const TASK_ADD_USAGE_PREFIX = "keiyaku task add [-C|--cwd DIR] [--needs ID]... [--parent ID] [--from ID]... [--pri 0-3]";
1
+ const TASK_ADD_USAGE_PREFIX = "keiyaku task add [-C|--cwd DIR] [--needs COORDINATE]... [--parent COORDINATE] [--from COORDINATE]... [--supersedes COORDINATE]... [--relates-to COORDINATE]... [--pri 0-3]";
2
2
  export const meta = {
3
3
  command: "task add",
4
4
  summary: "Add a task",
@@ -10,7 +10,7 @@ export const meta = {
10
10
  mode: "argument",
11
11
  selector: { kind: "positional", index: 0, required: true, rejectBlank: true },
12
12
  },
13
- flags: ["cwd", "needs", "parent", "from", "pri"],
13
+ flags: ["cwd", "needs", "parent", "from", "supersedes", "relates-to", "pri"],
14
14
  purpose: "Create a lightweight task from a title and optional body.",
15
15
  notes: [
16
16
  "<BODY> and literal - are mutually exclusive body selectors. Use literal - with stdin for multiline prose.",
@@ -5,6 +5,8 @@ import { meta as lsMeta } from "./ls/meta.js";
5
5
  import { meta as logMeta } from "./log/meta.js";
6
6
  import { meta as startMeta } from "./start/meta.js";
7
7
  import { meta as stopMeta } from "./stop/meta.js";
8
+ import { meta as holdMeta } from "./hold/meta.js";
9
+ import { meta as resumeMeta } from "./resume/meta.js";
8
10
  import { meta as doneMeta } from "./done/meta.js";
9
11
  import { meta as dropMeta } from "./drop/meta.js";
10
12
  import { meta as updateMeta } from "./update/meta.js";
@@ -17,6 +19,8 @@ export const taskCatalog = [
17
19
  { meta: logMeta, load: () => import("./log/handler.js") },
18
20
  { meta: startMeta, load: () => import("./start/handler.js") },
19
21
  { meta: stopMeta, load: () => import("./stop/handler.js") },
22
+ { meta: holdMeta, load: () => import("./hold/handler.js") },
23
+ { meta: resumeMeta, load: () => import("./resume/handler.js") },
20
24
  { meta: doneMeta, load: () => import("./done/handler.js") },
21
25
  { meta: dropMeta, load: () => import("./drop/handler.js") },
22
26
  { meta: updateMeta, load: () => import("./update/handler.js") },
@@ -1,10 +1,7 @@
1
1
  import { doneTask } from "../../../../core/task/index.js";
2
- import { effectiveDirectory } from "../../shared.js";
3
- import { mutationSelectionMessage, rejectBareContractTaskState, runTaskMutationCommand } from "../shared.js";
2
+ import { mutationSelectionMessage, runTaskMutationCommand } from "../shared.js";
4
3
  export const handler = async (_stdin, flags, _signal, positional) => {
5
4
  try {
6
- if (positional[0] === undefined)
7
- await rejectBareContractTaskState(effectiveDirectory(flags), "done");
8
5
  return await runTaskMutationCommand(flags, (repository) => doneTask(repository, positional[0]));
9
6
  }
10
7
  catch (error) {
@@ -1,7 +1,7 @@
1
1
  export const meta = {
2
2
  command: "task done",
3
3
  summary: "Complete a task",
4
- usage: ["keiyaku task done [-C|--cwd DIR] [ID]"],
4
+ usage: ["keiyaku task done [-C|--cwd DIR] [COORDINATE]"],
5
5
  stdin: { mode: "argument" },
6
6
  flags: ["cwd"],
7
7
  purpose: "Complete an open or in-progress task.",
@@ -1,7 +1,7 @@
1
1
  export const meta = {
2
2
  command: "task drop",
3
3
  summary: "Drop a task",
4
- usage: ["keiyaku task drop [-C|--cwd DIR] ID"],
4
+ usage: ["keiyaku task drop [-C|--cwd DIR] COORDINATE"],
5
5
  stdin: { mode: "argument" },
6
6
  flags: ["cwd"],
7
7
  purpose: "Abandon an open or in-progress task.",
@@ -0,0 +1,3 @@
1
+ import { holdTask } from "../../../../core/task/index.js";
2
+ import { runTaskMutationCommand } from "../shared.js";
3
+ export const handler = async (_stdin, flags, _signal, positional) => runTaskMutationCommand(flags, (repository) => holdTask(repository, positional[0]));
@@ -0,0 +1,8 @@
1
+ export const meta = {
2
+ command: "task hold",
3
+ summary: "Put a task on hold",
4
+ usage: ["keiyaku task hold [-C|--cwd DIR] COORDINATE"],
5
+ stdin: { mode: "argument" },
6
+ flags: ["cwd"],
7
+ purpose: "Move an open or in-progress task to on hold.",
8
+ };
@@ -7,6 +7,6 @@ export const handler = async (_stdin, flags, _signal, positional) => withTaskCom
7
7
  const task = board.byId.get(resolveTaskReadId(board, positional[0]));
8
8
  if (!task)
9
9
  throw new FlowError("INTERNAL_STATE", "resolved task disappeared from the board");
10
- const rows = await readTaskHistory(cwd, task.id);
10
+ const rows = await readTaskHistory(cwd, task.coordinate);
11
11
  return textResponse(rows.map(({ timestamp, actor, subject }) => `${timestamp}\t${actor}\t${subject}`).join("\n"));
12
12
  });
@@ -1,8 +1,8 @@
1
1
  export const meta = {
2
2
  command: "task log",
3
3
  summary: "Show one task's Git history",
4
- usage: ["keiyaku task log [-C|--cwd DIR] ID"],
4
+ usage: ["keiyaku task log [-C|--cwd DIR] COORDINATE"],
5
5
  stdin: { mode: "argument" },
6
6
  flags: ["cwd"],
7
- purpose: "Read the current hub branch's task micro-commit history.",
7
+ purpose: "Read the current checkout branch's task micro-commit history.",
8
8
  };
@@ -0,0 +1,3 @@
1
+ import { resumeTask } from "../../../../core/task/index.js";
2
+ import { runTaskMutationCommand } from "../shared.js";
3
+ export const handler = async (_stdin, flags, _signal, positional) => runTaskMutationCommand(flags, (repository) => resumeTask(repository, positional[0]));
@@ -0,0 +1,8 @@
1
+ export const meta = {
2
+ command: "task resume",
3
+ summary: "Resume a task on hold",
4
+ usage: ["keiyaku task resume [-C|--cwd DIR] COORDINATE"],
5
+ stdin: { mode: "argument" },
6
+ flags: ["cwd"],
7
+ purpose: "Return an on-hold task to open.",
8
+ };
@@ -1,6 +1,5 @@
1
- import * as path from "node:path";
2
1
  import { FlowError } from "../../../flow-error.js";
3
- import { isTaskId, openTaskCommandRepository, parseTaskDocument, readTaskDoctor, taskCommandContractId, } from "../../../core/task/index.js";
2
+ import { openTaskCommandRepository, parseTaskDocument, readTaskDoctor, taskCoordinateFromSourcePath, } from "../../../core/task/index.js";
4
3
  import { deriveTaskDisplay, renderTaskList, renderTaskMutationRow, } from "../../render/task.js";
5
4
  import { textResponse } from "../../render/shared.js";
6
5
  import { effectiveDirectory } from "../shared.js";
@@ -46,7 +45,7 @@ function taskDocumentsById(sources) {
46
45
  for (const source of sources) {
47
46
  try {
48
47
  const document = parseTaskDocument(source);
49
- documents.set(document.id, document);
48
+ documents.set(taskCoordinateFromSourcePath(source.path).coordinate, document);
50
49
  }
51
50
  catch {
52
51
  // Invalid documents are reported from board diseases.
@@ -59,9 +58,13 @@ function renderTaskDiseaseRows(diseases, documents) {
59
58
  const renderedCycles = new Set();
60
59
  for (const disease of diseases) {
61
60
  if (disease.code === "invalid_document") {
62
- const name = path.basename(disease.path).replace(/\.md$/, "");
63
- if (isTaskId(name))
64
- rows.push(`! ${name} invalid front matter — quarantined`);
61
+ try {
62
+ const coordinate = taskCoordinateFromSourcePath(disease.path).coordinate;
63
+ rows.push(`! ${coordinate} invalid front matter — quarantined`);
64
+ }
65
+ catch {
66
+ // A malformed non-coordinate path has no copyable task identity to render.
67
+ }
65
68
  continue;
66
69
  }
67
70
  if (disease.code === "needs_cycle" && disease.id && disease.relatedIds) {
@@ -90,6 +93,28 @@ function renderTaskDiseaseRows(diseases, documents) {
90
93
  rows.push(`! cycle detected: ${cycle.join(" → ")}`);
91
94
  continue;
92
95
  }
96
+ if (disease.code === "supersedes_cycle" && disease.id && disease.relatedIds) {
97
+ const cycleKey = `supersedes\0${disease.relatedIds.join("\0")}`;
98
+ if (renderedCycles.has(cycleKey))
99
+ continue;
100
+ renderedCycles.add(cycleKey);
101
+ const members = new Set(disease.relatedIds);
102
+ const start = [...members].sort()[0];
103
+ const cycle = [start];
104
+ let current = start;
105
+ for (let count = 0; count <= members.size; count += 1) {
106
+ const next = documents.get(current)?.supersedes.filter((id) => members.has(id)).sort()[0];
107
+ if (!next)
108
+ break;
109
+ cycle.push(next);
110
+ if (next === start)
111
+ break;
112
+ current = next;
113
+ }
114
+ if (cycle.at(-1) === start)
115
+ rows.push(`! supersedes cycle detected: ${cycle.join(" → ")}`);
116
+ continue;
117
+ }
93
118
  if (disease.code === "missing_reference" && disease.id && disease.relatedIds) {
94
119
  const document = documents.get(disease.id);
95
120
  if (!document)
@@ -99,6 +124,10 @@ function renderTaskDiseaseRows(diseases, documents) {
99
124
  rows.push(`! ${disease.id} needs missing task ${related}`);
100
125
  if (document.parent === related)
101
126
  rows.push(`? ${disease.id} parent ${related} does not exist`);
127
+ if (document.supersedes.includes(related))
128
+ rows.push(`! ${disease.id} supersedes missing task ${related}`);
129
+ if (document.relatesTo.includes(related))
130
+ rows.push(`! ${disease.id} relates to missing task ${related}`);
102
131
  }
103
132
  }
104
133
  }
@@ -108,11 +137,13 @@ export async function renderTaskDoctor(cwd) {
108
137
  const result = await readTaskDoctor(cwd);
109
138
  const board = result.board;
110
139
  const tasks = board.tasks;
111
- const counts = { inProgress: 0, ready: 0, blocked: 0, done: 0, dropped: 0, bound: 0 };
140
+ const counts = { inProgress: 0, onHold: 0, ready: 0, blocked: 0, done: 0, dropped: 0, bound: 0 };
112
141
  for (const task of tasks) {
113
142
  const display = deriveTaskDisplay(task);
114
143
  if (display === "in_progress")
115
144
  counts.inProgress += 1;
145
+ else if (display === "on_hold")
146
+ counts.onHold += 1;
116
147
  else if (display === "ready")
117
148
  counts.ready += 1;
118
149
  else if (display === "blocked")
@@ -126,7 +157,7 @@ export async function renderTaskDoctor(cwd) {
126
157
  }
127
158
  const lines = [
128
159
  `tasks · ${tasks.length} total`,
129
- `${counts.inProgress} in_progress · ${counts.ready} ready · ${counts.blocked} blocked · ${counts.done} done · ${counts.dropped} dropped · ${counts.bound} bound`,
160
+ `${counts.inProgress} in_progress · ${counts.onHold} on hold · ${counts.ready} ready · ${counts.blocked} blocked · ${counts.done} done · ${counts.dropped} dropped · ${counts.bound} bound`,
130
161
  ...renderTaskDiseaseRows(board.diseases, taskDocumentsById(result.sources)),
131
162
  ...result.orphans.map(({ taskId, contractId }) => `! ${taskId} points at contract ${contractId} which has no ledger entry — rolling back`),
132
163
  ];
@@ -134,9 +165,3 @@ export async function renderTaskDoctor(cwd) {
134
165
  lines.push("× board lock currently held by a live writer");
135
166
  return lines.join("\n");
136
167
  }
137
- export async function rejectBareContractTaskState(cwd, verb) {
138
- const contractId = await taskCommandContractId(cwd);
139
- if (contractId) {
140
- throw new FlowError("UNSUPPORTED_POLICY", `task ${verb} is not available in a contract worktree; task state follows the contract (${contractId})`);
141
- }
142
- }
@@ -8,6 +8,6 @@ export const handler = async (_stdin, flags, _signal, positional) => withTaskCom
8
8
  const task = board.byId.get(resolveTaskReadId(board, positional[0]));
9
9
  if (!task)
10
10
  throw new FlowError("INTERNAL_STATE", "resolved task disappeared from the board");
11
- const tree = buildTaskTree(board, task.id);
11
+ const tree = buildTaskTree(board, task.coordinate);
12
12
  return textResponse(flags.json ? taskJsonText(renderTaskShowJson(task, tree)) : renderTaskShow(task, tree));
13
13
  });
@@ -1,7 +1,7 @@
1
1
  export const meta = {
2
2
  command: "task show",
3
3
  summary: "Show one task and its children tree",
4
- usage: ["keiyaku task show [-C|--cwd DIR] ID [--json]"],
4
+ usage: ["keiyaku task show [-C|--cwd DIR] COORDINATE [--json]"],
5
5
  stdin: { mode: "argument" },
6
6
  flags: ["cwd", "json"],
7
7
  purpose: "Read one task and every descendant linked by parent edges.",
@@ -1,7 +1,7 @@
1
1
  export const meta = {
2
2
  command: "task start",
3
3
  summary: "Start a task",
4
- usage: ["keiyaku task start [-C|--cwd DIR] ID"],
4
+ usage: ["keiyaku task start [-C|--cwd DIR] COORDINATE"],
5
5
  stdin: { mode: "argument" },
6
6
  flags: ["cwd"],
7
7
  purpose: "Move an open task to in_progress.",
@@ -1,10 +1,7 @@
1
1
  import { stopTask } from "../../../../core/task/index.js";
2
- import { effectiveDirectory } from "../../shared.js";
3
- import { mutationSelectionMessage, rejectBareContractTaskState, runTaskMutationCommand } from "../shared.js";
2
+ import { mutationSelectionMessage, runTaskMutationCommand } from "../shared.js";
4
3
  export const handler = async (_stdin, flags, _signal, positional) => {
5
4
  try {
6
- if (positional[0] === undefined)
7
- await rejectBareContractTaskState(effectiveDirectory(flags), "stop");
8
5
  return await runTaskMutationCommand(flags, (repository) => stopTask(repository, positional[0]));
9
6
  }
10
7
  catch (error) {
@@ -1,7 +1,7 @@
1
1
  export const meta = {
2
2
  command: "task stop",
3
3
  summary: "Stop a task",
4
- usage: ["keiyaku task stop [-C|--cwd DIR] [ID]"],
4
+ usage: ["keiyaku task stop [-C|--cwd DIR] [COORDINATE]"],
5
5
  stdin: { mode: "argument" },
6
6
  flags: ["cwd"],
7
7
  purpose: "Return an in-progress task to open.",
@@ -1,13 +1,21 @@
1
- import { updateTask } from "../../../../core/task/index.js";
2
- import { runTaskMutationCommand } from "../shared.js";
1
+ import { updateTaskWithReceipt } from "../../../../core/task/index.js";
2
+ import { renderTaskUpdate, renderTaskUpdateJson } from "../../../render/task.js";
3
+ import { textResponse } from "../../../render/shared.js";
4
+ import { withTaskCommandRepository } from "../shared.js";
3
5
  export const handler = async (_stdin, flags, _signal, positional) => {
4
- return runTaskMutationCommand(flags, (repository) => updateTask(repository, positional[0], {
6
+ const result = await withTaskCommandRepository(flags, (repository) => updateTaskWithReceipt(repository, positional[0], {
5
7
  ...(flags.taskTitle === undefined ? {} : { title: flags.taskTitle }),
6
8
  ...(flags.taskBody === undefined ? {} : { body: flags.taskBody }),
9
+ ...(flags.taskAppend === undefined ? {} : { appendBody: flags.taskAppend }),
7
10
  ...(flags.taskPri === undefined ? {} : { pri: flags.taskPri }),
8
11
  ...(flags.taskNeeds === undefined ? {} : { addNeeds: flags.taskNeeds }),
9
12
  ...(flags.taskDropNeeds === undefined ? {} : { dropNeeds: flags.taskDropNeeds }),
10
13
  ...(flags.taskParent !== undefined ? { parent: flags.taskParent } : flags.taskNoParent ? { parent: null } : {}),
11
14
  ...(flags.taskFrom === undefined ? {} : { from: flags.taskFrom }),
15
+ ...(flags.taskSupersedes === undefined ? {} : { addSupersedes: flags.taskSupersedes }),
16
+ ...(flags.taskDropSupersedes === undefined ? {} : { dropSupersedes: flags.taskDropSupersedes }),
17
+ ...(flags.taskRelatesTo === undefined ? {} : { addRelatesTo: flags.taskRelatesTo }),
18
+ ...(flags.taskDropRelatesTo === undefined ? {} : { dropRelatesTo: flags.taskDropRelatesTo }),
12
19
  }));
20
+ return textResponse(flags.json ? renderTaskUpdateJson(result) : renderTaskUpdate(result));
13
21
  };
@@ -1,11 +1,11 @@
1
1
  export const meta = {
2
2
  command: "task update",
3
3
  summary: "Update task facts",
4
- usage: ["keiyaku task update [-C|--cwd DIR] ID [--title TEXT] [--body TEXT|-] [--pri 0-3] [--needs ID] [--drop-needs ID] [--parent ID|--no-parent] [--from ID]"],
4
+ usage: ["keiyaku task update [-C|--cwd DIR] COORDINATE [--title TEXT] [--body TEXT|-|--append TEXT|-] [--pri 0-3] [--needs COORDINATE] [--drop-needs COORDINATE] [--parent COORDINATE|--no-parent] [--from COORDINATE] [--supersedes COORDINATE] [--drop-supersedes COORDINATE] [--relates-to COORDINATE] [--drop-relates-to COORDINATE] [--json]"],
5
5
  stdin: {
6
6
  mode: "argument",
7
- selector: { kind: "flag", name: "taskBody", required: false, rejectBlank: false },
7
+ selector: { kind: "flag", names: ["taskBody", "taskAppend"], required: false, rejectBlank: false },
8
8
  },
9
- flags: ["cwd", "title", "body", "pri", "needs", "drop-needs", "parent", "no-parent", "from"],
9
+ flags: ["cwd", "title", "body", "append", "pri", "needs", "drop-needs", "parent", "no-parent", "from", "supersedes", "drop-supersedes", "relates-to", "drop-relates-to", "json"],
10
10
  purpose: "Change mutable task facts.",
11
11
  };
@@ -1,7 +1,7 @@
1
1
  import { assertSettingsKnobUsable } from "../config/settings/disease.js";
2
2
  import { loadKeiyakuSettings } from "../config/settings/loader.js";
3
3
  import { loadAkumaCatalog } from "../config/akuma-loader.js";
4
- import { listContractIds } from "../core/ledger.js";
4
+ import { readRepositoryLifecycle } from "../core/repository-ledger/read-model.js";
5
5
  import { TASK_FILTER_COMPLETION } from "../core/task/query.js";
6
6
  import { allCommandMetadata, getCommandMetadata } from "./commands/metadata.js";
7
7
  function topLevelCommands() {
@@ -27,7 +27,8 @@ function filterPrefix(items, word) {
27
27
  return items.filter((item) => item.startsWith(word));
28
28
  }
29
29
  async function contractAddressCandidates(cwd, word) {
30
- return filterPrefix((await listContractIds(cwd)).map((id) => `@${id}`), word);
30
+ const read = await readRepositoryLifecycle(cwd);
31
+ return filterPrefix(read.contracts.map((model) => `@${model.contract}`), word);
31
32
  }
32
33
  async function akumaCandidates(cwd, word) {
33
34
  const loaded = await loadKeiyakuSettings(cwd);