docs(adr): ADR 009 records the user's galloping top-left search and revises the predecessor lean (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-05 15:12:18 -07:00
committed by Edward Langley
parent 885e8eb469
commit 3698c68025
+43 -1
View File
@@ -1444,6 +1444,44 @@ analysis.*
The bottom-right corner is symmetric. An axis with **no** occupied position
answers "empty axis" under both, and the window is empty.
**The user's simpler procedure** (chat, relayed on 2026-10-05; on no PR), quoted
exactly as sent: "The simplest might be some kind of binary search for a new top left
corner: increasing jumps of the top left until you get a hit and then move down/right
until
You only have one new row column." *The author's reading:* the procedure belongs to the
client and uses forward requests only. The client moves its requested top-left up (or
left) in growing jumps until a request returns an occupied row before its old
top-left, then narrows back down (or right) until exactly one new row (or column) is
gained. A pruned top-left is no longer an edge case at all: the response simply starts
at a later position than the one asked for. *The rest is the author's analysis.*
- **What it requires of the response:** the **actual** absolute position of every
row and column returned, the first one included. The client compares those
positions with its old top-left, so this is T1 again, and it already holds under
clause 12. Nothing else is needed: no error, no snap rule, no direction in the
request, and no predecessor query.
- **Cost.** With doubling jumps, the gallop takes about log₂(gap) requests. A plain
binary search over the last interval takes about log₂(gap) more. At density 10^-4
the expected gap is about 10^4 rows, so that is 14 + 14 ≈ 28 forward requests,
against one predecessor request. The refinement is often much cheaper. A forward
response holds k occupied rows with their positions, so if the hit request's k rows
reach the old top-left, the client reads the last row before it directly, and the
search ends after the gallop, about 14 requests. Refinement is needed only when
more than k occupied rows lie between the hit and the old top-left. These figures
are arithmetic, not measured.
- **Statelessness and network transparency.** The host keeps nothing for the search.
Every request is an ordinary `project` from an absolute top-left, so the in-process
and remote consumers do exactly the same thing (T6). The cost is round trips: about
14 for a remote client and about 14 function calls for the TUI. For the TUI that is
nothing. Over a socket it is about 14 latencies for one scroll step into a sparse
stretch.
- **The author's lean, revised.** Take the user's procedure as the **baseline**. It
makes (a), (b) and (c) unnecessary, because a pruned top-left just returns a later
first position. Keep a model-side predecessor query only as an **optional
optimisation** for a remote client. It costs no state (see above) and collapses
the gallop into one request. Offer it once a measured round-trip cost shows it is
worth a second query direction. *As first written*, this item leaned to offering it
outright; that lean is withdrawn.
Cost at density 10^-4: finding the nearest occupied neighbour is one seek in the
ordered index, O(log n), in either direction, so (a) with named neighbours and (b)
cost the same on the host. (a) adds a round trip, and (b) adds none. The expected gap
@@ -2046,7 +2084,11 @@ revision, and none is renumbered.
predecessor search, which every option serves as cheaply as a successor search). It
also raises the edge case of a top-left that lands on a pruned row: fail and retry,
snap forward (which the user objects to: "the viewport getting stuck"), or snap toward
the scroll direction (clause 12). That edge case is
the scroll direction (clause 12). The user has since suggested a simpler client procedure:
gallop the top-left with forward requests until one hits, then narrow. A pruned
top-left then needs no special answer, provided responses report actual positions.
The author now takes that as the baseline, and keeps a predecessor query only as an
optional optimisation. That edge case is
part (d) of this question.
*Recommendation (the author's):* (a) yes, with (ii). (b) yes, once the user confirms
that pruning is the client's. (c) restate both in those terms, in ADR 008, since they