Per-Series detail page, keyed by the composite the rest of the system uses #144

Closed
opened 2026-08-21 16:39:23 +07:00 by sulthan · 1 comment
Owner

Parent

Spec #134.

What to build

A page per Series whose address is derivable from a row the owner is already looking at, so a Series in the list is one hop from its own page. The key is the composite site-and-series-id in one path segment, matching the shape the wire and the store read already use -- a surrogate id would need a column and a migration to save nothing, the colon is legal unescaped in a path segment, and no observed series id carries a slash.

The page holds: a back ghost, a 28px display title, a mono key line carrying the key, the Site and the Library, a 160px hatched cover, and an uppercase mono meta row carrying the marks (pending, sighting-raised, no cover, unpollable, orphan). Then a two-column grid reserved for the intervention forms later tickets add, so those tickets add a form rather than reshaping the page.

Everything shown is a Series-level fact plus an anonymous Reader count. The owner never learns which Reader reads what, and never sees a Reader's progress -- the boundary is enforced by the shape of the type the store returns, not by this template happening not to print a field.

The only entry point into admin stays the owner-gated link in the application chrome. No per-Series admin link from the library cards: that is a second gated branch in the reading templates for a hop the Series list already provides.

Acceptance criteria

  • The page renders for a key taken verbatim from a Series list row
  • An unknown key answers 404, and the route is owner-gated through the route list rather than by a check inside the handler
  • The meta row renders each mark when the underlying fact holds and omits it otherwise
  • The Reader count renders as a number and no Reader identity or progress appears anywhere in the response
  • The two-column intervention grid exists and is empty
  • No link into admin is added to the reading templates
  • The page carries no refresh timer and no ember token
  • go test ./... green

Blocked by

  • #138 — Split the admin surface into four bookmarkable pages with a nav row
  • #140 — Cross-Series admin read model, with the privacy boundary in the projection
## Parent Spec #134. ## What to build A page per Series whose address is derivable from a row the owner is already looking at, so a Series in the list is one hop from its own page. The key is the composite site-and-series-id in one path segment, matching the shape the wire and the store read already use -- a surrogate id would need a column and a migration to save nothing, the colon is legal unescaped in a path segment, and no observed series id carries a slash. The page holds: a back ghost, a 28px display title, a mono key line carrying the key, the Site and the Library, a 160px hatched cover, and an uppercase mono meta row carrying the marks (pending, sighting-raised, no cover, unpollable, orphan). Then a two-column grid **reserved for the intervention forms later tickets add**, so those tickets add a form rather than reshaping the page. Everything shown is a Series-level fact plus an anonymous Reader count. The owner never learns which Reader reads what, and never sees a Reader's progress -- the boundary is enforced by the shape of the type the store returns, not by this template happening not to print a field. The only entry point into admin stays the owner-gated link in the application chrome. **No per-Series admin link from the library cards**: that is a second gated branch in the reading templates for a hop the Series list already provides. ## Acceptance criteria - [ ] The page renders for a key taken verbatim from a Series list row - [ ] An unknown key answers 404, and the route is owner-gated through the route list rather than by a check inside the handler - [ ] The meta row renders each mark when the underlying fact holds and omits it otherwise - [ ] The Reader count renders as a number and no Reader identity or progress appears anywhere in the response - [ ] The two-column intervention grid exists and is empty - [ ] No link into admin is added to the reading templates - [ ] The page carries no refresh timer and no ember token - [ ] `go test ./...` green ## Blocked by - #138 — Split the admin surface into four bookmarkable pages with a nav row - #140 — Cross-Series admin read model, with the privacy boundary in the projection
sulthan added the ready-for-agent label 2026-08-21 16:39:23 +07:00
sulthan self-assigned this 2026-08-21 19:23:27 +07:00
Author
Owner

Merged into spec-134 as f5b52e4 (branch ticket/144-series-detail, commits 14990bd, 09a094c).

Per-Series detail page at GET /admin/series/{key}, rendering every fact the admin read model holds plus the anonymous Reader count, 404 on an unknown key. The intervention grid ships empty by design — #146 fills it.

One brief correction worth recording: the brief said the detail page should reuse Page: "series", which cannot work, since a single {{if eq .Page}} chain takes the first matching branch and the Series list already owns that value. Resolved with a distinct series-detail page value and the Series nav link widened to (or (eq .Page "series") (eq .Page "series-detail")), so the tab still underlines. Agreed live with the #142 implementer before either landed.

Known shortcut, marked in-tree with a ponytail: comment: the detail row is found by walking the Site-narrowed pages of SeriesPage rather than by a keyed read, because adding a store method here would have collided with #146's edits to the same package. Upgrade path is a keyed read alongside SeriesPage when the library outgrows it.

Review: spec axis one Important (page-1-only lookup, fixed with a regression test) and two Minors; standards axis clean.

go test ./... green on the merged base.

Merged into `spec-134` as f5b52e4 (branch `ticket/144-series-detail`, commits 14990bd, 09a094c). Per-Series detail page at `GET /admin/series/{key}`, rendering every fact the admin read model holds plus the anonymous Reader count, 404 on an unknown key. The intervention grid ships empty by design — #146 fills it. One brief correction worth recording: the brief said the detail page should reuse `Page: "series"`, which cannot work, since a single `{{if eq .Page}}` chain takes the first matching branch and the Series list already owns that value. Resolved with a distinct `series-detail` page value and the Series nav link widened to `(or (eq .Page "series") (eq .Page "series-detail"))`, so the tab still underlines. Agreed live with the #142 implementer before either landed. Known shortcut, marked in-tree with a `ponytail:` comment: the detail row is found by walking the Site-narrowed pages of `SeriesPage` rather than by a keyed read, because adding a store method here would have collided with #146's edits to the same package. Upgrade path is a keyed read alongside `SeriesPage` when the library outgrows it. Review: spec axis one Important (page-1-only lookup, fixed with a regression test) and two Minors; standards axis clean. `go test ./...` green on the merged base.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sulthan/mangaBookmark#144