docs(adr): ADR 009 leaves the operator's shape to its open question and points recommended clauses at theirs (improvise-felo.4)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
committed by
Edward Langley
parent
b7add90830
commit
e4f915423c
@@ -252,8 +252,8 @@ read-time parameters here. It keeps its one job, bumping `layout_version`. The c
|
||||
neither reads nor depends on that tier, which is a view fact. Only `document_generation`
|
||||
and the record tier matter to this ADR.
|
||||
|
||||
**Item grain.** Occupancy and per-column aggregates are keyed by the **items** of the
|
||||
axis categories, never by a position. So collapsing, expanding, hiding, showing,
|
||||
**Item grain** (recommended; see open question 6). Occupancy and per-column aggregates
|
||||
are keyed by the **items** of the axis categories, never by a position. So collapsing, expanding, hiding, showing,
|
||||
reordering and adding an axis item change no operator: the visible axis is a remap.
|
||||
ADR 008 measured the remap at 2.3 ms for 100k occupied tuples, against 6 to 45 ms for a
|
||||
rebuild. A collapsed group's value is combined from its members when read. That is exact
|
||||
@@ -349,7 +349,8 @@ money.
|
||||
|
||||
### 9. What ADR 008 gets
|
||||
|
||||
- **Occupancy**, per shape key at item grain: a count per row tuple and per column tuple.
|
||||
- **Occupancy**, per shape key at item grain: a count per row tuple and per column tuple
|
||||
(kept as a pre-filter and as the unpruned extent; see open question 11).
|
||||
Formula cells count by clause 5's stem rule. A tuple is occupied when its count is above zero.
|
||||
A fold row with a formula column but no stored cell under it, and the `Exact` case with
|
||||
nothing on None, are **not** occupied, on purpose.
|
||||
@@ -403,7 +404,8 @@ part of the output and is never state. So a request costs O(visible).
|
||||
|
||||
**Positions are absolute cross-product positions**, in each category's own item order,
|
||||
and a bigint where they do not fit 2⁵³ (sent as a JSON string). They do not move when
|
||||
occupancy changes. They do move when an item is inserted, removed or reordered, so:
|
||||
occupancy changes. They do move when an item is inserted, removed or reordered, so the
|
||||
recommended design (open question 10) is:
|
||||
- output elements are keyed by tuple identity (item ids under ADR 007), with the absolute
|
||||
position attached when sent;
|
||||
- an item edit is sent to every client as an **axis change** ("inserted at index j of
|
||||
@@ -426,11 +428,13 @@ O(|S| log n + k|S|). The index holds O(occupied cells) beside the store, fed fro
|
||||
cell delta and the stem rule. The query only looks forward, so a sorted `Vec` or a
|
||||
B-tree with a lower-bound seek suffices, with no rank over occupied tuples. A fill's
|
||||
whole state is its request and its last absolute position, so a resume is a seek.
|
||||
**Scrolling up or left is a new request with a new top-left.** The new top-left is found
|
||||
by galloping backwards in doubling jumps until a probe hits, then narrowing until one new
|
||||
row is gained. Each probe is a restricted successor query bounded by the old top-left.
|
||||
That is about 14 to 28 probes at density 10⁻⁴, and no special answer is needed when a
|
||||
top-left lands on an empty row. Which side of the wire runs the prune and the fill is
|
||||
**Scrolling up or left is a new request with a new top-left.** How the client finds that
|
||||
top-left when rows are pruned is the recommendation of open question 9: it gallops
|
||||
backwards in doubling jumps until a probe hits, then narrows until one new row is gained.
|
||||
Each probe is a restricted successor query bounded by the old top-left. That is about 14
|
||||
to 28 probes at density 10⁻⁴, and no special answer is needed when a top-left lands on an
|
||||
empty row. The alternatives there are a model-side predecessor query, and failing the
|
||||
fetch and retrying. Which side of the wire runs the prune and the fill is
|
||||
open question 9.
|
||||
|
||||
**The wire properties**, which hold on each hop:
|
||||
@@ -483,10 +487,10 @@ refetch by every client in a busy batch. Today's per-mirror push (`Sessions`,
|
||||
extent is the admitted record count, which the client fetches. Whether notices should be
|
||||
filtered per client is open question 3.
|
||||
|
||||
**A connection operator, at the wire boundary.** One component owns both ends of a
|
||||
connection. Its host half is a sink for the circuit's results and notices and a source of
|
||||
requests and model commands. Its client half is the mirror image: a source of results to
|
||||
the client, and a sink for the client's requests. It owns:
|
||||
**A connection operator, at the wire boundary.** A connection operator sits at the wire
|
||||
boundary as a sink/source adapter for the circuit. Toward the circuit it is a sink for
|
||||
results and notices and a source of requests and model commands. Toward the client it is
|
||||
the mirror image. It owns:
|
||||
- serialization (T1, T2);
|
||||
- stamps and the refetch rule (T3);
|
||||
- coalescing (T5);
|
||||
@@ -495,12 +499,11 @@ the client, and a sink for the client's requests. It owns:
|
||||
batches";
|
||||
- session resumption.
|
||||
|
||||
In process, both halves are function calls. That covers the TUI, and the browser's
|
||||
`Worker`, which already owns one `Host` and one port (`worker.rs:14-15`). The host half
|
||||
belongs in or above `improvise-incremental`, and the wire types in `improvise-protocol`.
|
||||
`zset` stays free of serde. The client half is shared by every front end, beside
|
||||
`improvise-protocol`'s `ClientState`, so that a new front end does not write it again.
|
||||
Its remaining shape is open question 17.
|
||||
In process it is function calls, as in the TUI and in the browser's `Worker`, which
|
||||
already owns one `Host` and one port (`worker.rs:14-15`). The wire types belong in
|
||||
`improvise-protocol`, and `zset` stays free of serde. Where its halves live (one
|
||||
component per connection, or a host half shared per host process with a client half in
|
||||
every front end), and who owns resumption state, are open question 17.
|
||||
|
||||
### 13. Column widths for text
|
||||
|
||||
@@ -610,7 +613,10 @@ the displayed path until the differential test holds:
|
||||
re-keys every entry stored under the moved items. *Recommendation:* the order label,
|
||||
with only the drawn members ordered. Not measured.
|
||||
|
||||
3. **Should notices be filtered per client?** Broadcast (clause 12) keeps no per-client state,
|
||||
3. **Should notices be filtered per client?** Clause 12's notice replaces the pushed
|
||||
stream. The rejected alternative is to send no notice at all and let each client
|
||||
refetch on its own schedule: it keeps the host silent, but another client's change
|
||||
stays invisible until the next refetch. Broadcast (clause 12) keeps no per-client state,
|
||||
but every client receives every live shape's moved keys. The options:
|
||||
- **topics:** a client receives only its own shape key's key lists. Headers still go to
|
||||
everyone, so the refetch rule keeps working. That costs one subscription per
|
||||
@@ -663,8 +669,13 @@ the displayed path until the differential test holds:
|
||||
state. On the model, prune is a request parameter, and the model keeps a pruned axis,
|
||||
with rank and select for "row i of n", and runs the fill as a sliced job resumable
|
||||
from (request, fill front). At a density of 10⁻⁴, a client without the successor query
|
||||
would ship mostly empty windows. ADR 008 asks the same question. *Recommendation:* the
|
||||
client.
|
||||
would ship mostly empty windows. A second part: when scrolling up or left, how does the
|
||||
client find a new top-left when rows are pruned? The options are galloping backwards
|
||||
with successor probes (about 14 to 28 probes at density 10⁻⁴, with no host state), a
|
||||
model-side predecessor query (one round trip, no new state, but a second query
|
||||
direction), or failing the fetch and retrying with the nearest occupied row named. ADR
|
||||
008 asks the same question. *Recommendation:* the client prunes and fills, and it
|
||||
gallops.
|
||||
|
||||
10. **Are deltas keyed by identity, with item edits sent as axis changes?** Keying deltas
|
||||
by position instead goes stale on every item insert. Identity costs rank and select
|
||||
|
||||
Reference in New Issue
Block a user