wowbagger 0.1.0-alpha.7 → 0.1.0-alpha.8

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 (51) hide show
  1. package/CHANGELOG.md +193 -1
  2. package/README.md +194 -16
  3. package/adapters/claude-code/wowbagger-adapter.json +1 -1
  4. package/adapters/codex/wowbagger-adapter.json +1 -1
  5. package/adapters/opencode/wowbagger-adapter.json +1 -1
  6. package/docs/adapter-contract.md +66 -6
  7. package/docs/host-contract.md +287 -0
  8. package/docs/mutation-contract.md +558 -17
  9. package/docs/work-claim-contract.md +56 -24
  10. package/package.json +14 -1
  11. package/schemas/bare-ready-result.json +27 -0
  12. package/schemas/bare-validation-result.json +23 -0
  13. package/schemas/common.json +470 -0
  14. package/schemas/core-capabilities-response.json +346 -0
  15. package/schemas/core-envelope.json +141 -0
  16. package/schemas/core-inspect-response.json +37 -0
  17. package/schemas/core-inspect-workbench-response.json +340 -0
  18. package/schemas/core-list-error-response.json +242 -0
  19. package/schemas/core-list-query.json +98 -0
  20. package/schemas/core-list-response.json +140 -0
  21. package/schemas/core-report-response.json +379 -0
  22. package/schemas/core-transition-error-response.json +852 -0
  23. package/schemas/core-transition-request.json +47 -0
  24. package/schemas/core-transition-response.json +49 -0
  25. package/schemas/index.json +108 -0
  26. package/schemas/ledger-mutation-refusal.json +288 -0
  27. package/schemas/report-config-v1.json +140 -0
  28. package/schemas/report-config-v2.json +242 -0
  29. package/skills/wowbagger/SKILL.md +151 -38
  30. package/src/adapter/core-probe.js +87 -7
  31. package/src/claim-capabilities.js +4 -5
  32. package/src/claim-coordinator.js +15 -2
  33. package/src/claim-prospective.js +151 -0
  34. package/src/claim-publication.js +49 -9
  35. package/src/claim-sync.js +66 -0
  36. package/src/cli.js +671 -49
  37. package/src/extension-provision.js +33 -0
  38. package/src/git-autocommit.js +36 -6
  39. package/src/git-reconciliation.js +53 -9
  40. package/src/launch.js +52 -0
  41. package/src/lifecycle.js +153 -0
  42. package/src/limits.js +39 -0
  43. package/src/list.js +365 -0
  44. package/src/mutation.js +145 -118
  45. package/src/projection.js +39 -0
  46. package/src/report-graph.js +122 -21
  47. package/src/report-html.js +219 -51
  48. package/src/report-view.js +189 -0
  49. package/src/report.js +145 -15
  50. package/src/validate.js +4 -1
  51. package/src/workbench.js +117 -0
@@ -1082,6 +1082,66 @@ caller-known expected revision with the current revision and state; never
1082
1082
  retry until that observation is reviewed. This distinction prevents both a
1083
1083
  hidden committed mutation and an orphaned duplicate mutation.
1084
1084
 
1085
+ ### 6.2 Response loss and the unknown-outcome envelope
1086
+
1087
+ Losing the response is not the same as observing a failure. A transport between
1088
+ the consumer and the workspace-owning host can drop after the adapter dispatched
1089
+ the core, and a dropped transport observes nothing about the ledger. The adapter
1090
+ therefore reports an unresolved mutation as unresolved, and the consumer answers
1091
+ it with one sequence: Dispatch once, never replay, invalidate the inspected
1092
+ revision, reconnect, then re-read the ledger. Re-reading returns current ledger
1093
+ state only. A later item state never proves that the lost dispatch caused it, so
1094
+ a consumer MUST NOT present it as evidence that the mutation succeeded or failed.
1095
+
1096
+ An unresolved mutation is exactly this envelope. It carries the root
1097
+ `mutation_outcome` member, which no other adapter result has:
1098
+
1099
+ ```json
1100
+ {
1101
+ "ok": false,
1102
+ "adapter_contract_version": 2,
1103
+ "request_id": "transition-recovery-0001",
1104
+ "mutation_outcome": "unknown",
1105
+ "error": {
1106
+ "code": "mutation-outcome-unknown",
1107
+ "message": "The mutation may have been applied; inspect current state before retrying.",
1108
+ "details": {
1109
+ "command": "transition",
1110
+ "item_id": "wb_01Q45X474N28T5CY4GNF6YY4HM",
1111
+ "recovery": {
1112
+ "action": "validate-inspect-and-compare-revision",
1113
+ "expected_revision": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
1114
+ "retry": "never-before-current-state-review"
1115
+ }
1116
+ }
1117
+ },
1118
+ "process": {}
1119
+ }
1120
+ ```
1121
+
1122
+ `details.recovery` is the bounded machine-readable form of the recovery rules
1123
+ above, and it has exactly one shape per command:
1124
+
1125
+ | Command | `action` | Other members | `retry` |
1126
+ |---|---|---|---|
1127
+ | `create` | `inspect-caller-known-id` | `validate_ledger_first: true` | `only-after-item-not-found-and-audited-artifact-recovery` |
1128
+ | `transition`, `patch` | `validate-inspect-and-compare-revision` | `expected_revision`, the caller-known revision or `null` | `never-before-current-state-review` |
1129
+
1130
+ `details` also carries `process_issue` naming the contradicted or missing
1131
+ observation member when the observation itself was the reason the outcome is
1132
+ unknown. `process` is the same bounded observation summary every transport
1133
+ failure carries.
1134
+
1135
+ Exit 4 is never response loss. A `revision-conflict` with the core state
1136
+ `unchanged` is a complete observed refusal: the adapter forwards it verbatim
1137
+ with `core_exit_code: 4` and no `mutation_outcome` member. It invalidates the
1138
+ consumer's inspected revision and requires re-inspection; relabelling it
1139
+ `mutation-outcome-unknown` would turn a proven non-write into an unresolved one.
1140
+
1141
+ The adapter adds no operation ID, no durable outcome store, and no replay
1142
+ endpoint. Once-only dispatch plus an honest unresolved outcome need no
1143
+ correlation identity, and adding one requires a new contract decision.
1144
+
1085
1145
  ## 7. Instruction inputs
1086
1146
 
1087
1147
  The contract does not assume `CLAUDE.md`, `AGENTS.md`, any other filename,
@@ -1297,7 +1357,7 @@ adapter, so its implementation statuses remain `unverified`.
1297
1357
  `node spec/run-adapter-implementation.js` accepts the same fixture directory,
1298
1358
  evaluates transactions through the shipped Claude Code entrypoint and emits the
1299
1359
  same result shape with an evidence platform. Its current native Darwin, Linux,
1300
- and Windows runs are each `pass`: 210 of 210 assertions and 16 of 16 cases
1360
+ and Windows runs are each `pass`: 212 of 212 assertions and 16 of 16 cases
1301
1361
  pass. That native common-vector evidence earns the Claude Code manifest's
1302
1362
  Darwin, Linux, and win32 `supported` declarations.
1303
1363
  Codex, Kimi, and generic adapter implementations remain `unverified`.
@@ -1367,7 +1427,7 @@ unproven, and no count of passing conformance assertions changes that.
1367
1427
 
1368
1428
  This contract does not:
1369
1429
 
1370
- - adopt Wowbagger in PropertyCompass or any other consumer repository;
1430
+ - adopt Wowbagger in a consumer repository;
1371
1431
  - add vendor-specific lifecycle logic to the core;
1372
1432
  - require an MCP server, daemon, hook, slash command, Gastown, or Beads;
1373
1433
  - define work claims, policy ranking, or hidden persistent agent memory;
@@ -1408,14 +1468,14 @@ error registry. It changes only this versioned surface:
1408
1468
  | Path or behavior | Version 2 requirement |
1409
1469
  |---|---|
1410
1470
  | Manifest `adapter_contract_versions` | Exactly `[2]` |
1411
- | Manifest and describe required core contract | Exactly `4` |
1471
+ | Manifest and describe required core contract | Exactly `5` |
1412
1472
  | Describe `selected_adapter_contract_version` | Exactly `2` |
1413
1473
  | Describe `core.commands` order | `capabilities`, `create`, `inspect`, `patch`, `ready`, `transition`, `validate` |
1414
1474
  | Invoke and result `adapter_contract_version` | Exactly `2` |
1415
- | Independently probed core `contract_version` | Exactly `4` |
1475
+ | Independently probed core `contract_version` | Exactly `5` |
1416
1476
 
1417
1477
  The core capability probe adapter contract version 2 requires is core
1418
- contract version 4. It adds exactly
1478
+ contract version 5. It adds exactly
1419
1479
  `operations.patch: {"supported":true,"write_scope":"single-item","cas_scope":"exact-byte-sha256"}`,
1420
1480
  `operations.work_claim.api_version: 2`, and
1421
1481
  `limits.max_item_source_bytes: 8388608` as the first member of
@@ -1507,6 +1567,6 @@ receives exactly the compact refusal
1507
1567
  `{"ok":false,"error":{"code":"unsupported-adapter-contract-version"}}` plus
1508
1568
  one LF. It receives no v2 describe result and no requested core child is
1509
1569
  launched. Conversely, an adapter that requires core contract version 1, 2, or 3 while
1510
- probing a version 4 core observes `contract_version: 4` and refuses the
1570
+ probing a version 5 core observes `contract_version: 5` and refuses the
1511
1571
  pairing as `core-contract-version-mismatch`; neither direction silently
1512
1572
  receives the other version's behavior.
@@ -0,0 +1,287 @@
1
+ # Direct-core host contract
2
+
3
+ Status: core contract version 5. This document is the supported way for a
4
+ non-agent consumer — a UI plugin, an editor extension, a service — to resolve,
5
+ launch, bound, and validate the Wowbagger core on the host that owns the
6
+ workspace.
7
+
8
+ It publishes what Wowbagger owns: the package seam, the process tuple, the
9
+ advertised limits, the response domains, the exit meanings, and the packaged
10
+ JSON Schemas. It states, and does not claim, what the host owns: executable
11
+ discovery, working directory selection, timeout, cancellation, process-tree
12
+ containment, stream caps, consent, and routing. Wowbagger documents these
13
+ requirements and their outcomes; it does not claim these facilities, because it
14
+ cannot enforce them from inside a child process the host started.
15
+
16
+ The behaviour this document describes is defined normatively elsewhere. Command
17
+ semantics live in [the mutation contract](mutation-contract.md), ledger
18
+ semantics in [SPEC.md](../SPEC.md), adapter semantics in [the adapter
19
+ contract](adapter-contract.md), and claim semantics in [the work-claim
20
+ contract](work-claim-contract.md). Nothing here widens any of them.
21
+
22
+ ## 1. Resolving the core
23
+
24
+ The core is a Node.js program. Launching it needs four things and never a
25
+ shell: an absolute Node executable, the absolute `wowbagger.js` the package
26
+ installed, an argument array, and `shell: false`. Neither path is discovered by
27
+ searching a global npm directory, and neither is a platform command shim.
28
+
29
+ Wowbagger requires Node.js 20 or later. The package declares that floor in
30
+ `engines.node`, and the launch seam exports it as `MINIMUM_NODE_MAJOR` for a
31
+ host that resolves its own runtime instead of reusing the one it is running on.
32
+
33
+ ~~~js
34
+ import { resolveCoreLaunch } from 'wowbagger';
35
+
36
+ const launch = resolveCoreLaunch(['capabilities', '--json']);
37
+ // { executable: <absolute Node executable>, args: [<absolute wowbagger.js>, 'capabilities', '--json'], shell: false }
38
+ ~~~
39
+
40
+ `resolveCoreLaunch(argv, { nodeExecutable })` supplies a host-resolved runtime.
41
+ The executable must be an absolute path: a bare name such as `node` is refused
42
+ rather than left for `PATH` to answer, because PATH lookup is the discovery this
43
+ seam exists to remove.
44
+
45
+ A host that wants the script path alone, without importing anything, resolves
46
+ the published subpath:
47
+
48
+ ~~~js
49
+ const script = fileURLToPath(import.meta.resolve('wowbagger/wowbagger.js'));
50
+ ~~~
51
+
52
+ `CORE_SCRIPT_PATH` from the main entry is the same absolute path. Both are
53
+ resolved from the installed package's own location, so they are correct for a
54
+ global install, a local install, a workspace link, and a plain clone alike, and
55
+ neither depends on the caller's working directory.
56
+
57
+ The `bin` mapping still exists and is still the supported way for a person at a
58
+ terminal to run `wowbagger`. It is not the seam for a host: on Windows it is a
59
+ `.cmd` shim that cannot be spawned without a shell at all.
60
+
61
+ ## 2. The process tuple
62
+
63
+ | Element | Value | Owner |
64
+ |---|---|---|
65
+ | executable | the absolute Node executable | host |
66
+ | argv | `[<absolute wowbagger.js>, ...coreArguments]` | Wowbagger publishes the array; the host passes it verbatim |
67
+ | `shell` | `false`, always and never a shell | Wowbagger |
68
+ | working directory | the directory that owns the ledger path | host |
69
+ | stdin | bounded request bytes, then EOF | host |
70
+ | stdout | captured, byte-exact, one JSON object and one LF | host captures; Wowbagger writes |
71
+ | stderr | captured; empty on every `--json` response | host captures |
72
+ | timeout | a wall-clock bound the host enforces | host |
73
+ | cancellation | the host's own signal or kill | host |
74
+ | process-tree containment | a process group or job object the host reaps | host |
75
+ | stream caps | a byte cap on captured stdout and stderr | host |
76
+
77
+ The argument array goes to the process verbatim: never a shell, never a
78
+ concatenated command string, and never a string a shell would re-split. Every
79
+ argument value — a ledger path, an item ID, a date — travels as one array
80
+ element, so no quoting rule exists to get wrong.
81
+
82
+ `--ledger` is a host-chosen path. The core performs no ledger discovery and
83
+ binds no implicit path.
84
+
85
+ ### A missing executable is a host result
86
+
87
+ A missing or unlaunchable executable is a host-level result, not malformed
88
+ Wowbagger JSON. A process that never started emitted nothing, so there is no
89
+ envelope to parse and no exit code to interpret; the host reports its own
90
+ transport failure. Core failures that do run keep their documented response
91
+ domains and exit codes, in section 5.
92
+
93
+ ## 3. Bounded input and output
94
+
95
+ ### Input
96
+
97
+ A mutation request is strict UTF-8 JSON with one top-level object. It reaches
98
+ the core in exactly one of two ways:
99
+
100
+ 1. **Bounded stdin.** `--input -` reads standard input to EOF. The host writes
101
+ the complete request, closes the stream, and enforces its own byte bound
102
+ before it writes.
103
+ 2. **A host-created request file.** `--input <file>` reads a file the host
104
+ created with the permissions it wants. A secure host-created temporary file
105
+ is the alternative when a runtime cannot deliver stdin.
106
+
107
+ Never shell source, and never inline unbounded argv JSON. An invocation
108
+ primitive that carries argv only cannot call `transition` at all; that is the
109
+ gap the two transports above close, and widening argv is not the answer.
110
+
111
+ ### Output
112
+
113
+ Every `--json` invocation writes exactly one JSON object and one trailing LF to
114
+ stdout, and nothing to stderr. A host caps both streams and treats an incomplete
115
+ stdout as an unresolved outcome, not as a refusal.
116
+
117
+ `list` and `inspect --workbench` are the two bounded reads: each carries an
118
+ advertised response bound, refuses rather than exceeding it, and says which
119
+ bound it hit. A full `inspect` has no response bound, and this is deliberate
120
+ rather than an omission. `inspect` is lossless: it returns the complete
121
+ base64 item source beside the parsed core view, and an item committed before
122
+ `max_item_source_bytes` existed is still inspectable. Bounding it would make the
123
+ one read an operator uses to repair an oversized item the one read that refuses.
124
+ A host that needs a bound uses `list` to choose a row and reads
125
+ `max_item_source_bytes` to size its buffer for the read that follows.
126
+
127
+ ## 4. Path semantics on the owning host
128
+
129
+ The host that owns the workspace is the host that runs the core, with that
130
+ runtime's own Node executable and that runtime's own path spelling. There is
131
+ no cross-runtime path guessing.
132
+
133
+ | Host shape | Ledger path | Node executable |
134
+ |---|---|---|
135
+ | native worktree or plain folder | the local absolute path | the local runtime's Node |
136
+ | Git worktree | the worktree's own path, not the main checkout's | the local runtime's Node |
137
+ | direct SSH | the remote absolute path, resolved on the remote | the remote runtime's Node |
138
+ | WSL or another paired runtime | the path as that runtime spells it | that runtime's Node |
139
+
140
+ A Windows host must not translate a path into a WSL path, or the reverse, and
141
+ then launch the other runtime's core against it. Route the invocation to the
142
+ runtime that owns the workspace and let it resolve its own package.
143
+
144
+ Routing, relay negotiation, credential forwarding, and consent are the host's
145
+ concerns. Wowbagger neither performs nor validates them.
146
+
147
+ ## 5. Reading the response
148
+
149
+ ### Dispatch on the namespace first
150
+
151
+ A consumer must dispatch on the root `namespace` member before it reads any
152
+ version field. Two domains answer to `capabilities`, and one core command can
153
+ answer in two domains, so the command member alone is ambiguous.
154
+
155
+ 1. A response with a root `namespace` member belongs to the domain that member
156
+ names.
157
+ 2. A response with no namespace member but a root `ok` member belongs to the core
158
+ domain; its `contract_version` is the core contract version.
159
+ 3. A response with neither is a bare result. Only `validate` and `ready` emit
160
+ one, and they are deliberately outside the envelope rule for compatibility.
161
+
162
+ | Domain | Root `namespace` | Version to negotiate |
163
+ |---|---|---|
164
+ | core | absent | top-level `contract_version` of `capabilities --json`, currently `5` |
165
+ | bare result | absent, and no `ok` member either | none |
166
+ | work-claim | `work-claim` | `result.operations.work_claim.api_version` |
167
+ | ledger-publication | `ledger-publication` | the same work-claim `api_version` |
168
+ | ledger-mutation | `ledger-mutation` | the same work-claim `api_version` |
169
+
170
+ A claim-domain `contract_version: 1` is the legacy envelope marker. Never
171
+ compare it with the core `contract_version`.
172
+
173
+ ### Exit status
174
+
175
+ | Exit | Condition |
176
+ |---:|---|
177
+ | 0 | successful command; a mutation is state `committed` |
178
+ | 1 | a bare-result command found the ledger invalid, or `report` refused: an invalid ledger, an unreadable input, or a failed publication |
179
+ | 2 | argument, request, lookup, or candidate/lifecycle/layout-precondition failure |
180
+ | 3 | the complete configured ledger is invalid, for every command except `report` |
181
+ | 4 | cooperative comparison, lock, identity, or default-path conflict |
182
+ | 5 | the backend lacks the required capability or write scope |
183
+ | 6 | an unexpected operating or post-publication recovery condition |
184
+
185
+ Only exit 0 is normal completion. A host must read the mutation `state` member
186
+ on every nonzero `create`, `transition`, or `patch` result.
187
+
188
+ ### When the response is lost
189
+
190
+ Only complete observed process output establishes a result. When a transition's
191
+ response never arrives — a signal, a timeout, an incomplete stream, or a lost
192
+ transport — the outcome is unresolved, and the host follows one sequence:
193
+ **Dispatch once, never replay, invalidate the inspected revision, reconnect,
194
+ then re-read the ledger.** A later ledger state is not causal proof that the
195
+ lost dispatch produced it.
196
+
197
+ Exit 4 `revision-conflict` is the exception in the other direction: it is a
198
+ proven non-write. Re-inspect for the current revision and dispatch again with
199
+ it.
200
+
201
+ There is no operation identity, operation journal, replay endpoint, or automatic
202
+ retry, and adding correlation requires a new contract decision. [The mutation
203
+ contract](mutation-contract.md), section 10, is normative for this behaviour.
204
+
205
+ ## 6. Advertised limits and capabilities
206
+
207
+ `capabilities --json` advertises every semantic limit under the negotiated core
208
+ contract version. Read them there: no consumer must infer a limit from an
209
+ adapter implementation, and none of these numbers is discoverable any other way.
210
+
211
+ | `result.limits` member | Value | Meaning |
212
+ |---|---:|---|
213
+ | `max_item_source_bytes` | 8388608 | the complete serialized item source at every candidate door |
214
+ | `default_list_page_size` | 50 | the page size `list` uses when the query names none |
215
+ | `max_list_page_size` | 200 | the largest page size a query may request |
216
+ | `max_list_title_characters` | 120 | the code-point bound on a projected list title |
217
+ | `max_list_response_bytes` | 131072 | the whole `list` response, envelope and trailing LF included |
218
+ | `max_workbench_title_characters` | 120 | the code-point bound on a projected workbench title |
219
+ | `max_workbench_collection_entries` | 50 | the largest bounded workbench collection |
220
+ | `max_workbench_response_bytes` | 65536 | the whole `inspect --workbench` response |
221
+
222
+ The same envelope advertises which operations exist and how they are negotiated:
223
+ `result.operations.list.query_version`,
224
+ `result.operations.inspect.workbench.projection_version`,
225
+ `result.operations.report.config_versions` with `named_views`, and
226
+ `result.operations.work_claim.api_version`. The list query and the workbench
227
+ projection have their own version domains; a shape this core does not recognize
228
+ is refused by that number, not by the core contract version.
229
+
230
+ `result.limits` also states plainly what the local backend does not do:
231
+ `multi_item_atomicity`, `cross_clone_coordination`,
232
+ `cross_worktree_coordination`, `cross_machine_coordination`,
233
+ `noncooperating_writer_protection`, and `automatic_stale_lock_breaking` are all
234
+ `false`.
235
+
236
+ ## 7. Packaged JSON Schemas
237
+
238
+ The schemas are published with the package and resolvable from an installed
239
+ consumer as `wowbagger/schemas/<file>`. `schemas/index.json` names each one's
240
+ response domain and that domain's version. Each `$id` is a stable identifier,
241
+ not a fetch URL.
242
+
243
+ | Schema | Domain | Validates |
244
+ |---|---|---|
245
+ | `common.json` | shared | definitions reused below; not a validation target on its own |
246
+ | `core-envelope.json` | core 5 | every core-domain response, by its four exact root shapes |
247
+ | `core-capabilities-response.json` | core 5 | `capabilities --json`, including every advertised limit |
248
+ | `core-inspect-response.json` | core 5 | the default lossless `inspect` read |
249
+ | `core-inspect-workbench-response.json` | core 5 | `inspect --workbench`, projection version 1 |
250
+ | `core-list-query.json` | list query 1 | the strict `list` request |
251
+ | `core-list-response.json` | core 5 | a bounded, paginated `list` page |
252
+ | `core-list-error-response.json` | core 5 | every documented `list` refusal |
253
+ | `core-transition-request.json` | core 5 | the strict `transition` request |
254
+ | `core-transition-response.json` | core 5 | a committed `transition` |
255
+ | `core-transition-error-response.json` | core 5 | every documented `transition` refusal, with its state |
256
+ | `core-report-response.json` | core 5 | `report` publications and refusals, including a named view |
257
+ | `bare-validation-result.json` | bare result | `validate`, and `ready` on an invalid ledger |
258
+ | `bare-ready-result.json` | bare result | a `ready` success |
259
+ | `ledger-mutation-refusal.json` | ledger-mutation 1 | the legacy-write fence refusals |
260
+ | `report-config-v1.json` | report config 1 | `<ledger>/.wowbagger/report.json` at version 1 |
261
+ | `report-config-v2.json` | report config 2 | the same file at version 2, which names views |
262
+
263
+ Every schema fixes its root members exactly and pins the version of its own
264
+ domain. A core schema therefore refuses a namespaced refusal, a neighbouring
265
+ contract version, and an extra root member, and a version 1 report configuration
266
+ refuses a version 2 one. Validate against the schema for the domain you
267
+ dispatched to, never against a union of all of them.
268
+
269
+ ## 8. What Wowbagger does not provide
270
+
271
+ These are host concerns or rejected product behaviour. They are not coming to
272
+ the core, and a host must not read this contract as a promise of any of them:
273
+
274
+ - consent UI, declared-tool policy, and audit records;
275
+ - opaque workspace tokens, focus generation, and focus invalidation;
276
+ - remote routing, relay negotiation, and credential forwarding;
277
+ - process spawning, host caps, and cancellation enforcement;
278
+ - workspace event listeners and change notifications;
279
+ - an automatic transition, a scheduled mutation, or any write the caller did
280
+ not dispatch explicitly;
281
+ - mirrored ledger state or a second store; a response is a live projection and
282
+ may be cached as transient interface state only;
283
+ - operation identity, an operation journal, or a replay endpoint; and
284
+ - a daemon, an RPC service, or a long-lived core process.
285
+
286
+ Each invocation is one process, started by the host, that reads a request,
287
+ writes one response, and exits.