DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path inspection-briefs/loomworks-list-loading-step-0-report-v0_1.md

Loomworks list-loading — Step-0 inspection report v0.1

Document: loomworks-list-loading-step-0-report-v0_1 Version: v0.1 Status: complete — read-only inspection Date: 2026-06-27 By: Claude Code (CC) on DUNIN7-M4 Answers: loomworks-list-loading-step-0-inspection-brief-v0_1 Paired with: loomworks-list-loading-scoping-note-v0_1 (not present in the record; CC worked from the canonical queue + engine source directly) Queue item: Companion build queue Item 1 — list-loading (cross-cutting pagination)

> Read-only inspection. No code changed, no commits, no migrations. This report tells the CR-drafting chat the real engine delta before the CR is written.


0a — Item 1 canonical scope text

Canonical source: loomworks-record/current-status/loomworks-companion-build-queue-v0_3.md (dated 2026-06-27). This is the highest version of the build-queue lineage and is the single canonical queue — the sibling loomworks-companion-build-queue-and-list-loading-v0_1.md carries an explicit banner: "SUPERSEDED by loomworks-companion-build-queue-v0_3.md." So v0_3 governs; the "and-list-loading-v0_1" doc is preserved only for discovery-trajectory and must not be worked from.

Exact scope text of Item 1 (queue table row, v0_3 line 35):

> List-loading pattern (cross-cutting PAGINATION) — State: Scope next — "NOT 'loading states/skeletons' — that's item 4. The unbounded-growth pagination concern across EVERY surface. Pattern pre-decided (below). Engine extends its existing has_more/cursor pattern to room + engagement listings."

The settled pattern (v0_3 §"Item 1", lines 70–83), six rules:

  1. First page loads on mount (sensible page size per section; conversation used 50).
  2. total_count always visible — no silent truncation ("showing 50 of 2,000").
  3. Explicit "load more" control — not infinite scroll (settled Axis 11 §6 Q2).
  4. The control appears only when more exists — gated on engine has_more.
  5. Cursor is composite (created_at, id) — the Axis 11 R2 lesson (a created_at-only strict-< cursor can skip a turn sharing the boundary timestamp). Engine-side.
  6. Held-type / small working sets stay wholesale — pagination targets the unboundedly-growing sections only.

Surfaces Item 1 names (v0_3 §"Every surface it touches", lines 56–68) — this is more specific than the scoping note's reconstruction:

Did Item 1 name surfaces/endpoints the scoping note missed? Yes — Item 1 explicitly enumerates the Held exemption, the personal-memory backstop precedent, and the three distinct home lenses. It does not name route paths (the queue stays at the surface level) — supplying those is precisely the job of 0c below.

Is "loading states / skeletons" a separate sub-item? Confirmed yes. v0_3 makes this a first-class correction: it is Item 4 ("Loading STATES (home + rooms)"), explicitly not the list-loading concern. The v0.3 change log calls out that an earlier CC draft wrongly narrowed "list-loading" to skeletons/retry; v0.3 reverses that. The scoping note's treatment of skeletons as out of scope is correct.


0b — Seed and manifest version confirmations

| Item | Scoping-note assumption | Canonical reality | Delta | |---|---|---|---| | Seed | v0.12 | v0.12 confirmedloomworks-record/candidate-seeds/loomworks/loomworks-candidate-seed-v0_12.md, header Version. 0.12. Only one current seed present (earlier in archive/). | None. | | Manifest | v0.56 (stale mirror) | Highest present = v0.68 (current-status-manifest-v0_68.md). | 12 versions stale. |

List-loading / pagination decisions in the canonical manifest the v0.56 mirror would have missed: The governing record is Manifest Entry 109 — "Axis 11: CenterPane conversation pagination" (manifest v0.48, present in v0_68). Key facts the scoping note could not have had at full fidelity:

No other pagination decision is recorded in v0.57–v0.68 that bears on this scope.


0c — Listing-endpoint enumeration (the core)

Every read endpoint in loomworks-engine that returns a list of accumulating items, grouped by pagination posture. All routes verified against router source (src/loomworks/api/routers/ and src/loomworks/orchestration/routers/). All rows are pure reads (see 0e).

Group A — Already conformant to the target pattern (composite cursor + total_count + has_more-equivalent)

| Route | Returns | Unbounded? | Paginated today? | Cursor shape | total_count? | Sort | |---|---|---|---|---|---|---| | GET /me/dashboard/needs_you | held assertions + pending shapes + review reports (home "Needs you") | Yes | Yeslimit (1–100, def 50) + opaque cursor | Composite {ts,id} (base64 JSON) | Yes + next_cursor | surfaced_at DESC, item_id DESC | | GET /me/dashboard/recent | produced renders/artifacts (home "Recent") | Yes | Yeslimit + cursor | Composite {ts,id} | Yes + next_cursor | completed_at DESC, artifact_id DESC | | GET /me/dashboard/active | in-flight jobs | Yes | Yeslimit + cursor | Composite {ts,id} | Yes + next_cursor | started_at DESC, item_id DESC | | GET /operator/inbox | operator-vocab wrap of needs_you | Yes | Yes — inherits needs_you (limit + cursor) | Composite {ts,id} | Yes + next_cursor | surfaced_at DESC, item_id DESC |

These four already implement rules 1–5 exactly, composite cursor included. No engine work needed.

Group B — The reference endpoint (paginated, but created_at-only cursor; no total_count)

| Route | Returns | Unbounded? | Paginated today? | Cursor shape | total_count? | Sort | |---|---|---|---|---|---|---| | GET /operator/conversation-history | conversation turns | Yes | Yeslimit (1–100, def 50) + before (datetime) | created_at-only strict-< | No (returns has_more only) | created_at DESC, id DESC |

Returns ConversationHistoryResponse(turns, has_more). The ORDER BY is composite for stable ordering, but the cursor boundary uses created_at alone — the Axis 11 R2 gap. See 0d.

Group C — Partially paginated: limit/offset + total_count, but no cursor and no has_more

| Route | Returns | Unbounded? | Paginated today? | Cursor shape | total_count? | Sort | |---|---|---|---|---|---|---| | GET /engagements/{id}/assertions | assertions (Memory — Settled/In record) | Yes | Partiallimit (def 50, ≤200) + offset | Offset (not cursor) | Yes | COALESCE(payload→created_at, last_updated_at) DESC, object_id ASC | | GET /engagements/{id}/renders | renders (Rendering room) | Yes (Goosey at 13) | Partiallimit (def 50, ≤500) + offset | Offset | Yes | created_at ASC, object_id ASC |

total_count present (client can compute "more exists"), but the mechanism is offset, not the composite cursor rule 5 requires, and there is no has_more flag.

Group D — Partially paginated: limit/offset only (no total_count, no has_more)

| Route | Returns | Unbounded? | Paginated today? | Cursor shape | total_count? | Sort | |---|---|---|---|---|---|---| | GET /engagements/{id}/manifestations | manifestations (Manifestation room) | Yes | Partiallimit (def 50, ≤500) + offset | Offset | No | derived_at DESC NULLS LAST | | GET /engagements/{id}/shapings | shapings (Shaping room) | Yes | Partiallimit (def 50, ≤500) + offset | Offset | No | created_at DESC |

Group E — Wholesale: no pagination at all (the from-scratch engine deltas)

| Route | Returns | Unbounded? | Paginated today? | Cursor shape | total_count? | Sort | |---|---|---|---|---|---|---| | GET /engagements | engagement summaries (home "Your engagements") | Yes (grows with memberships) | No — no limit/offset/cursor | — | No | e.id | | GET /engagements/{id}/assertion-groups | assertion groups (Memory grouping) | Yes | No — wholesale | — | No | snapshot dict order (unspecified) | | GET /engagements/{id}/considerations | considerations (Memory-adjacent) | Yes | No — filters only (triggering_reason, current_stage, terminal_state, since) | — | No | last_updated_at DESC | | GET /engagements/{id}/shape-events | shape events (Shaping production runs) | Yes | No — filters only (declared_shape_type_id, state, version range, self_consumer) | — | No | engagement_version_at_production ASC, object_id ASC | | GET /engagements/{id}/compositions | render compositions (Rendering pipeline) | Yes | Nostate filter only | — | No | created_at DESC |

Group F — Bounded / small: wholesale is correct (not deltas)

| Route | Returns | Why wholesale OK | |---|---|---| | GET /engagements/{id}/assertions/{aid}/history | one assertion's versions | bounded by revision count | | GET /engagements/{id}/assertions/{aid}/relationships | one assertion's relationships | structurally small | | GET /engagements/{id}/contributions/supported-types | registered extraction types | registry-bounded | | GET /engagements/{id}/considerations/dismissal-patterns | aggregated report | one row per trigger | | GET /engagements/{id}/declared-shape-types | shape-type config | config-bounded, small | | GET /engagements/{id}/declared-render-types | render-type config | config-bounded, small | | GET /operator/dashboard | preview aggregator | hardcoded _PREVIEW_LIMIT = 10 + surfaces *_count; intentionally a bounded preview |

Home-consumption note (frontend, flagged for the CR chat): The home's "Needs you" and "Recent" lenses are backed by distinct endpoints — /me/dashboard/needs_you and /me/dashboard/recent (Group A, fully conformant) — not by filtered views over /engagements. But /operator/dashboard (Group F) is a preview aggregator that calls those sections with limit=10, cursor=None and returns only counts. If the built home currently consumes /operator/dashboard, then making those two lenses page is a frontend re-wire (switch to the paginated section endpoints + add the load-more control) — not engine work. Which endpoint the home consumes today is a frontend (loomworks OL repo) question outside this engine inspection; the CR chat should confirm it before sizing the home lenses.


0d — Composite-cursor status on the reference

GET /operator/conversation-history cursor is still created_at-only. It has NOT been upgraded to composite (created_at, id).

Verified in src/loomworks/orchestration/routers/conversation_history.py:

Consequence for the CR: the composite-cursor work in rule 5 includes fixing the reference endpoint itself, not only the new ones. This is the Axis 11 R2 follow-on (manifest Entry 109), still open. It is the single endpoint where the manifest already pre-acknowledges the engine debt.


0e — Classification check (pure-read boundary)

Every unbounded listing endpoint that currently returns wholesale is a pure read. Confirmed per handler:

| Endpoint | Writes? | Derives? | Touches provenance/FORAY? | Classification | |---|---|---|---|---| | GET /engagements | No | No | No | pure read (SELECT engagements ⨝ memberships) | | GET /…/assertion-groups | No | No | No | pure read (snapshot over memory_events) | | GET /…/considerations | No | No | No | pure read (SELECT current_memory_objects) | | GET /…/shape-events | No | No | No | pure read (SELECT shape_events_view) | | GET /…/compositions | No | No | No | pure read (SELECT render_compositions_view) |

The partially-paginated reads (assertions, renders, manifestations, shapings) and the conformant dashboard reads are likewise pure SELECT-and-resolve handlers — no writes, no derivation, no provenance thread contact on the GET path. No listing endpoint does more than read.

Flag: none. Every endpoint in scope carries the same Memory-read-boundary classification Axis 11 carried — a pure-read, no-Memory-contact, consumer/reader-surface capability. The eventual CR inherits Axis 11's classification cleanly.


Bottom line — the real engine delta

The scoping concern was: how many listing endpoints already paginate vs. need engine pagination added. The answer is not binary — the engine sits at three tiers:

So the real engine delta is ~10 endpoints touched (5 new-pagination + 5 conform-upgrade), governed by one decision: standardize on composite (created_at, id) cursor + has_more + total_count, including retrofitting the reference endpoint the prior phase deliberately left as a follow-on. This is materially larger than Axis 11's frontend-only footprint — it is a genuine engine CR, not a frontend skin — but every endpoint is a pure read, so it carries Axis 11's Memory-read-boundary classification without a four-room production change. The held/small sets (Memory Held, the Group F bounded reads) stay wholesale per rule 6 and are explicitly out of the delta.


DUNIN7 — Done In Seven LLC — Miami, Florida Loomworks list-loading — Step-0 inspection report — v0.1 — 2026-06-27 Read-only inspection. Halt after report — CR not drafted (a fresh Claude.ai chat drafts it with this report in hand).