DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path phases/list-loading-cr-2/loomworks-list-loading-cr-2-implementation-notes-v0_1.html

Loomworks list-loading — CR-2 implementation notes — v0.1

Document: loomworks-list-loading-cr-2-implementation-notes-v0_1 Version: v0.1 Status: complete — as-built record of CR-2's build Date: 2026-06-27 CR: loomworks-list-loading-cr-2-frontend-load-more-v0_5 (final) Engine contract: CR-1 merged at eeae9c2 / alembic 0096 Branches (built, not yet pushed at time of writing): engine list-loading-cr-2-page-size @ e5bbf05; OL list-loading-cr-2-frontend-load-more @ 376b098


What CR-2 built

The frontend half of list-loading: a reusable explicit load-more control consuming CR-1's keyset contract, applied to every list surface that exists today, plus per-surface page-size defaults and a voice-adjustable global override. Built across six steps, split per surface for blast-radius discipline. Two surfaces deferred to named engine dependencies.

The commits, in order

Step Commit What
1 — engine setting e5bbf05 (engine branch) lists.page_size — nullable bounded [10,100] on the three-place pattern; shared numeric tune path made None-safe (zero regression). Engine suite 3041/0/63.
2 — reusable spine 5973ff8 lib/pagination.ts + hooks/usePagedList.ts (append/prepend, dedup, caller-resolved pageSize) + components/lists/LoadMoreControl.tsx (explicit, has_more-gated, count shown). 12 isolation tests.
3 — renders 4eb517b RenderingRoom on the spine. D-4 land-on-oldest (forward-only ASC keyset). 5 files / 28 tests.
3 — Memory d7fa2b6 MemoryRoom: two usePagedList instances (held + settled) over one state-filtered endpoint (?state=held|committed). Confirm-act reloads both. 5 files / 25 tests.
3 — engagements DEFERRED — structural blocker (see below).
4 — home lenses e3f401d needs_you + recent stop discarding next_cursor, wired to the spine; applySettingChange lists.page_size branch (the third settings place).
5 — defaults + override 5406c71 (with Step 4) Per-surface defaults (needs_you 15, recent 40, Memory 15, Rendering 15) via useListsPageSize() ?? <default>; override uniformises + clears, test-pinned; store coerces garbage → null.
6 — CenterPane 20dcaf7 conversation-history before → composite cursor; CenterPane adopts the shared spine (prepend mode); dedup guard kept as belt-and-braces (R2 now engine-handled). total_count surfaced against loadedCount not turns.length.
6 follow-up — count suppress 376b098 LoadMoreControl gained showCount? (default true); CenterPane passes showCount={false} — conversation reads clean, the four list surfaces keep their count.

Final OL suite: 79 files / 415 tests green. Final engine suite (Step 1 branch): 3041/0/63.

Decisions resolved during the build

D-4 (renders direction) — land-on-oldest. The lean was land-newest/page-older. Build found the renders endpoint sorts created_at ASC and pages forward — a forward-only keyset cannot land on the newest page. So page 1 = oldest, load-more = newer. Matches what RenderingRoom showed before; no display change. Mechanics governed the lean.

D-3 (shared control) — extract, CenterPane adopts. Built the reusable control to the general list shape; CenterPane (the precedent it was extracted from) adopted it at Step 6 in prepend mode, keeping its dedup guard as a CenterPane-level concern. The precedent became the reusable thing.

Count line on conversation — suppressed. "Showing N of TOTAL" earns its place on lists (completeness is a real question); a conversation is a stream to read, so the count is suppressed there while kept on the four list surfaces.

R2 closed end-to-end

CR-1 added the composite (created_at, id) cursor to close the boundary-tie skip the Axis 11 phase left open. CR-2 Step 6 is where it was proven on the surface: the new test shows load-earlier prepends older turns, dedups the boundary tie, and rides the real opaque composite cursor (&cursor=, never &before=, pinned by converse.test.ts). The dedup guard that used to do that work is now redundant, demoted to belt-and-braces. The R2 thread — open since the Axis 11 phase — is closed.

Two named follow-ons (deferred, with dependencies)

The home engagements list — cannot be paginated until an engine visibility='automatic' projection exists. The home "Your engagements" list is a client-side-derived zone: useHomeData fetches the whole /engagements set and classify() splits rows into zones (Personal, Shared/commons, Administrative-hidden, Yours) from per-row signals, needing the complete set. /engagements sorts DESC, so Personal/commons are oldest → would land on last pages → zones would mis-classify under pagination. No zone-scoped endpoint exists to page. Deferred; the home-list pagination is the waiting consumer of the visibility projection. 7 engagements today — no pressure.

The six roomless endpoints (considerations, compositions, shape-events, assertion-groups, manifestations, shapings) — no frontend consumer; their rooms are empty stubs. Paginate when the rooms are built. The reusable spine is ready, so each is a wire-up. Note: compositions uses item-key items (the Step-0 A outlier); the spine's item-key prop handles it.

Notes for the next builder


DUNIN7 — Done In Seven LLC — Miami, Florida Loomworks list-loading — CR-2 implementation notes — v0.1 — 2026-06-27 As-built trace. CR-2 frontend load-more built at all buildable surfaces; engagements + six roomless endpoints deferred to named engine dependencies. Not a regression source for the remember-about-me confirmation-loop bug (separately scoped).