@@ -27,7 +27,7 @@ namespace as user data.
27271 . ** 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
45454 . ** 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.
58582 . 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.
63634 . 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.
69696 . Action has no undefine. Once defined, the only way to remove is to
7070 edit history.
71717 . 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.
74748 . 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.
8282User-chosen data topics stay where they are.
8383
8484```
@@ -95,7 +95,7 @@ Glance test: a topic starting with `xs.` is runtime-managed; everything
9595else is app data.
9696
9797Runtime 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
9999schema-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,
110110a 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
235235I6 and I8 don't catch one of the enumerated deficiencies directly, but
236236they'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;
238238without single-instance (I8) you can't unambiguously define "the thing"
239239that I1/I2 track. They stay in the set as supporting invariants.
240240
@@ -246,8 +246,8 @@ misreading of `stopped` as a terminal event.
246246
247247Actions 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
270270api.terminate -> xs.service.api.term
271271api.stopped (reason=...) -> xs.service.api.fin.{ok|error|term} or .replaced
272272api.shutdown -> xs.service.api.stopped
273- api.parse.error -> xs.service.api.parse.error
273+ api.parse.error -> xs.service.api.invalid
274274greet.define -> xs.action.greet.create
275275greet.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)
277277game.nu -> xs.module.game
278278```
279279
280280Same ` idx_topic ` entries get rewritten alongside. CAS bytes are
281281untouched.
282282
283- After migration, the runtime only knows the new vocabulary -- any
283+ After migration, the runtime only knows the new vocabulary; any
284284remaining old-shape frames in a partially-migrated store would be ignored
285285as app data, which is incorrect, so the migration is mandatory and
286286atomic with the version bump.
0 commit comments