@astrosheep/keiyaku 4.6.2 → 4.6.4

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 (57) hide show
  1. package/README.md +93 -45
  2. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +4 -3
  3. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/references/automation.md +2 -2
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-babysit/SKILL.md +1 -1
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +4 -3
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +34 -22
  7. package/build/src/akuma/providers/pi/events.js +3 -2
  8. package/build/src/body/render.d.ts +9 -1
  9. package/build/src/body/render.js +11 -0
  10. package/build/src/cli/commands/akuma-grammar.js +12 -3
  11. package/build/src/cli/commands/akuma.js +6 -0
  12. package/build/src/cli/commands/contract-grammar.js +7 -0
  13. package/build/src/cli/commands/contract-help.d.ts +1 -1
  14. package/build/src/cli/commands/contract-help.js +21 -9
  15. package/build/src/cli/commands/contract.js +3 -1
  16. package/build/src/cli/commands/task-grammar.js +9 -7
  17. package/build/src/cli/parse.js +5 -0
  18. package/build/src/cli/render/akuma-activity.d.ts +26 -6
  19. package/build/src/cli/render/akuma-activity.js +128 -118
  20. package/build/src/cli/render/akuma-tool.js +1 -1
  21. package/build/src/cli/render/audit.js +4 -6
  22. package/build/src/cli/render/catalog.js +7 -4
  23. package/build/src/cli/render/contract-history.js +8 -18
  24. package/build/src/cli/render/contract.js +5 -10
  25. package/build/src/cli/render/kanshi-akuma.js +1 -1
  26. package/build/src/cli/render/kanshi.js +24 -30
  27. package/build/src/cli/render/receipt.d.ts +1 -2
  28. package/build/src/cli/render/receipt.js +1 -6
  29. package/build/src/cli/render/refusal.js +2 -1
  30. package/build/src/cli/render/task.js +2 -1
  31. package/build/src/core/facts/codec.js +0 -6
  32. package/build/src/core/facts/fold.js +0 -7
  33. package/build/src/core/facts/types.d.ts +1 -4
  34. package/build/src/core/verbs/deliver.js +2 -14
  35. package/build/src/library/bind.d.ts +20 -20
  36. package/build/src/library/bind.js +2 -1
  37. package/build/src/library/contract-composition.d.ts +5 -2
  38. package/build/src/library/contract-composition.js +5 -3
  39. package/build/src/library/contract-creation.d.ts +7 -1
  40. package/build/src/library/contract-creation.js +19 -9
  41. package/build/src/library/contract-execution.d.ts +1 -0
  42. package/build/src/library/contract-execution.js +12 -4
  43. package/build/src/library/contract-settings.d.ts +2 -2
  44. package/build/src/library/contract-settings.js +21 -12
  45. package/build/src/library/outcome.d.ts +476 -652
  46. package/build/src/library/refusal.d.ts +2 -2
  47. package/build/src/protocol/bind.d.ts +1 -1
  48. package/build/src/protocol/deliver.js +1 -1
  49. package/build/src/protocol/execution-observation.d.ts +0 -8
  50. package/build/src/protocol/intent.js +2 -2
  51. package/build/src/protocol/operations.d.ts +1 -1
  52. package/build/src/protocol/read/status.d.ts +1 -1
  53. package/build/src/protocol/read/status.js +0 -2
  54. package/build/src/runtime/proc/windows-launch.exe +0 -0
  55. package/build/src/world.d.ts +1 -0
  56. package/build/src/world.js +14 -0
  57. package/package.json +3 -3
package/README.md CHANGED
@@ -52,7 +52,7 @@ The CLI parses a predicate; the evaluator never sees a raw shell string.
52
52
  Query reads only Task facts. No Contract. No Akuma.
53
53
 
54
54
  ## Verification
55
- ```bash
55
+ ```bash timeout=2m
56
56
  npm test
57
57
  ```
58
58
  ```
@@ -60,60 +60,60 @@ npm test
60
60
  ## What a deal looks like
61
61
 
62
62
  ```bash
63
- keiyaku bind - < keiyaku.md # terms written; an isolated worktree appears
64
- keiyaku call worker - # a worker goes in
65
- keiyaku deliver # tendered; Verification runs; gates judge
66
- keiyaku review --satisfied # attested; main moves with a commit receipt
63
+ keiyaku bind - < contract.md # terms written; an isolated worktree appears
64
+ keiyaku call worker - # a worker goes in
65
+ keiyaku deliver <contract> # delivered; Verification runs; gates judge
66
+ keiyaku review <contract> --satisfied --summary - # attested; main moves with a commit receipt
67
67
  ```
68
68
 
69
69
  ## What the journal records
70
70
 
71
- One real deal, pulled from this repo's own journal:
71
+ One real deal, read back with `keiyaku history`:
72
72
 
73
73
  ```text
74
- $ keiyaku audit kei/add-acp-provider-and-grok-build-profile
75
-
76
- accepted audit kei/add-acp-provider-and-grok-build-profile head=0508f7eb9cb738bcab06060b13ff1a96d36e3754
77
- report {"reworks":1,"reviews":2,"timeline":[
78
- {"kind":"bind","at":"2026-08-14T08:18:36.224Z"},
79
- {"kind":"deliver","at":"2026-08-14T13:02:08.339Z"},
80
- {"kind":"attestation","gate":"reviewed","verdict":"unsatisfied","summary":
81
- "Implementation review found no blocking code issues; authenticated official
82
- Grok smoke remains unavailable because this host has no grok binary."},
83
- {"kind":"attestation","gate":"verified","verdict":"satisfied","summary":"[1 bash exit 0] …"}]}
74
+ history kei/ship-typed-task-query-b186 · accepted · 9 entries
75
+
76
+ 2026-10-04
77
+
78
+ 16:53 bound to targetless @ 3c5d8c0 · gate review
79
+ 16:53 delivered 210eeed · ✓ verification · 10 lines
80
+ 16:53 × review · 6 lines
81
+ 16:53 delivered aebef87 · ✓ verification · 10 lines
82
+ 16:53 ✓ review · 6 lines
83
+ 16:53 accepted
84
84
  ```
85
85
 
86
- The review said no — with a reason, in bytes, on the journal. The deal
87
- did not land until the gates had current evidence.
86
+ The review said no, and the deal did not land. The verdict and its reason
87
+ stay on the journal as bytes — `keiyaku history --json` reads them:
88
+
89
+ ```json
90
+ {
91
+ "kind": "attestation",
92
+ "contract": "kei/ship-typed-task-query-b186",
93
+ "data": {
94
+ "gate": "reviewed",
95
+ "verdict": "unsatisfied",
96
+ "summary": "Query evaluator still reaches for Contract facts; reads are not Task-owned."
97
+ }
98
+ }
99
+ ```
88
100
 
89
101
  ## The board
90
102
 
91
- `keiyaku status` is the one screen. A slice of this repository, right now:
103
+ `keiyaku status` is the one screen. Two Contracts and two Tasks in flight:
92
104
 
93
105
  ```text
94
- kanshi ─ 7 keiyaku · 18 akuma · 286 task ─ /Users/astrosheep/Developer/keiyaku-v4 main 9cfdca6017633e51827b9b2eba3c76a7fe08e05f
95
-
96
- keiyaku 7
97
- ! kei/add-acp-provider-and-grok-build-profile tendered
98
- worktree · integration c5cafef6 · -> refs/heads/main
99
- × reviewed
100
- ⧗ kei/align-task-cli-truth-promises waiting
101
- ! reviewed
102
- held by task/align-task-cli-truth-promises-for-ready-compose
103
-
104
- akuma 18
105
- ● aku/expert-akuma/a7aafc9e running
106
- alias @process-custody-lead
107
- keiyaku kei/make-process-custody-capability-honest (active)
108
- ○ aku/design-akuma/cc53ef08 asleep
109
- alias @timeline-design
110
- ! aku/grok/95d90b7d stranded
111
- alias @acp-provider-impl
112
-
113
- task 8 · 5 ready · 2 held
114
- ● task/align-task-cli-truth-promises-for-ready-compose in_progress
115
- Align Task CLI truth promises for ready compose and world absence
116
- P0 · keiyaku kei/align-task-cli-truth-promises (active)
106
+ CONTRACTS // recent
107
+
108
+ ⧗ kei/ship-typed-task-query-c898 · 1s · Ship typed Task query
109
+ [✓] delivery [ ] review
110
+ ⧗ kei/tighten-receipt-vocabulary-4a70 · 18s · Tighten receipt vocabulary
111
+ [ ] delivery [ ] review
112
+
113
+ TASKS // recent
114
+
115
+ ○ task/regenerate-readme-from-real-010b · ready · P1 · Regenerate README from real output
116
+ ○ task/align-task-cli-truth-promises-3af5 · ready · P0 · Align Task CLI truth promises
117
117
  ```
118
118
 
119
119
  Marks accelerate scanning; the words carry the state.
@@ -122,9 +122,8 @@ Marks accelerate scanning; the words carry the state.
122
122
 
123
123
  ```markdown
124
124
  ---
125
- provider: claude-agent-sdk
126
- model: claude-sonnet-4-5
127
- access: write
125
+ provider: pi
126
+ model: kimi-coding/k3-256k
128
127
  description: Repository implementation agent
129
128
  ---
130
129
  Make scoped changes and run relevant tests.
@@ -132,6 +131,55 @@ Make scoped changes and run relevant tests.
132
131
 
133
132
  One Markdown file, one worker. `keiyaku call worker` summons it.
134
133
 
134
+ ## Configuration
135
+
136
+ Settings are JSON at two addresses: `~/.keiyaku/settings.json` for the user,
137
+ `.keiyaku/settings.json` in the project. A project record
138
+ wholly shadows the same-name user record. There is no write command; edit the
139
+ files directly and inspect the merged, provenance-annotated view with
140
+ `keiyaku settings`.
141
+
142
+ Named gate groups provide reusable gate sets. Bind and amend select them with
143
+ `--gates`; the `default` group applies when binding with `--gates` omitted.
144
+ Amending without `--gates` keeps the existing gates:
145
+
146
+ ```json
147
+ {
148
+ "gates": {
149
+ "default": { "kind": "bundle", "gates": ["reviewed"] },
150
+ "strict": { "kind": "bundle", "gates": ["reviewed", "verified"] }
151
+ }
152
+ }
153
+ ```
154
+
155
+ Selections accept the built-in names `reviewed` and `verified`, or configured
156
+ group names. Unknown names are rejected with the known names listed. Custom gates
157
+ must be declared inside a configured group; they stay unsatisfied until a producer
158
+ attests them.
159
+
160
+ Worktree hooks run commands when a Contract's managed worktree is created or
161
+ destroyed — dependency installs are the usual suspect:
162
+
163
+ ```json
164
+ {
165
+ "worktree": {
166
+ "create": [
167
+ { "name": "install", "argv": ["npm", "ci", "--ignore-scripts", "--prefer-offline"], "timeoutMs": 300000 }
168
+ ],
169
+ "destroy": [
170
+ { "name": "teardown", "argv": ["docker", "compose", "down", "-v"], "timeoutMs": 60000 }
171
+ ]
172
+ }
173
+ }
174
+ ```
175
+
176
+ Hooks run as one ordered phase inside the worktree and must be replay-safe: a
177
+ retry reruns the phase from its beginning. Create hooks prepare the worktree;
178
+ destroy hooks release external resources the worktree deletion alone cannot.
179
+ A failing hook retains the worktree
180
+ and reports lag; it never abandons the Contract. `keiyaku settings --help`
181
+ lists every recognized namespace.
182
+
135
183
  ## Install
136
184
 
137
185
  ```bash
@@ -1,8 +1,9 @@
1
1
  ---
2
2
  name: keiyaku-akuma
3
3
  description: >-
4
- Calling an Akuma: delegate work to a new or existing worker, then observe,
5
- guide, and collect the result.
4
+ Use when the work involves Akuma or Aku, or when you want other hands on
5
+ it: delegating a job, or fanning out research, checks, or other parallel
6
+ work.
6
7
  ---
7
8
 
8
9
  # Keiyaku Akuma
@@ -22,7 +23,7 @@ waiting for the first answer. Use explicit `--wait <duration>` when this
22
23
  invocation should block while observing that initial work. `--wait` only bounds
23
24
  this command; it never stops the Akuma. Read `call --help` for flag syntax,
24
25
  defaults, and action vocabulary.
25
- For seat grants see `keiyaku-workflow`'s table; Reviewers need `--allowed contract.review`.
26
+ To hand a Contract role to an Akuma, use the grant table in `keiyaku-workflow`.
26
27
 
27
28
  The selected Akuma name is a reusable worker configuration, not an individual.
28
29
  Calling it again creates an independent Akuma. Use different names for
@@ -46,8 +46,8 @@ skeptics to break the finalists, and generate replacements only for the
46
46
  identified weaknesses. Bound rounds and cost; "keep improving" is not a useful
47
47
  stop condition.
48
48
 
49
- Independent judgment needs independent contexts. Create separate Akuma for
50
- blind comparisons or adversarial verification. Reuse an identity for follow-up
49
+ Blind comparisons and adversarial checks need separate contexts. Create
50
+ separate Akuma for them. Reuse an identity for follow-up
51
51
  investigation, not as a supposedly fresh judge of its own earlier answer.
52
52
 
53
53
  ## Public Entry And A Single Structured Turn
@@ -52,7 +52,7 @@ does not move without you.
52
52
  | Observed shape | First response |
53
53
  | --- | --- |
54
54
  | Silent wedge: initial answer empty and worktree clean | Inspect status and history, then send one steering `tell`. If the worker remains unresponsive, `kill` and call a new Akuma; an empty answer alone is not proof the Contract failed. |
55
- | Verdict record refused with `not-allowed` | Read the worker's analysis in history; call a new Reviewer with the grant from `keiyaku-workflow`, then have it inspect current work and record a fresh verdict. The refused request wrote no Contract testimony. |
55
+ | Verdict record refused with `not-allowed` | Read the worker's analysis in history. Record the verdict yourself if you did not deliver the candidate, or call a new Reviewer with the grant from `keiyaku-workflow` and have it inspect current work and record a fresh verdict. The refused request wrote no Contract testimony. |
56
56
  | Review stale after changed candidate tender | Normal content-currentness result. Give the same Reviewer the changed diff and current terms with `tell`; have it re-examine and record current testimony. A target move alone does not stale review. |
57
57
  | Target moved under the appointed worktree | Have the Deliverer inspect target movement and merge the target branch (main when selected) in that worktree when required; resolve conflicts there, then audit again. Do not resolve against a stale integration preview. |
58
58
  | Typed refusal names its rule | Fix the named input or physical condition, then retry from a fresh observation. Do not repeat the same request blindly. |
@@ -183,9 +183,10 @@ keiyaku -C <repo> bind --gates "" - < CONTRACT.md
183
183
  keiyaku -C <repo> bind --gates reviewed - < CONTRACT.md
184
184
  ```
185
185
 
186
- Omitting `--gates` uses `gates.default`, or `reviewed` when no default bundle
186
+ Omitting `--gates` uses `gates.default`, or `reviewed` when no default group
187
187
  exists. `--gates ""` selects no gates. Gates are named acceptance obligations,
188
- not work assignments.
188
+ not work assignments. Named gate groups and worktree hooks live in Settings;
189
+ see the README Configuration section or `keiyaku settings --help`.
189
190
 
190
191
  ## 3. After Binding
191
192
 
@@ -204,7 +205,7 @@ waiting, the stated prerequisites remain; do not bind a duplicate Contract.
204
205
 
205
206
  The holder now owns the lifecycle:
206
207
 
207
- 1. Start the work directly or delegate Tasks, Arc chapters, Akumas, or seats.
208
+ 1. Start the work directly or delegate Tasks, Arc chapters, Akumas, or Contract roles.
208
209
  2. Run independent work in parallel and let it converge on the one acceptance
209
210
  boundary.
210
211
  3. Prepare and deliver the candidate.
@@ -38,13 +38,26 @@ to create parallel work.
38
38
  Use `after` only when one `kei` requires another to be accepted first. Ordinary
39
39
  overlap is not a dependency.
40
40
 
41
- ## 3. Hold the `kei`
41
+ ## 3. Roles
42
42
 
43
- Every `kei` has exactly one holder. The holder coordinates the work and owns
44
- acceptance until the Contract is accepted or abandoned. Delegation does not
45
- transfer that responsibility. An Akuma holding the whole loop needs the `kei`, current Arc
46
- when one exists, appointed worktree, holder duties, and actions its commission
47
- will perform. Birth freezes permissions; Contract association grants none.
43
+ A `kei` has three roles. They are responsibilities, not people, and one agent
44
+ may fill more than one of them, with one exception below.
45
+
46
+ - **Holder:** exactly one per `kei`. Owns the fulfillment loop and acceptance
47
+ until the Contract is accepted or abandoned. Handing work to others never
48
+ transfers this responsibility.
49
+ - **Deliverer:** produces the candidate in the appointed worktree and tenders
50
+ it with `deliver`.
51
+ - **Reviewer:** judges the complete Contract against its acceptance conditions
52
+ and records the verdict with `review`.
53
+
54
+ Review is independent when the Reviewer did not deliver the candidate under
55
+ review. That is the whole requirement. The holder may review its own `kei`; a
56
+ fresh Akuma is not what makes review independent. Keiyaku does not check who
57
+ reviews, so keeping the Reviewer apart from the Deliverer is the holder's duty.
58
+
59
+ Any role may be filled directly or handed to an Akuma. Hand one over when other
60
+ hands help the work, not to make review count.
48
61
 
49
62
  ## 4. Lifecycle
50
63
 
@@ -78,31 +91,30 @@ Placement judges declared gates, not the producer's identity. Review does not
78
91
  replace Verification; audit never requests placement. Read the final receipt
79
92
  for stops and reconciliation lag after an admitted act.
80
93
 
81
- ## 5. Seats And Capabilities
94
+ ## 5. Handing A Role To An Akuma
82
95
 
83
96
  This is the one commissioning map. `call --help` owns the exact action
84
- vocabulary and default set. `--contract` records association, not authority.
97
+ vocabulary and default set. The flagship acting directly needs no grant.
98
+ Birth freezes an Akuma's actions; `--contract` records association, not
99
+ authority.
85
100
 
86
- | Seat | Grant and commission | Return |
101
+ | Role | Grant at birth | Return |
87
102
  | --- | --- | --- |
88
- | Holder | Direct flagship commands need no Akuma grant. A delegated holder gets only its birth-time actions; include review when the delegated loop records review. | Own the acceptance loop and inspect receipts. |
89
- | Deliverer | The ordinary baseline includes every Task mutation, Contract audit and delivery, and Akuma management. `keiyaku -C <repo> call worker --alias @deliverer --contract <kei/...> --workdir <appointed-path> -` | Produce and tender the candidate; return the receipt. |
90
- | Reviewer | Add `--allowed contract.review` at birth: `keiyaku -C <repo> call review-akuma --alias @reviewer --contract <kei/...> --workdir <appointed-path> --allowed contract.review -` | Record a verdict and return its receipt. Without the grant, `not-allowed` refuses the mutation; no Contract verdict is recorded. |
103
+ | Holder | The actions the delegated loop will perform; include `contract.review` when this holder will review. Commission with the `kei`, current Arc when one exists, appointed worktree, and holder duties. | Acceptance or abandonment, with receipts. |
104
+ | Deliverer | The ordinary baseline: every Task mutation, Contract audit and delivery, and Akuma management. `keiyaku -C <repo> call worker --alias @deliverer --contract <kei/...> --workdir <appointed-path> -` | The delivery receipt. |
105
+ | Reviewer | `--allowed contract.review`: `keiyaku -C <repo> call review-akuma --alias @reviewer --contract <kei/...> --workdir <appointed-path> --allowed contract.review -` | The verdict receipt. Without the grant, `not-allowed` refuses and no verdict is recorded. |
91
106
 
92
- An Archetype's explicit allowed set replaces the ordinary baseline. Check
107
+ An Archetype's explicit allowed set replaces the ordinary baseline; check
93
108
  `status` for effective actions. `tell` and `fork` cannot add a grant to a live
94
- Akuma; call a fresh one with the grant. Nested calls cannot exceed their direct
95
- parent's actions.
96
-
97
- ## 6. Delegate the Work
109
+ Akuma, so call a fresh one with the grant. Nested calls cannot exceed their
110
+ direct parent's actions.
98
111
 
99
112
  Commission with a clear objective, current Arc when one exists, appointed
100
- worktree, and expected result. A Deliverer produces the candidate. A Reviewer
101
- checks the complete Contract against its acceptance conditions and returns
102
- verdict evidence. Use `keiyaku-akuma` for invocation and observation, and
103
- command help for exact syntax.
113
+ worktree, and expected result. Never ask the Akuma that delivered a candidate
114
+ to review it. Use `keiyaku-akuma` for invocation and observation, and command
115
+ help for exact syntax.
104
116
 
105
- ## 7. Decide What Happens Next
117
+ ## 6. Decide What Happens Next
106
118
 
107
119
  The holder checks candidate, verification, review, prerequisites, and gates.
108
120
 
@@ -164,10 +164,11 @@ function translateMessage(event, state) {
164
164
  state.assistantSeen = true;
165
165
  state.answer = text;
166
166
  }
167
- // Gemini emits an empty text block alongside every tool-use message. It is
167
+ // Some providers emit a blank text block alongside every tool-use message:
168
+ // empty from Gemini, whitespace from openai-responses-style proxies. It is
168
169
  // transport scaffolding, not narration; an ordinary terminal empty answer
169
170
  // remains public evidence.
170
- if (hasTextContent(message.content) && !(message.stopReason === "toolUse" && text.length === 0)) {
171
+ if (hasTextContent(message.content) && !(message.stopReason === "toolUse" && text.trim().length === 0)) {
171
172
  translated.push({ type: "assistant", text });
172
173
  }
173
174
  return translated;
@@ -1,4 +1,12 @@
1
1
  import type { ArcData } from "../core/facts/types.js";
2
- import type { ContractBody } from "./types.js";
2
+ import type { ContractBody, DecodedContractDocument } from "./types.js";
3
3
  export declare function renderContractBody(body: ContractBody, currentArc?: ArcData): string;
4
+ /**
5
+ * Binding persists the canonical rendering of decoded terms, never the caller's
6
+ * literal byte stream: layout-only authorship is normalized at admission so it
7
+ * never reappears as phantom changes in later amendment diffs.
8
+ */
9
+ export declare function decodeCanonicalContractDocument(source: string, options?: Readonly<{
10
+ requireTimeout?: boolean;
11
+ }>): DecodedContractDocument;
4
12
  export declare function renderAmendedContractBody(currentSource: string, body: ContractBody, changedSections: ReadonlySet<string>): string;
@@ -1,4 +1,5 @@
1
1
  import { formatDuration } from "../duration.js";
2
+ import { decodeContractDocument } from "./decode.js";
2
3
  import { parseToAST } from "../markdown/parse.js";
3
4
  import { indexDocument, indexedHeadings, normalizeTitle, rawSlice } from "../markdown/query.js";
4
5
  import { CONTRACT_SECTIONS } from "./shape.js";
@@ -54,6 +55,16 @@ export function renderContractBody(body, currentArc) {
54
55
  .join("\n\n")
55
56
  .concat("\n");
56
57
  }
58
+ /**
59
+ * Binding persists the canonical rendering of decoded terms, never the caller's
60
+ * literal byte stream: layout-only authorship is normalized at admission so it
61
+ * never reappears as phantom changes in later amendment diffs.
62
+ */
63
+ export function decodeCanonicalContractDocument(source, options = {}) {
64
+ const decoded = decodeContractDocument(source, options);
65
+ const rendered = renderContractBody(decoded);
66
+ return rendered === decoded.document.bytes ? decoded : decodeContractDocument(rendered, options);
67
+ }
57
68
  function sections(document) {
58
69
  return indexedHeadings(indexDocument(document), { level: 2 }).filter((node) => node.type === "section");
59
70
  }
@@ -304,8 +304,10 @@ function parseAddressed(action, rawSelectors, flags, output, fail) {
304
304
  return { command: action, akuma, at, output };
305
305
  }
306
306
  function parsePrompted(action, positionals, stdin, fail) {
307
- if (positionals.length < 1 || positionals.length > 2)
308
- fail(`${action} has invalid positional arguments`);
307
+ if (positionals.length === 0)
308
+ fail(action === "call" ? "call requires an Akuma name" : `${action} requires an Akuma selector`);
309
+ if (positionals.length > 2)
310
+ fail(`${action} accepts at most ${action === "call" ? "a name" : "a selector"} and one prompt`);
309
311
  const argument = positionals[1];
310
312
  if (stdin && argument !== undefined)
311
313
  fail(`${action} accepts either a prompt argument or stdin, not both`);
@@ -406,7 +408,14 @@ export function parseAkumaCommand(argv) {
406
408
  : parseAsk(flags, parsed.subject, parsed.prompt, output, fail);
407
409
  }
408
410
  if (spec.arity === "one-or-more" ? positionals.length === 0 : positionals.length !== spec.arity) {
409
- fail(`${action} has invalid positional arguments`);
411
+ if (positionals.length === 0)
412
+ fail(spec.arity === "one-or-more"
413
+ ? `${action} requires at least one Akuma selector`
414
+ : `${action} requires an Akuma selector`);
415
+ else
416
+ fail(spec.arity === 1
417
+ ? `${action} accepts one Akuma selector`
418
+ : `${action} accepts exactly ${spec.arity} Akuma selectors`);
410
419
  }
411
420
  if (stdin)
412
421
  fail(`${action} reads no stdin`);
@@ -9,6 +9,7 @@ import { executionChannel } from "../../akuma/requests.js";
9
9
  import { settings } from "../../settings.js";
10
10
  import { AkumaWorldScopeError } from "../../index.js";
11
11
  import { akumasWithExecution } from "../../library/akumas.js";
12
+ import { World } from "../../world.js";
12
13
  import { contractFromInput } from "../selectors.js";
13
14
  import { ActivityDriver } from "../activity.js";
14
15
  import { displayContext, resultContext, writeJson, writeStderr, writeStdout } from "../streams.js";
@@ -67,6 +68,11 @@ async function inputInitiator(input) {
67
68
  catch { }
68
69
  if (initiator === undefined)
69
70
  return {};
71
+ // Plugins coordinate within an established World; a `call` may run where the
72
+ // marker does not exist yet, and activating there would only fail noisily.
73
+ // The initiator still travels with the request either way.
74
+ if (!(await World.established(input.path)))
75
+ return { initiator };
70
76
  try {
71
77
  await emitInitiatingPluginSignal({
72
78
  world: input.path,
@@ -22,6 +22,9 @@ function optionalFlag(flags, name) {
22
22
  function refuse(command, message) {
23
23
  throw new CliUsageError(message, commandGuide(command, CONTRACT_COMMAND_SPECS[command].usage));
24
24
  }
25
+ // The CLI restates the literal gate-word grammar its help prints; the corpus
26
+ // pin in cli-parse.test.ts keeps this shape honest against the owner's gateWord.
27
+ const GATE_NAME_PATTERN = /^[a-z][a-z0-9-]{0,63}$/u;
25
28
  function parseGateNames(parts, command) {
26
29
  const value = optionalFlag(parts.flags, "gates");
27
30
  if (value === undefined)
@@ -32,6 +35,10 @@ function parseGateNames(parts, command) {
32
35
  if (names.some((name) => name.length === 0)) {
33
36
  refuse(command, '--gates requires comma-separated names or "" to clear gates');
34
37
  }
38
+ const invalid = names.find((name) => !GATE_NAME_PATTERN.test(name));
39
+ if (invalid !== undefined) {
40
+ refuse(command, `--gates name must match ^[a-z][a-z0-9-]{0,63}$: ${invalid}`);
41
+ }
35
42
  return names;
36
43
  }
37
44
  function parseBind(parts) {
@@ -106,7 +106,7 @@ export declare const CONTRACT_COMMAND_SPECS: {
106
106
  readonly json: "boolean";
107
107
  };
108
108
  readonly usage: "ls task[/] [--limit <count>]\nls kei[/] [--limit <count>]\nls aku[/] [--limit <count>]\nls aku/<name>[/] [--limit <count>]\nls \"aku/<name>/*\" [--limit <count>]\nls \"aku/*/*\" [--limit <count>]";
109
- readonly purpose: "List Tasks, Contracts, or Akumas.";
109
+ readonly purpose: "List tasks (ls task), contracts (ls kei), or akumas (ls aku).";
110
110
  readonly details: string;
111
111
  };
112
112
  readonly audit: {
@@ -15,11 +15,15 @@ export const CONTRACT_COMMAND_SPECS = {
15
15
  usage: "bind [--task <task/...>] [--target <ref>] [--after <kei/...> ...] [--gates <name,...>] [--actor <actor>] - | bind --fork-of <kei/...> [--target <ref>] [--actor <actor>]",
16
16
  purpose: "Create a Contract from stdin Markdown or copy an existing Contract as a starting point.",
17
17
  details: [
18
- "--gates <name,...> accepts gate words and configured bundle names together.",
19
- "A matching bundle expands; otherwise the name is a literal gate. Duplicates",
20
- "are removed in first-seen order. Gate words match ^[a-z][a-z0-9-]{0,63}$.",
18
+ "Gates are acceptance obligations checked at placement: reviewed awaits a",
19
+ "satisfied review, verified awaits a passing Verification run.",
20
+ "",
21
+ "--gates <name,...> selects reviewed, verified, or named gate groups from Settings",
22
+ "(keiyaku settings). A group expands into its gates; unknown names are rejected",
23
+ "with the known names listed. Custom gates must be declared in a configured group.",
24
+ "Duplicates are removed in first-seen order. Names match ^[a-z][a-z0-9-]{0,63}$.",
21
25
  "Omitting --gates selects gates.default, or reviewed when no default exists.",
22
- '--gates "" explicitly binds without gates; --gates reviewed needs no bundle.',
26
+ '--gates "" explicitly binds without gates; --gates reviewed needs no configuration.',
23
27
  "",
24
28
  "Binding appoints one managed worktree for the Contract and the receipt",
25
29
  "reports its path; the holder and delegates work there. When appointment",
@@ -55,9 +59,14 @@ export const CONTRACT_COMMAND_SPECS = {
55
59
  usage: "amend [<contract>|@<contract>] [--after <kei/...> ... | --clear-after] [--gates <name,...>] [--actor <actor>] [-]",
56
60
  purpose: "Change a Contract's document, prerequisites, or acceptance gates.",
57
61
  details: [
58
- "--gates <name,...> replaces gates using gate words and configured bundle names.",
59
- "A matching bundle expands; otherwise the name is a literal gate. Duplicates",
60
- "are removed in first-seen order. Gate words match ^[a-z][a-z0-9-]{0,63}$.",
62
+ "Gates are acceptance obligations checked at placement: reviewed awaits a",
63
+ "satisfied review, verified awaits a passing Verification run.",
64
+ "",
65
+ "--gates <name,...> replaces gates using reviewed, verified, or named gate groups",
66
+ "from Settings (keiyaku settings). A group expands into its gates; unknown names",
67
+ "are rejected with the known names listed. Custom gates must be declared in a",
68
+ "configured group. Duplicates are removed in first-seen order.",
69
+ "Names match ^[a-z][a-z0-9-]{0,63}$.",
61
70
  'Omitting --gates leaves gates unchanged; --gates "" clears them.',
62
71
  "",
63
72
  " ## Context|Objective|Design|Region|Criteria|Verification|<extension> (replace, or add new extension)",
@@ -88,6 +97,7 @@ export const CONTRACT_COMMAND_SPECS = {
88
97
  "Delivery requests integration. If prerequisites and gates pass, it integrates now and accepts the Contract; otherwise the candidate remains waiting.",
89
98
  "Only changed candidate work invalidates earlier review. If Verification did not finish, repeating delivery resumes that candidate; --overwrite replaces it.",
90
99
  "Delivery never counts as an independent review verdict.",
100
+ "Content identity names the change's content hash, never a commit; it survives rebase and Git cannot look it up.",
91
101
  "",
92
102
  " --materialize-conflict Write the observed integration conflict into the worktree as an uncommitted merge.",
93
103
  " With --include-dirty, current non-ignored changes are preserved first.",
@@ -147,8 +157,9 @@ export const CONTRACT_COMMAND_SPECS = {
147
157
  stdin: "none",
148
158
  flags: { limit: "value", json: "boolean" },
149
159
  usage: 'ls task[/] [--limit <count>]\nls kei[/] [--limit <count>]\nls aku[/] [--limit <count>]\nls aku/<name>[/] [--limit <count>]\nls "aku/<name>/*" [--limit <count>]\nls "aku/*/*" [--limit <count>]',
150
- purpose: "List Tasks, Contracts, or Akumas.",
160
+ purpose: "List tasks (ls task), contracts (ls kei), or akumas (ls aku).",
151
161
  details: [
162
+ "ls kei lists active Contracts; accepted and abandoned ones leave the list, and their record stays in history and Git.",
152
163
  "ls aku/ lists the callable Akuma-name catalog; ls aku/<name> lists the living Akumas under one name.",
153
164
  'ls "aku/<name>/*" is the same living set in glob form; ls "aku/*/*" lists every living Akuma.',
154
165
  ].join("\n"),
@@ -162,6 +173,7 @@ export const CONTRACT_COMMAND_SPECS = {
162
173
  details: [
163
174
  "--include-dirty checks all non-ignored worktree changes, staged or not.",
164
175
  "--show-diff includes the proposed candidate diff when one can be prepared.",
176
+ "Content identity names the change's content hash, never a commit; it survives rebase and Git cannot look it up.",
165
177
  "Progress appears on stderr; stdout contains one final result.",
166
178
  ].join("\n"),
167
179
  },
@@ -210,7 +222,7 @@ export function renderContractHelp(command) {
210
222
  "command; edit the files directly.",
211
223
  "",
212
224
  "Recognized settings:",
213
- " gates gate bundles selected by bind and amend",
225
+ " gates named gate groups selected by bind and amend",
214
226
  " worktree commands run when worktrees are created or destroyed",
215
227
  " git.requireBranchesToBeUpToDate whether deliver and audit require up-to-date target branches",
216
228
  " providers the available Akuma and how each is run",
@@ -148,7 +148,9 @@ async function contractLibrary(command, execution, configuration, runtime) {
148
148
  return composeContractLibrary(execution, createKeiyakuHandle, {
149
149
  ...(configuration === undefined ? {} : { settings: configuration }),
150
150
  ...(actor === undefined ? {} : { actor }),
151
- });
151
+ },
152
+ // The CLI owns this default: an omitted Markdown --target names the invocation branch.
153
+ { kind: "current-branch" });
152
154
  }
153
155
  async function runBind(command, input, library) {
154
156
  const { coordinates, runtime } = input;
@@ -204,14 +204,12 @@ export function renderTaskHelp(action) {
204
204
  const spec = TASK_COMMAND_SPECS[action];
205
205
  return `${spec.purpose}\n\n${renderTaskUsage(action)}${spec.details === undefined ? "" : `\n\n${spec.details}`}`;
206
206
  }
207
+ const nameWidth = Math.max(...Object.keys(TASK_COMMAND_SPECS).map((name) => name.length));
207
208
  return [
208
209
  "usage keiyaku task <command> ...",
210
+ " keiyaku task <command> --help shows that command's complete usage",
209
211
  "",
210
- "commands:",
211
- ...Object.values(TASK_COMMAND_SPECS).flatMap((spec) => spec.usage
212
- .split("\n")
213
- .map((line) => ` ${line}`)
214
- .concat(` ${spec.purpose}`)),
212
+ ...Object.entries(TASK_COMMAND_SPECS).map(([name, spec]) => ` ${name.padEnd(nameWidth)} ${spec.purpose}`),
215
213
  ].join("\n");
216
214
  }
217
215
  function renderTaskUsage(action) {
@@ -387,8 +385,12 @@ function validateListSelector(selector, fail) {
387
385
  }
388
386
  function validateTaskScan(action, scanned, fail) {
389
387
  const spec = TASK_COMMAND_SPECS[action], { positionals, flags, stdin } = scanned;
390
- if (positionals.length < spec.arity[0] || positionals.length > spec.arity[1])
391
- fail(`task ${action} has invalid positional arguments`);
388
+ if (positionals.length < spec.arity[0])
389
+ fail(spec.arity[1] === 1 ? `task ${action} requires a TaskId` : `task ${action} requires at least one TaskId`);
390
+ if (positionals.length > spec.arity[1])
391
+ fail(spec.arity[1] === 0
392
+ ? `task ${action} accepts no positional arguments`
393
+ : `task ${action} accepts at most ${spec.arity[1]} positional argument${spec.arity[1] === 1 ? "" : "s"}`);
392
394
  if (action === "add" && (stdin === "document") === (positionals.length === 1))
393
395
  fail("task add requires either TITLE or '-' input");
394
396
  if (action === "add" &&
@@ -294,6 +294,11 @@ export function parseArgv(argv) {
294
294
  const help = helpCoordinate(invocation.commandArgv);
295
295
  if (help !== null)
296
296
  return { help };
297
+ // A bare invocation asks for orientation: the root help index, not a refusal.
298
+ if (invocation.commandArgv.length === 0)
299
+ return { help: { kind: "root" } };
300
+ if (invocation.commandArgv.length === 1 && invocation.commandArgv[0] === "task")
301
+ return { help: { kind: "task" } };
297
302
  if (invocation.commandArgv.length === 1 && invocation.commandArgv[0] === "--version")
298
303
  return { version: true };
299
304
  const task = invocation.commandArgv[0] === "task" ? parseTaskCommand(invocation.commandArgv.slice(1)) : undefined;