Review: Tenant-scoped Drizzle query helper (v2)
Verdict: NEEDS_REFINEMENT
Re-review of
review-1091-2026-04-25. Previous verdict was NEEDS_REFINEMENT with two recommendations: [SCOPE] resolve arch label mismatch, [BODY] pin Lineage to explicit issue numbers.Changes Since v1 Review
- [x]
[BODY]Lineage pinned — issue body now reads "Hard depends onforgejo_admin/westside-admin#1(Drizzle setup ...). Hard depends onforgejo_admin/westside-admin#6(scaffolding)." Related section also adds "DEPENDS ON:#6,#1". Resolved. - [~]
[SCOPE]Arch label changed fromarch:scopeddb-helpertoarch:scoped-db. Sibling ticket #1089 (Drizzle setup) also carriesarch:scoped-db, so the two helper-layer tickets now share one arch label cleanly. Partially resolved — the label is consistent across the helper layer, but no backingarch-scoped-dbnote exists.
Template Completeness
- [x] Type — Feature
- [x] Lineage — explicit issue numbers (#1, #6)
- [x] Repo — forgejo_admin/westside-admin
- [x] User Story — story-westside-admin-admin-row-crud
- [x] Context — clear motivation, swap-point design, lint enforcement rationale
- [x] File Targets — Create + Modify + Do NOT modify lists present
- [x] Acceptance Criteria — 6 ACs, behavioral + type-system + lint + comment
- [x] Test Expectations — unit + integration described
- [x] Constraints — no JWT-derived tenant_id (v1), Drizzle typed builders only
- [x] Checklist — PR / ACs / lint verification
- [x] Related — project page + arch note + DEPENDS ON line
Traceability
- [x] story:admin-row-crud label — present
- [x] story note verified — found in
project-westside-adminuser-stories table (story-westside-admin-admin-row-crud, Admin role, "Zero psql UPDATEs / zero fix-X texts over 30 days") - [x] arch:scoped-db label — present (was
arch:scopeddb-helperin v1; now matches sibling #1089) - [ ] arch note MISSING —
[SCOPE]Noarch-scoped-dbnote exists in pal-e-docs (verified viasearch_notesqueries "arch-scoped-db" and "scoped-db westside-admin", both empty). The project-westside-admin Architecture section lists exactly three arch notes:arch-domain-westside-admin,arch-dataflow-westside-admin,arch-deployment-westside-admin. The issue's Related section still points toarch-dataflow-westside-admin. The arch label and the issue's Related link disagree. Two clean resolutions: (a) createarch-scoped-db-westside-adminas a sub-component note nested underarch-dataflow-westside-admin— this matches the sibling-ticket pattern and gives the helper layer a real architecture home, or (b) relabel both #1091 and #1089 toarch:dataflow-westside-adminand dropscoped-dbas a phantom component. Recommend (a):scoped-dbis a real architectural seam (the only legal entry point to the database from page-server code), worth its own note. - [x] type:feature label — present
- [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/forgejo_admin/westside-admin/issues/3 — open
File Targets
Repo
forgejo_admin/westside-admin still contains only README.md on main (verified via Forgejo contents API). Every File Target is part of the dependent ticket chain (#6 scaffolding → #1 Drizzle setup → this ticket). Cannot be live-verified; scoped relative to post-#1 tree.- [~]
src/lib/server/tenant.ts(Create) — path well-formed, plausible after #1 - [~]
src/lib/server/scopedDb.ts(Create) — path well-formed - [~]
src/lib/server/db.ts(Modify) — issue #1 explicitly creates this; consistent - [~]
package.json(Modify) — root path, exists after scaffolding #6
Acceptable for backlog→todo. Dev agent must re-verify after #6 + #1 land.
Repo Placement
OK. All file targets in
westside-admin; matches issue's Repo header. No multi-repo fan-out.Dependencies
- Hard blocker: #1 (Drizzle setup, board item #1089, backlog) — creates
db.tsandschema.ts. Now explicitly named in Lineage. - Hard blocker: #6 (scaffolding, board item #1087, backlog) — creates SvelteKit shell. Now explicitly named in Lineage.
- Downstream: #2 hooks-server (#1090), #4 page-server tables (#1092), #5 page-server rows (#1093, already in todo) all consume
scopedDb. Note: #1093 is intodocolumn despite depending on this ticket which is stillbacklog— that's a board ordering question for Ava, not this ticket's concern. - Documented? Yes — Lineage now pins both upstream issues by number.
Acceptance Criteria
Unchanged from v1, all 6 ACs agent-verifiable:
- AC1–AC2: SQL output assertions — verifiable via snapshot test of generated query
- AC3: TypeScript type error on system-table misuse — verifiable via
expectTypeOfortsc --noEmit - AC4: CI lint check fails on direct
dbimport — verifiable by running lint on fixture - AC5: 6+ unit tests — quantitative
- AC6: multi-line swap-procedure comment in
tenant.ts— verifiable via grep
Integration test (URL/form-body tenant_id mutation still produces correct WHERE clause) remains the strongest data-leak guarantee. Solid.
Blast Radius
Unchanged from v1:
- First Drizzle consumer in the cluster — pattern set here informs future adoption
- Downstream rebases on #1090, #1092, #1093 if API shape is wrong; agent should sanity-check Drizzle's actual builder API before locking signatures
- Grep-based lint is brittle to renames/re-exports; acceptable for v1
TENANT_ID = 1in TS file works for v1; swap-point comment is the discipline
Decomposition Assessment
4 file targets (2 create + 2 modify), 6 ACs, ~6 unit tests + 1 integration test. Estimated agent work 30–60 min one-pass. No decomposition needed. Within 5-minute-rule envelope.
Recommendation
[SCOPE]Createarch-scoped-db-westside-adminnote (or chosen slug) underarch-dataflow-westside-admin. Document: helper API surface (scopedDb+systemDb), tenant-table vs system-table split, lint-enforced single entry point,TENANT_IDswap procedure. Same note backs sibling ticket #1089. Once created, also align board labels and the issue's Related section so they reference the new arch note instead of (or alongside)arch-dataflow-westside-admin. Ava's call on slug + whether to relabel.
One unresolved
[SCOPE] from v1 remains. The [BODY] Lineage fix landed cleanly. Ticket otherwise reads READY — strong scope, crisp ACs, well-defined seam. Once the arch note exists (or Ava decides the label is purely organizational and waives the requirement), this flips to APPROVED.