Skip to content

Commit b3cee51

Browse files
committed
docs(adr): rename parse.error to invalid, strip em-dashes, reframe trade-off as property
1 parent fe23570 commit b3cee51

1 file changed

Lines changed: 27 additions & 27 deletions

File tree

docs/adr/0006-xs-lifecycle-topics.md

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ namespace as user data.
2727
1. **Hot-replace with parse error**. Service and action keep the *old*
2828
instance running live (correct), but their *historical compaction* is
2929
latest-wins: on restart, the broken `.spawn` / `.define` is what's
30-
"remembered" and the previously good version is lost. Actor is worse --
30+
"remembered" and the previously good version is lost. Actor is worse:
3131
the old instance self-terminates before the new one is validated, so a
3232
broken hot-replace kills both.
3333

@@ -44,7 +44,7 @@ namespace as user data.
4444

4545
4. **Runtime lifecycle frames share the user namespace**. To find "all
4646
actor lifecycle events," the runtime has to scan every frame in the
47-
store and filter by suffix -- O(stream), no index can help cheaply.
47+
store and filter by suffix, an O(stream) cost that no index helps.
4848
This drives the historical-scan cost we measured at ~17us/frame ×
4949
110k frames per dispatcher start.
5050

@@ -58,7 +58,7 @@ implementation. Each is referenced by the invariant set below.
5858
2. Service hot-replace where the new `.spawn` has a parse error: old
5959
keeps running live (correct), but compaction is latest-wins so the
6060
broken `.spawn` survives and the service vanishes on next boot.
61-
3. Action hot-replace + parse error: same shape as #2 -- broken `.define`
61+
3. Action hot-replace + parse error: same shape as #2. Broken `.define`
6262
overwrites; previously-good define is lost on restart.
6363
4. Action with a broken `.define` retries the broken version on every
6464
boot. Dispatcher doesn't scan `.error` historically; no "skip broken"
@@ -69,16 +69,16 @@ implementation. Each is referenced by the invariant set below.
6969
6. Action has no undefine. Once defined, the only way to remove is to
7070
edit history.
7171
7. Action `.error` overloads parse failure (`register_action` Err) and
72-
runtime failure (per-call `execute_action` Err) -- can't tell apart
73-
at the topic level.
72+
runtime failure (per-call `execute_action` Err); the two cannot be
73+
told apart at the topic level.
7474
8. Actor `.unregistered` overloads parse failure with graceful
75-
teardown -- only `meta.error`'s presence distinguishes them.
75+
teardown; only `meta.error`'s presence distinguishes them.
7676

7777
## Decision
7878

7979
### Namespace
8080

81-
Lifecycle frames live under `xs.` -- a namespace owned by the runtime.
81+
Lifecycle frames live under `xs.`, a namespace owned by the runtime.
8282
User-chosen data topics stay where they are.
8383

8484
```
@@ -95,7 +95,7 @@ Glance test: a topic starting with `xs.` is runtime-managed; everything
9595
else is app data.
9696

9797
Runtime queries become pure prefix scans on the existing hierarchical
98-
`idx_topic` index -- no new index keyspace, no suffix matcher, no
98+
`idx_topic` index, with no new index keyspace, no suffix matcher, and no
9999
schema-version bump:
100100

101101
| Query | Prefix |
@@ -106,7 +106,7 @@ schema-version bump:
106106
| all modules | `xs.module.` |
107107
| one module's history | `xs.module.game.` |
108108

109-
At startup, each dispatcher reads only the frames in its own namespace --
109+
At startup, each dispatcher reads only the frames in its own namespace,
110110
a few hundred, not 110k.
111111

112112
### Lifecycle vocabulary
@@ -119,7 +119,7 @@ Apply uniformly across actor, service, and action (action uses a subset).
119119
| `create` | in | user wants this thing running |
120120
| `term` | in | user wants this thing stopped |
121121
| `active` | out | runtime is up; `meta` points at the originating `create` |
122-
| `parse.error` | out | source failed to parse; `meta` points at the originating `create` |
122+
| `invalid` | out | source failed to parse (or any other init-time validation); `meta` points at the originating `create` |
123123
| `fin.error` | out | runtime crashed |
124124
| `fin.ok` | out | task ran to natural completion |
125125
| `fin.term` | out | exited because of `term` |
@@ -144,7 +144,7 @@ State transitions:
144144
|---|---|
145145
| `create` | `pending = this` |
146146
| `active(source=X)` | `confirmed = create-X`; clear `pending` if it points at X |
147-
| `parse.error(source=X)` | clear `pending` if it points at X |
147+
| `invalid(source=X)` | clear `pending` if it points at X |
148148
| `term` | clear both |
149149
| `fin.*` (error / ok / term) | clear both |
150150
| `replaced` | no effect |
@@ -163,7 +163,7 @@ else: nothing to start
163163
- **Hot-replace race** (`create₁ → active₁ → create₂ → ???` and xs dies):
164164
on restart, `confirmed=create₁`, `pending=create₂`. Try `create₂`; on
165165
fail, fall back to `create₁`.
166-
- **Hot-replace, broken replacement** (`parse.error` lands live):
166+
- **Hot-replace, broken replacement** (`invalid` lands live):
167167
`pending` cleared, `confirmed=create₁` survives. Old version restarts on
168168
boot.
169169
- **Hot-replace success during transition window**: `replaced` does not
@@ -172,21 +172,21 @@ else: nothing to start
172172
overwrites `confirmed` cleanly.
173173
- **First create, never acked** (xs died before processing): `pending` set,
174174
`confirmed` empty. Try `pending`. If it succeeds, advance; if it fails,
175-
nothing to fall back to -- correct.
175+
nothing to fall back to (correct).
176176
- **Server crash mid-run**: `confirmed` set, `pending` empty. Start
177-
`confirmed` -- service was running fine, server crash should resume.
177+
`confirmed`: service was running fine, server crash should resume.
178178
- **Server shutdown**: `stopped` doesn't affect compaction; `confirmed`
179179
persists; service resumes on next boot.
180180
- **User `term` while xs offline is impossible** (fjall is single-writer),
181181
but `term` appended in a prior live session clears both slots, so the
182182
thing stays down on next boot.
183183

184-
### Trade-off accepted
184+
### Property: ack-independence of `term` and `fin.*`
185185

186-
`fin.*` clears both slots: a `term` is treated as a successful stop even
187-
if xs died before the `fin.term` ack landed. Acceptable -- a `term` in
188-
the log is a clear user intent, and respecting it without waiting for
189-
the ack matches what the user wanted.
186+
`term` and `fin.*` clear both compaction slots on observation. The
187+
algorithm never waits for a paired ack. If `term` is in the log but
188+
xs died before processing it (so no `fin.term` was emitted), the
189+
`term` alone keeps the thing stopped on the next restart.
190190

191191
### Invariants
192192

@@ -200,7 +200,7 @@ the previous section.
200200
subsequent `fin.*`/`term` resumes on every restart until something
201201
terminal lands.
202202
- **I3. Hot-replace fallback.** When a newer `create₂` follows a
203-
known-good `create₁`, and `create₂` is broken (`parse.error`) or
203+
known-good `create₁`, and `create₂` is broken (`invalid`) or
204204
untested (no ack), restarts fall back to `create₁`. Live behaviour
205205
and post-restart behaviour agree.
206206
- **I4. Bidirectional lifecycle.** Every kind supports a user-driven
@@ -209,7 +209,7 @@ the previous section.
209209
distinguishes: failed-to-init vs user-terminated vs runtime-crashed
210210
vs naturally-finished vs replaced vs server-shut-down.
211211
- **I6. Ack traceability.** Every runtime-emitted ack (`active`,
212-
`parse.error`, `fin.*`, `replaced`) carries a meta pointer to its
212+
`invalid`, `fin.*`, `replaced`) carries a meta pointer to its
213213
originating `create` (or `term`).
214214
- **I7. Server-shutdown invisibility.** A `stopped` event does not
215215
affect compaction; the thing resumes on next start.
@@ -234,7 +234,7 @@ invariant:
234234

235235
I6 and I8 don't catch one of the enumerated deficiencies directly, but
236236
they're load-bearing for the invariants that do: without ack traceability
237-
(I6) you can't pair `parse.error` to its `create`, so I3 is unenforceable;
237+
(I6) you can't pair `invalid` to its `create`, so I3 is unenforceable;
238238
without single-instance (I8) you can't unambiguously define "the thing"
239239
that I1/I2 track. They stay in the set as supporting invariants.
240240

@@ -246,8 +246,8 @@ misreading of `stopped` as a terminal event.
246246

247247
Actions don't run long-lived tasks. The events they use:
248248

249-
- `create` (was `.define`), `term` (new -- adds the missing undefine),
250-
`active` (was `.ready`), `parse.error`, `fin.term` (on user undefine),
249+
- `create` (was `.define`), `term` (new, adds the missing undefine),
250+
`active` (was `.ready`), `invalid`, `fin.term` (on user undefine),
251251
`fin.replaced` (on re-define), `replaced` (transient).
252252
- No `fin.ok` (actions don't naturally finish), no `fin.error` at the
253253
*lifecycle* level (per-invocation runtime errors stay on the app's
@@ -270,17 +270,17 @@ api.spawn -> xs.service.api.create
270270
api.terminate -> xs.service.api.term
271271
api.stopped (reason=...) -> xs.service.api.fin.{ok|error|term} or .replaced
272272
api.shutdown -> xs.service.api.stopped
273-
api.parse.error -> xs.service.api.parse.error
273+
api.parse.error -> xs.service.api.invalid
274274
greet.define -> xs.action.greet.create
275275
greet.ready -> xs.action.greet.active
276-
greet.error -> xs.action.greet.parse.error (parse cases only)
276+
greet.error -> xs.action.greet.invalid (parse cases only)
277277
game.nu -> xs.module.game
278278
```
279279

280280
Same `idx_topic` entries get rewritten alongside. CAS bytes are
281281
untouched.
282282

283-
After migration, the runtime only knows the new vocabulary -- any
283+
After migration, the runtime only knows the new vocabulary; any
284284
remaining old-shape frames in a partially-migrated store would be ignored
285285
as app data, which is incorrect, so the migration is mandatory and
286286
atomic with the version bump.

0 commit comments

Comments
 (0)