Skip to content

feat(gardener): phase G-h — kind tabs + pagination, group-paged near-duplicates (gh-#657) - #659

Merged
genwave-radio merged 5 commits into
mainfrom
feat/gardener-g-h
Sep 1, 2026
Merged

feat(gardener): phase G-h — kind tabs + pagination, group-paged near-duplicates (gh-#657)#659
genwave-radio merged 5 commits into
mainfrom
feat/gardener-g-h

Conversation

@genwave-radio

Copy link
Copy Markdown
Collaborator

📄 Phase G-h — the Gardener queue at scale (gh-#657 · closes #654, #655, #657)

Stacked on #658 (the pending specs — merge it first and this PR shrinks to the three build commits).

What ships

  • T385 2f39410 — store: kind-scoped paging with exact totals; near-duplicates page by whole group (distinct group_key asc). State scopes which groups qualify, never which members render; dismissed members render, resolved members don't (a stale group_key must never let Keep-this-one bulk-ineligible an ex-duplicate — proven live in review, twice, from both directions). 17 real-Postgres facts.
  • T386 83ecada — wire: kind-scoped GET /api/gardener/findings carries total (groups for near_duplicate, rows otherwise) via RotFindingPage.Total: int? — the store owns the distinction, the controller projects it. Un-scoped responses byte-compatible with the old shape (pinned to exactly ["groups"]). 9 wire facts over real HTTP.
  • T387 e50e51a — the page, rebuilt on the catalog idiom: server-rendered ?tab=&page=&limit=, five badged kind tabs, "Page N of M" from total, size picker 25/50/100/250 (default 25, URL-only), verbs via router.refresh(). The gh-gardener: flat 1000-row findings page ordered by kind starves every later kind to zero rows #654 flat-fetch starvation defect is deleted with GardenerView; gh-gardener: "Purge unavailable…" button label reads as "purge is unavailable" #655's label folds in ("Purge dead tracks…"). Pager extracted to components/ui/pager.tsx (catalog converted, markup byte-identical). Page numbers clamp inside Int32 — paging params are hints, never 400s.

Review + verification

7 opus review rounds across the three tasks (2 FAIL + surgical r3/r4 on T385; 1 PASS + LOWs on T386; 1 FAIL + 1 PASS on T387) — every finding re-proven closed on live Postgres or in the rendered DOM, never taken from the report. Suites at the tip: MediaLibrary 1156, Host 2488, Core 181, Architecture 119, Context/Orchestration/Tts green, admin-ui Jest 1215/0, tsc/eslint/next build clean. Browser-smoked against the production binary: badges, both pages, picker, tab-switch semantics, and the whole-cluster card showing the dismissed member with the resolved member absent.

SPEC riders: F153.9/F153.10 (2026-08-31) + the two T385-review amendments (dismissed/resolved member semantics; qualification symmetry).

After merge: v5.5.1 pins+changelog PR on your word (T388) — Abstractions stays 5.5.0.

https://claude.ai/code/session_01716w9dNYBWSbmuchPQep8A

…licates page by whole group (STORY-383, gh-#657)

IRotFindingStore.ListWithMediaAsync returns RotFindingPage (Items + Total).
Flat kinds page rows with an exact row total; near_duplicate limit/offset
count distinct group_keys (asc, NULLs excluded) and return every non-resolved
member of each selected group — state scopes which groups qualify, never
which members render, and qualification applies the same resolved-exclusion
as rendering, so no group can qualify while rendering zero rows. Dismissed
members render (still duplicates); resolved members don't (system closed
them — a stale group_key must not let Keep-this-one bulk-ineligible a track
that is no longer a duplicate). Kind-less read keeps the T377 shape verbatim;
controller change is mechanical only — total reaches the wire at T386.

4 opus review rounds; every finding re-proven closed on live Postgres.
Story383: 17 facts green. Smoked through the production binary on :5236.

Claude-Session: https://claude.ai/code/session_01716w9dNYBWSbmuchPQep8A
…the wire (STORY-382, gh-#657)

RotFindingPage.Total becomes int? — null iff the read was kind-less, so the
controller's `page.Total is int total` gate emits { groups, total } for
kind-scoped calls and the byte-compatible T377 { groups } shape otherwise;
the store stays the single writer of the distinction. Totals unit: groups
for near_duplicate, rows for every other kind. 400/clamp posture untouched.

Story382 wire specs un-pinned: 9 facts over real HTTP (WebApplicationFactory
+ ephemeral Postgres), incl. the gh-#654 un-scoped starvation pin and the
exactly-["groups"] shape pin. Opus review PASS + 4 LOWs folded. Smoked
through the production binary: near-dup total counts groups (2, not 4 rows).

Claude-Session: https://claude.ai/code/session_01716w9dNYBWSbmuchPQep8A
…: kind tabs + pagination (STORY-381/382, gh-#657; closes gh-#654, gh-#655)

Server-rendered /gardener reads ?tab=&page=&limit= — five badged TabStrip
kind tabs, per-tab kind-scoped fetch parallel with /api/status, "Page N of M"
from the response total (groups for near-duplicates), size picker 25/50/100/
250 default 25, URL-only. GardenerView's client fetch-on-mount (the gh-#654
starvation) is deleted; verbs refresh via router.refresh(). Purge trigger
reads "Purge dead tracks…" (gh-#655). Page number clamps so the derived
offset stays inside Int32 — paging values are hints, never 400s. The pager
is extracted to components/ui/pager.tsx and the catalog converts to it
(markup byte-identical). Status-fetch failure degrades to unbadged tabs;
near-duplicate suppresses its fallback count rather than unit-swapping.

2 opus rounds (4 MED + 5 LOW folded). Jest 1215/0; tsc, eslint, next build
clean. Browser-smoked against the production binary: badges, both pages,
picker, tab-switch semantics, whole-cluster card with the resolved member
absent and the dismissed member present.

Claude-Session: https://claude.ai/code/session_01716w9dNYBWSbmuchPQep8A
# Conflicts:
#	admin-ui/__specs__/gardener-pagination.spec.tsx
#	admin-ui/__specs__/gardener-tabs.spec.tsx
@genwave-radio
genwave-radio merged commit d5e1a53 into main Sep 1, 2026
11 checks passed
@genwave-radio
genwave-radio deleted the feat/gardener-g-h branch September 1, 2026 00:01
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 1, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gardener: flat 1000-row findings page ordered by kind starves every later kind to zero rows

1 participant