docs(adr): ADR 009 settles incremental cycles, a temporary Eager, and per-column width hints (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:
Claude (adr9-steward)
2026-10-09 22:28:52 -07:00
committed by Edward Langley
parent 6bf899c9ec
commit 81f1b5e13a
+26 -36
View File
@@ -54,10 +54,12 @@ depends on `zset` and improvise-core. improvise-core never depends on `zset`.
The circuit decides *which* formula cells to re-evaluate. The one existing evaluator
computes each value, so formulas are never compiled into operators.
**`Eager` is deprecated.** Today's code moves behind the trait as `Eager`, unchanged.
It serves as the oracle of a differential property test, which applies one random edit
sequence to both backends and compares every drawn cell, total and occupancy count. It
is removed once its remaining uses are replaced (open question 2).
**`Eager` is temporary.** Today's code moves behind the trait as `Eager`, unchanged. It
exists only to show discrepancies between the new evaluator and the old one: a
differential property test applies one random edit sequence to both backends and
compares every drawn cell, total and occupancy count. Eventually it is deleted, and
nothing depends on it surviving. Long-term tests compare against exact references, such
as `ExactSum` against rational sums, not against `Eager`.
### 3. The cell delta
@@ -126,12 +128,13 @@ before enumerating. This replaces `Expansion::members`.
### 6. Circular references
DBSP's least fixed point is not improvise's circularity: `X = X + 1` has none, and the
answer must be `Error("circular")`. The first version finds the formula graph's strongly
connected components, recomputed when the formula set changes; how to keep that
incremental belongs with open question 1. Acyclic formulas are evaluated once each, in topological order. A
component is evaluated with today's bounded fixed point, restricted to that component.
That reproduces the sixteen-pass bound for the differential test. It is to be replaced
by a DBSP-native treatment (open question 1).
answer must be `Error("circular")`. The formula graph's strongly connected components
are maintained incrementally as formulas change. Acyclic formulas are evaluated once
each, in topological order. A cyclic component is maintained incrementally too: a change
re-iterates only the cells it reaches. A component that does not converge within the
existing bound of sixteen passes (`MAX_EVAL_DEPTH`) propagates `Error("circular")`, as
today. An optimisation that speeds convergence is allowed only where it is exact: it
gives the same result as the plain iteration, and a test compares the two.
### 7. Operator state follows the request's shape key
@@ -343,12 +346,12 @@ circuit, the client's adapter is a sink for results and notices and a source of
Toward the client it is the mirror image. The client's end keeps the watermark. In
process, an edge is a function call.
### 10. Text column widths
### 10. Column width hints
This ADR owns the rule for text columns. The host maintains each text column's maximum
display width as a histogram from width to count, per (shape key, column), fed by the
delta of the column's text cells. Hidden rows come out through clause 7's overlay, where
that applies. Numeric widths stay with ADR 008's view-layer rule.
Every column incrementally maintains a width hint, which optimises grid sizing and
reduces jitter. The hint is an integrated aggregate: a histogram from display width to
count, so a retraction is O(log d). Which end of the connection maintains it is open
question 1.
### 11. Rollback and memory
@@ -413,24 +416,11 @@ Step 8's required tests:
## Open questions
1. **How should cycles be handled natively?** Clause 6's component fixed point is
interim. The options:
- (a) a recursion operator bounded at sixteen iterations, writing `circular` where it
has not converged, which keeps today's answers;
- (b) marking every cell in a graph cycle `circular`, which changes guarded cycles
such as `A = IF(x, B, 1)`;
- (c) a monotone reformulation, where one exists.
Separately, the strongly connected components are recomputed whenever the formula set
changes. Should they be maintained incrementally too? *Recommendation:* (a), and
recompute the components only for the formulas an edit touches.
2. **What replaces `Eager`?** It still serves as the differential oracle and as the
backend for live sources that can only answer aggregate queries. *Recommendation:* a
small from-scratch reference implementation, kept in the test crate, as the oracle,
and a pushdown backend behind the same trait for query-only sources.
3. **Which columns get a host-maintained width?** Columns whose cells are text? What
about a column holding both text and numbers? *Recommendation:* text cells get the
maintained width, and the view layer takes the larger of that and its own numeric
width.
1. **Which end of the connection maintains a column's width hint?** The client end knows
which values are potentially in its viewport, so it can maintain the hint over the
values it holds, fed by the deltas it already receives. The host end can maintain it
over a whole column, through the graph, including values the client has never held.
It would then need the request's hidden rows (clause 7's overlay) and, for numbers, the
number format. *Recommendation:* the client end, over the values it holds. The host
computes a whole-column hint only where a client asks for one, as a filter operator
on its connection (clause 9).