docs(context): correct Model::over's file, the staged unit count, what moves seen, and who catches a panicking turn

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
Claude (bookkeeping2)
2026-10-10 22:33:51 -07:00
committed by Edward Langley
parent 482f7178d1
commit 745d179402
4 changed files with 9 additions and 9 deletions
+2 -2
View File
@@ -296,7 +296,7 @@ crates/improvise-core/examples/category_width_bench.rs
### Sources, incremental evaluation and circuits (`crates/improvise-core/src/model/`, `crates/{improvise-incremental,zset,zset-ffi,numtower}/`) ### Sources, incremental evaluation and circuits (`crates/improvise-core/src/model/`, `crates/{improvise-incremental,zset,zset-ffi,numtower}/`)
``` ```
improvise-core/src/model/source.rs CellSource, ChangeSource, Select, Summary/Numbers/Agreement, BadParts, SharedSource; TableSource (test-support); Model::over improvise-core/src/model/source.rs CellSource, ChangeSource, Select, Summary/Numbers/Agreement, BadParts, SharedSource; TableSource (test-support); `Model::over` is in model/types.rs
improvise-core/src/model/over_tests.rs tests of a model over a source improvise-core/src/model/over_tests.rs tests of a model over a source
improvise-incremental/src/lib.rs Derived trait and re-exports improvise-incremental/src/lib.rs Derived trait and re-exports
reading.rs, delta.rs, eager.rs Reading, CellDelta/DeltaValue/cell_delta/moved_records, Eager (the oracle) reading.rs, delta.rs, eager.rs Reading, CellDelta/DeltaValue/cell_delta/moved_records, Eager (the oracle)
@@ -304,7 +304,7 @@ improvise-incremental/src/lib.rs Derived trait and re-exports
incremental.rs, edit.rs, fold.rs Incremental (over/sync/apply_changes), Edit, the fold incremental.rs, edit.rs, fold.rs Incremental (over/sync/apply_changes), Edit, the fold
pushdown.rs, reads.rs Pushdown over a query-answering source; reads shared with Eager pushdown.rs, reads.rs Pushdown over a query-answering source; reads shared with Eager
harness.rs differential, probes, Maintained harness.rs differential, probes, Maintained
sched.rs, staged.rs Executor/Unit/Pool (Pool native-only); the aggregate/grid example on two units sched.rs, staged.rs Executor/Unit/Pool (Pool native-only); the aggregate/grid example on three units (head, aggregate, grid)
zset/src/{circuit,ops,trace,edge}.rs Circuit (arena, Send not Sync, begin/commit/rollback), operators, Trace, edges (EdgeSink/EdgeSource/Pipe/EdgeCodec/Stamp) zset/src/{circuit,ops,trace,edge}.rs Circuit (arena, Send not Sync, begin/commit/rollback), operators, Trace, edges (EdgeSink/EdgeSource/Pipe/EdgeCodec/Stamp)
zset-ffi/src/{lib,engine}.rs the C ABI; include/zset_ffi.h (zs_circuit_begin/commit/rollback, ZS_ERR_TRANSACTION, ZS_ERR_INTERRUPTED) zset-ffi/src/{lib,engine}.rs the C ABI; include/zset_ffi.h (zs_circuit_begin/commit/rollback, ZS_ERR_TRANSACTION, ZS_ERR_INTERRUPTED)
``` ```
+2 -2
View File
@@ -138,7 +138,7 @@ Dated claims and counts inside an entry are snapshots: recount rather than cite.
53. **Delivery: a watermark is a `Stamp`; a correction is `target - held`, applied only at the watermark it starts from.** `through` is not `at` (the watermark advances only to `through`), a refused frame restores `Delivery` with the app snapshot, and a lost last notice is found only by `poll`. Axis changes refetch the window until ADR 008's render model. The argument is `context/code/delivery.md` (sections 3, 4 and 6); a refused frame restoring `Delivery` is §6, Rollback. 53. **Delivery: a watermark is a `Stamp`; a correction is `target - held`, applied only at the watermark it starts from.** `through` is not `at` (the watermark advances only to `through`), a refused frame restores `Delivery` with the app snapshot, and a lost last notice is found only by `poll`. Axis changes refetch the window until ADR 008's render model. The argument is `context/code/delivery.md` (sections 3, 4 and 6); a refused frame restoring `Delivery` is §6, Rollback.
**Livelocks (improvise-z2vr).** A notice from another epoch only sets `stale`; `seen` moves only through an answer (`apply_answer`, shared by `sync` and `poll`), because ignoring one kind of foreign notice after another left the live host's old notices looking new. `Notice::cut_for` limits a notice to one shape and notices cut for different shapes do not merge. Long form `context/code/delivery.md`, sections 3 and 4. **Livelocks (improvise-z2vr).** A notice from another epoch only sets `stale`; a notice from a foreign epoch never becomes `seen`; only an answer (`apply_answer`, shared by `sync` and `poll`) moves `seen` to another epoch, because ignoring one kind of foreign notice after another left the live host's old notices looking new. `Notice::cut_for` limits a notice to one shape and notices cut for different shapes do not merge. Long form `context/code/delivery.md`, sections 3 and 4.
54. **Sources and exactness:** one evaluator (`Model`'s) over any `CellSource`; a `Model::over` source is read-only in every build, and a `ChangeSource` has one drainer (its cells are as of its last drain). Eager's math is fixed (`settled` compares by bits with every NaN one value; Min/Max by `total_cmp`; NaN-only Min/Max is NaN; a stable NaN is not `circular`). An exactness test needs an input that is inexact in floats. Stem order is a known evaluator defect (improvise-bw4p.30). `context/code/source.md`, `improvise-incremental.md`. 54. **Sources and exactness:** one evaluator (`Model`'s) over any `CellSource`; a `Model::over` source is read-only in every build, and a `ChangeSource` has one drainer (its cells are as of its last drain). Eager's math is fixed (`settled` compares by bits with every NaN one value; Min/Max by `total_cmp`; NaN-only Min/Max is NaN; a stable NaN is not `circular`). An exactness test needs an input that is inexact in floats. Stem order is a known evaluator defect (improvise-bw4p.30). `context/code/source.md`, `improvise-incremental.md`.
@@ -146,7 +146,7 @@ Dated claims and counts inside an entry are snapshots: recount rather than cite.
56. **An edge joins by correction only.** A receiver joins an incarnation only by the answer to its latest request (a token per receiver incarnation, seeded fresh by `EdgeSource::new`); the host's timeout must exceed the round trip. `context/code/zset.md`, "Edges". 56. **An edge joins by correction only.** A receiver joins an incarnation only by the answer to its latest request (a token per receiver incarnation, seeded fresh by `EdgeSource::new`); the host's timeout must exceed the round trip. `context/code/zset.md`, "Edges".
57. **A scheduler turn runs outside the lock.** A panicking turn is caught and its unit buried (dropped inside `catch_unwind`), so `wait_idle` does not hang on a dead unit; a panic payload whose own drop panics is still open (improvise-bw4p.40). `context/code/improvise-incremental.md`, "Scheduling". 57. **A scheduler turn runs outside the lock.** `Pool` catches a panicking turn and buries its unit (dropped inside `catch_unwind`), so `wait_idle` does not hang on a dead unit; a caller driving `Executor::claim` itself must catch `Claimed::run` and call `Executor::abandon`; a panic payload whose own drop panics is still open (improvise-bw4p.40). `context/code/improvise-incremental.md`, "Scheduling".
58. **Tooling notes:** `Cargo.nix` has drifted from a fresh crate2nix run (improvise-pqf1). `proptest-regressions/` is gitignored, but zset commits its seeds (`crates/zset/tests/*.proptest-regressions`, a file beside the test, which that pattern does not match). `scripts/build-doc-site.mjs` lists every `context/` doc by hand: register a new one there. CI's test derivation sets `CARGO_PROFILE_DEV_DEBUG = "0"` (`nix/ci.nix`). 58. **Tooling notes:** `Cargo.nix` has drifted from a fresh crate2nix run (improvise-pqf1). `proptest-regressions/` is gitignored, but zset commits its seeds (`crates/zset/tests/*.proptest-regressions`, a file beside the test, which that pattern does not match). `scripts/build-doc-site.mjs` lists every `context/` doc by hand: register a new one there. CI's test derivation sets `CARGO_PROFILE_DEV_DEBUG = "0"` (`nix/ci.nix`).
+4 -4
View File
@@ -3,7 +3,7 @@
Terminal pivot-table app. Rust 2024, Ratatui TUI, command/effect architecture. Terminal pivot-table app. Rust 2024, Ratatui TUI, command/effect architecture.
Apache-2.0. Root binary+lib `improvise`; sub-crates under `crates/`: Apache-2.0. Root binary+lib `improvise`; sub-crates under `crates/`:
- `improvise-core` — `Model`, `View`, `Workbook`, number formatting, and `model/source.rs` (`CellSource`: where raw cells are read from; `Model::over`). Depends on `improvise-formula` + `numtower` (exact sums). No UI/IO. `context/code/source.md`. - `improvise-core` — `Model`, `View`, `Workbook`, number formatting, and `model/source.rs` (`CellSource`: where raw cells are read from; `Model::over` is in `model/types.rs`). Depends on `improvise-formula` + `numtower` (exact sums). No UI/IO. `context/code/source.md`.
- `improvise-formula` — formula parser, AST, `parse_formula`. - `improvise-formula` — formula parser, AST, `parse_formula`.
- `improvise-io` — `.improv` save/load, CSV/JSON import, `Storage` backends. No UI/commands. - `improvise-io` — `.improv` save/load, CSV/JSON import, `Storage` backends. No UI/commands.
- `improvise-protocol` — the wire vocabulary and the portable session slice: `Command`, `Projection`, `ViewState`, the per-widget caches, `reduce_view`, key types, and `delivery/{wire,notice,exchange,client}.rs` (notices, watermarks, corrections). Depends on `improvise-core`, `zset`, `numtower` + serde. No IO, no command layer, no terminal. - `improvise-protocol` — the wire vocabulary and the portable session slice: `Command`, `Projection`, `ViewState`, the per-widget caches, `reduce_view`, key types, and `delivery/{wire,notice,exchange,client}.rs` (notices, watermarks, corrections). Depends on `improvise-core`, `zset`, `numtower` + serde. No IO, no command layer, no terminal.
@@ -49,7 +49,7 @@ Architectural intent lives in `context/design-principles.md` — read that for t
| Change import-wizard keys/steps | `command/cmd/wizard.rs` (keys) + `import/wizard.rs` (steps) | | Change import-wizard keys/steps | `command/cmd/wizard.rs` (keys) + `import/wizard.rs` (steps) |
| Change frame layout | `draw.rs` → `draw()` | | Change frame layout | `draw.rs` → `draw()` |
| Change an incremental operator or the circuit | `crates/zset/src/{ops,circuit}.rs` | | Change an incremental operator or the circuit | `crates/zset/src/{ops,circuit}.rs` |
| Change where raw cells are read from | `improvise-core/src/model/source.rs` (`CellSource`, `Model::over`); `context/code/source.md` | | Change where raw cells are read from | `improvise-core/src/model/source.rs` (`CellSource`) + `model/types.rs` (`Model::over`); `context/code/source.md` |
| Change an incremental aggregate/formula | `improvise-incremental/src/{group,shapes,formulas}.rs` (`Incremental`); reads shared with `Eager`/`Pushdown` in `reads.rs` | | Change an incremental aggregate/formula | `improvise-incremental/src/{group,shapes,formulas}.rs` (`Incremental`); reads shared with `Eager`/`Pushdown` in `reads.rs` |
| Run sub-circuits on threads | `improvise-incremental/src/sched.rs` (`Executor`, `Unit`, `Pool`) + `staged.rs` | | Run sub-circuits on threads | `improvise-incremental/src/sched.rs` (`Executor`, `Unit`, `Pool`) + `staged.rs` |
| Change the edge protocol between circuits | `crates/zset/src/edge.rs` + `context/code/zset.md`, "Edges" | | Change the edge protocol between circuits | `crates/zset/src/edge.rs` + `context/code/zset.md`, "Edges" |
@@ -229,11 +229,11 @@ the incidents and the ticket ids.** Read the long form of every gotcha your diff
50. **A composed TUI row measures in cells and names which side yields** (`fill_line` + `Keeps`). Keep whichever key table is *derived from what it describes*. Scrolls are measured where drawn (`grid_width`). 50. **A composed TUI row measures in cells and names which side yields** (`fill_line` + `Keeps`). Keep whichever key table is *derived from what it describes*. Scrolls are measured where drawn (`grid_width`).
51. **The command language reads pipes, refuses surplus, and checks names when applied** (`only_when_omitted`; `RequireKnown`/`unless_unknown`). 51. **The command language reads pipes, refuses surplus, and checks names when applied** (`only_when_omitted`; `RequireKnown`/`unless_unknown`).
52. **A date column imports as one hierarchical category;** `--split-date` restores flat. `Plan::spelling_clash` refuses what would silently merge; `--strict` refuses partial skips. 52. **A date column imports as one hierarchical category;** `--split-date` restores flat. `Plan::spelling_clash` refuses what would silently merge; `--strict` refuses partial skips.
53. **Delivery: a watermark is a `Stamp`; a correction is `target - held`, applied only at the watermark it starts from.** `through` is not `at` (the watermark advances only to `through`), a refused frame restores `Delivery` with the app snapshot, and a lost last notice is found only by `poll`. Axis changes refetch the window until ADR 008's render model. A notice from a foreign epoch only marks the shape stale (`seen` moves only through `apply_answer`), and notices cut for different shapes (`cut_for`) never merge. Long form `context/code/delivery.md`. 53. **Delivery: a watermark is a `Stamp`; a correction is `target - held`, applied only at the watermark it starts from.** `through` is not `at` (the watermark advances only to `through`), a refused frame restores `Delivery` with the app snapshot, and a lost last notice is found only by `poll`. Axis changes refetch the window until ADR 008's render model. A notice from a foreign epoch only marks the shape stale (a foreign-epoch notice never becomes `seen`; only `apply_answer` moves `seen` to another epoch), and notices cut for different shapes (`cut_for`) never merge. Long form `context/code/delivery.md`.
54. **Sources and exactness:** one evaluator (`Model`'s) over any `CellSource`; a `Model::over` source is read-only in every build, and a `ChangeSource` has one drainer (its cells are as of its last drain). Eager's math is fixed (`settled` compares by bits with every NaN one value; Min/Max by `total_cmp`; NaN-only Min/Max is NaN; a stable NaN is not `circular`). An exactness test needs an input that is inexact in floats. Stem order is a known evaluator defect (improvise-bw4p.30). `context/code/source.md`, `improvise-incremental.md`. 54. **Sources and exactness:** one evaluator (`Model`'s) over any `CellSource`; a `Model::over` source is read-only in every build, and a `ChangeSource` has one drainer (its cells are as of its last drain). Eager's math is fixed (`settled` compares by bits with every NaN one value; Min/Max by `total_cmp`; NaN-only Min/Max is NaN; a stable NaN is not `circular`). An exactness test needs an input that is inexact in floats. Stem order is a known evaluator defect (improvise-bw4p.30). `context/code/source.md`, `improvise-incremental.md`.
55. **zset circuits are ids, not shared pointers.** `Stream`/`Input`/`Output` are `Copy` ids used through the circuit (`current`/`push`/`take`); a closure cannot read a stream during a step. Rollback replays newest first, `take` an output only after `commit`, and a panicking step outside a transaction leaves the circuit unusable. `context/code/zset.md`, "Threads" and "Transactions". 55. **zset circuits are ids, not shared pointers.** `Stream`/`Input`/`Output` are `Copy` ids used through the circuit (`current`/`push`/`take`); a closure cannot read a stream during a step. Rollback replays newest first, `take` an output only after `commit`, and a panicking step outside a transaction leaves the circuit unusable. `context/code/zset.md`, "Threads" and "Transactions".
56. **An edge joins by correction only.** A receiver joins an incarnation only by the answer to its latest request (a token per receiver incarnation, seeded fresh by `EdgeSource::new`); the host's timeout must exceed the round trip. `context/code/zset.md`, "Edges". 56. **An edge joins by correction only.** A receiver joins an incarnation only by the answer to its latest request (a token per receiver incarnation, seeded fresh by `EdgeSource::new`); the host's timeout must exceed the round trip. `context/code/zset.md`, "Edges".
57. **A scheduler turn runs outside the lock.** A panicking turn is caught and its unit buried (dropped inside `catch_unwind`), so `wait_idle` does not hang on a dead unit; a panic payload whose own drop panics is still open (improvise-bw4p.40). `context/code/improvise-incremental.md`, "Scheduling". 57. **A scheduler turn runs outside the lock.** `Pool` catches a panicking turn and buries its unit (dropped inside `catch_unwind`), so `wait_idle` does not hang on a dead unit; a caller driving `Executor::claim` itself must catch `Claimed::run` and call `Executor::abandon`; a panic payload whose own drop panics is still open (improvise-bw4p.40). `context/code/improvise-incremental.md`, "Scheduling".
58. **Tooling notes:** `Cargo.nix` has drifted from a fresh crate2nix run (improvise-pqf1). `proptest-regressions/` is gitignored, but zset commits its seeds (`crates/zset/tests/*.proptest-regressions`, a file beside the test, which that pattern does not match). `scripts/build-doc-site.mjs` lists every `context/` doc by hand: register a new one there. CI's test derivation sets `CARGO_PROFILE_DEV_DEBUG = "0"` (`nix/ci.nix`). 58. **Tooling notes:** `Cargo.nix` has drifted from a fresh crate2nix run (improvise-pqf1). `proptest-regressions/` is gitignored, but zset commits its seeds (`crates/zset/tests/*.proptest-regressions`, a file beside the test, which that pattern does not match). `scripts/build-doc-site.mjs` lists every `context/` doc by hand: register a new one there. CI's test derivation sets `CARGO_PROFILE_DEV_DEBUG = "0"` (`nix/ci.nix`).
--- ---
+1 -1
View File
@@ -595,12 +595,12 @@ decision('improvise-felo.4',dirty_flag_is_delta_plus,'The document dirty flag is
decision('improvise-bw4p',zset_send_by_arena,'Circuit is Send and not Sync with no lock: it owns every edge slot, input pending delta and sink sum in one Vec<Box<dyn Any + Send>> arena, nodes hold typed ids, and Stream/Input/Output are Copy ids used through the circuit (current/push/take; a foreign handle panics). A closure can therefore not read a stream during a step. Cost about 7-9% per tick, measured and kept (context/code/zset.md, Threads). Not Sync is pinned by a_circuit_is_not_sync.'). decision('improvise-bw4p',zset_send_by_arena,'Circuit is Send and not Sync with no lock: it owns every edge slot, input pending delta and sink sum in one Vec<Box<dyn Any + Send>> arena, nodes hold typed ids, and Stream/Input/Output are Copy ids used through the circuit (current/push/take; a foreign handle panics). A closure can therefore not read a stream during a step. Cost about 7-9% per tick, measured and kept (context/code/zset.md, Threads). Not Sync is pinned by a_circuit_is_not_sync.').
decision('improvise-bw4p',multithreading_is_scheduling,'User ruling 2026-10-11: a sub-circuit stays synchronous and single-threaded, the edge (zset::edge) is the only channel that crosses threads, and a small executor (improvise-incremental sched.rs) decides which thread runs which unit. Native threads only: Pool and the blocking calls are cfg(not(wasm32)). A ruling recorded in context/code, not an ADR amendment.'). decision('improvise-bw4p',multithreading_is_scheduling,'User ruling 2026-10-11: a sub-circuit stays synchronous and single-threaded, the edge (zset::edge) is the only channel that crosses threads, and a small executor (improvise-incremental sched.rs) decides which thread runs which unit. Native threads only: Pool and the blocking calls are cfg(not(wasm32)). A ruling recorded in context/code, not an ADR amendment.').
decision('improvise-bw4p',edge_join_by_correction,'A zset edge receiver joins a sender incarnation only by the answer to its latest request: a token per receiver incarnation (EdgeSource::new seeds it from a fresh base; restart keeps the counter), echoed in the Correction. A frame from an unjoined epoch only marks stale. Two earlier designs, joining by a first delta with from == 0 and a left list of incarnations, were wrong. The host timeout (connection_lost) must exceed the round trip (context/code/zset.md, Edges).'). decision('improvise-bw4p',edge_join_by_correction,'A zset edge receiver joins a sender incarnation only by the answer to its latest request: a token per receiver incarnation (EdgeSource::new seeds it from a fresh base; restart keeps the counter), echoed in the Correction. A frame from an unjoined epoch only marks stale. Two earlier designs, joining by a first delta with from == 0 and a left list of incarnations, were wrong. The host timeout (connection_lost) must exceed the round trip (context/code/zset.md, Edges).').
decision('improvise-bw4p',sched_worker_survives_any_turn_panic,'A unit turn runs outside the executor lock. Its panic is caught and the unit buried (never run or kicked again, recorded in Executor::panics), and a dead unit is dropped inside catch_unwind so a Drop that panics cannot kill the worker. wait_idle does not hang on a dead unit. OPEN: a panic_any payload whose own Drop panics is dropped outside catch_unwind (improvise-bw4p.40).').
decision('improvise-bw4p.5',zset_rollback_is_an_undo_log,'Circuit::begin/commit/rollback keep an undo log of what the steps touch, not a snapshot; rollback replays newest first so no undo leaves i64; a Rebuild is bound to its circuit and a rollback that took its chunks interrupts it for good (the circuit counts undone steps); take an Output only after commit. A step that panics outside a transaction leaves the circuit unusable. zset-ffi exposes zs_circuit_begin/commit/rollback with ZS_ERR_TRANSACTION and ZS_ERR_INTERRUPTED.'). decision('improvise-bw4p.5',zset_rollback_is_an_undo_log,'Circuit::begin/commit/rollback keep an undo log of what the steps touch, not a snapshot; rollback replays newest first so no undo leaves i64; a Rebuild is bound to its circuit and a rollback that took its chunks interrupts it for good (the circuit counts undone steps); take an Output only after commit. A step that panics outside a transaction leaves the circuit unusable. zset-ffi exposes zs_circuit_begin/commit/rollback with ZS_ERR_TRANSACTION and ZS_ERR_INTERRUPTED.').
decision('improvise-rnak',a_change_source_shows_state_as_of_its_last_drain,'A ChangeSource shows its cells as of its last drain_changes, so a snapshot read now plus changes drained later counts no change twice or not at all. A feed has one drainer: two backends over one feed split the changes (test a_feed_has_one_drainer). Model::over(source) is read-only in every build (assert, not debug_assert) and the evaluator is still the single Model one.'). decision('improvise-rnak',a_change_source_shows_state_as_of_its_last_drain,'A ChangeSource shows its cells as of its last drain_changes, so a snapshot read now plus changes drained later counts no change twice or not at all. A feed has one drainer: two backends over one feed split the changes (test a_feed_has_one_drainer). Model::over(source) is read-only in every build (assert, not debug_assert) and the evaluator is still the single Model one.').
decision('improvise-rnak',an_exactness_test_needs_an_inexact_input,'A test that a sum or merge is exact must feed values whose float sum is order-dependent or inexact; with exactly representable inputs a float implementation passes (found reviewing Summary merges). Summary::from_parts refuses states no sequence of values could produce rather than repairing them.'). decision('improvise-rnak',an_exactness_test_needs_an_inexact_input,'A test that a sum or merge is exact must feed values whose float sum is order-dependent or inexact; with exactly representable inputs a float implementation passes (found reviewing Summary merges). Summary::from_parts refuses states no sequence of values could produce rather than repairing them.').
decision('improvise-bw4p.20',eager_math_fixed,'User ruling 2026-10-10: do not reproduce math bugs in Eager. Min/Max order by total_cmp over non-NaN values (-0.0 below +0.0, platform independent); Min/Max over only NaN is NaN; the formula fixed point compares with model::types::settled (bits, every NaN one value) so a stable NaN is not circular and a zero sign flip is a change; empty SUM is +0.0. Min/Max skip NaN while Sum/Avg propagate it (kept, a product question).'). decision('improvise-bw4p.20',eager_math_fixed,'User ruling 2026-10-10: do not reproduce math bugs in Eager. Min/Max order by total_cmp over non-NaN values (-0.0 below +0.0, platform independent); Min/Max over only NaN is NaN; the formula fixed point compares with model::types::settled (bits, every NaN one value) so a stable NaN is not circular and a zero sign flip is a change; empty SUM is +0.0. Min/Max skip NaN while Sum/Avg propagate it (kept, a product question).').
decision('improvise-bw4p.30',stem_order_is_a_known_evaluator_defect,'recompute_formulas updates its cache in place in stem order and stops at 16 passes, so two sources holding the same cells in different orders can disagree. This is an evaluator defect, not a source contract: distinct_without returns first-seen order and a source with no order of its own should list stems in a stable one until it is fixed.'). decision('improvise-bw4p.30',stem_order_is_a_known_evaluator_defect,'recompute_formulas updates its cache in place in stem order and stops at 16 passes, so two sources holding the same cells in different orders can disagree. This is an evaluator defect, not a source contract: distinct_without returns first-seen order and a source with no order of its own should list stems in a stable one until it is fixed.').
decision('improvise-bw4p',sched_worker_survives_any_turn_panic,'A unit turn runs outside the executor lock. Pool catches its panic and buries the unit (never run or kicked again, recorded in Executor::panics); a caller driving Executor::claim itself must catch Claimed::run and call Executor::abandon, or the unit is never handed back. A dead unit is dropped inside catch_unwind so a Drop that panics cannot kill the worker. wait_idle does not hang on a dead unit. OPEN: a panic_any payload whose own Drop panics is dropped outside catch_unwind (improvise-bw4p.40).').
wire_carries(command,both_directions,'user intent up, projection commands down'). wire_carries(command,both_directions,'user intent up, projection commands down').
wire_carries(snapshot,on_connect_only,'improvise-q08: full ViewState + render cache filled from current viewport'). wire_carries(snapshot,on_connect_only,'improvise-q08: full ViewState + render cache filled from current viewport').
wire_carries(command,both_directions,'user intent client->server, projections server->client'). wire_carries(command,both_directions,'user intent client->server, projections server->client').