Replies: 1 comment
|
Branch: Cross-link: this is part of an emerging Things UI cluster with #3998 (Things Listing Enhancements) and #4005 (TableBase Coverage Dashboard & Enrichment Queue). #3945 covers the universal record inspector (detail page); #3998 covers the listing/index side; #4005 covers the coverage dashboard. Consider treating these as siblings of one Things UI epic. — Discussion review 2026-04-08. |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Things UI — Universal Record Inspector
TL;DR
Every record in the system needs a discoverable detail page. Currently, 12 of 20 thing types have no dedicated page, and record IDs in entity-profile are only clickable when a source-check verdict exists. We'll build
/things/:idas a universal record inspector showing thing metadata, source-check verdicts, and navigation links — with a minimal/thingssearch page as the index. Phase 1 (detail page + entity-profile linking) delivers the core value in 1-2 sessions. The previous things dashboard was deleted as redundant; our dashboard phase is deliberately minimal.Problem
The
thingstable is a universal index across all 20 domain types (~10K-50K rows), but it has no UI. Record IDs in the entity-profile viewer (E1929) are only clickable when a source-check verdict exists (PR #3938). Join-table types (personnel, investment, equity-position, etc.) have no dedicated page —thingHref()returnsnullfor 12 of 20 types. An editor reviewing entity data cannot click through to inspect individual records, verify their accuracy, or see what data the system holds.User stories:
/things/:idshows both.Current State
thingsPG table/api/things/*)GET /api/things/:id/api/source-checks/verdicts/:type/:idtablebase_audit_logthingHref()nullfor 12Proposed Approach
Two pages, minimal server work:
/things/:id— Universal record detail page. Fetches the thing fromGET /api/things/:id(existing). Fetches source-check verdicts fromGET /api/source-checks/verdicts/:type/:id(existing). Shows thing metadata, verdict summary, navigation links. For things with dedicated pages, shows prominent "View full page" link. For things without pages, this IS the destination./things— Minimal search/index page. A search box + type filter that queries the existing/api/things/searchand/api/things/endpoints. Each result links to/things/:id. Deliberately minimal — not a full dashboard with stats bars and filter tabs (those were deleted as redundant).Phase 2 enhancement: Add
record-lookupendpoint to fetch full source record fields (TABLE_MAP pattern). This turns the detail page from "thing metadata + verdicts" into "full row data + verdicts" — significantly more useful for debugging.Key Decisions
URL uses
things.id(PK), no fallback neededthings.id!=things.sourceIdfor entities (id=stableId, sourceId=slug) and resources./things/:id. For the 12 types without dedicated pages (personnel, investment, equity-position, benchmark-result, division-personnel, policy-stakeholder, entity-event, entity-assessment, publication, political-race, race-candidate, fact),things.idalways equalsthings.sourceId(both are the source table PK). So a simple PK lookup viaGET /api/things/:idis sufficient — no fallback mechanism needed.Link to dedicated pages when they exist,
/things/:idonly as fallbackthingHref()or existinggetRecordHref()) when one exists. For the 12 types returningnull, link to/things/:id.Keep
thingHref()returningnullfor join-table typeshref === null. Changing this would add ~5K+ join-table records to search results, diluting quality./things/:idis discoverable through entity-profile links and direct URL, not through Cmd+K.Defer TABLE_MAP/record-lookup to Phase 2
GET /api/things/:id) + verdicts. This is sufficient for an MVP.record-lookupendpoint with a TABLE_MAP whitelist validated by Zod enum.Dashboard deliberately minimal
Architecture
apps/web/src/app/things/[id]/page.tsxapps/web/src/app/internal/entity-profile/entity-profile-viewer.tsx/things/:idfor types without dedicated pages (currently 12 types link nowhere)apps/web/src/app/source-checks/source-checks-shared.tsxgetRecordHref()to return/things/:idfor types that currently returnnullapps/wiki-server/src/routes/tablebase/record-lookup.tsGET /:sourceTable/:sourceIdwith TABLE_MAP whitelist + Zod validation. Returns full source record row + entity name resolution. ExportsRecordLookupRoutetype.apps/wiki-server/src/app.tsapps/web/src/app/things/[id]/page.tsxapps/web/src/app/things/page.tsxapps/web/src/app/things/things-table.tsx"use client"component with search, type filter, paginationcontent/docs/internal/things-dashboard.mdxapps/web/src/components/mdx-components.tsxapps/web/src/lib/wiki-nav.tsapps/web/src/app/internal/things/page.tsx/wiki/E<new>Implementation Phases
Phase 1: Detail page + entity-profile linking — S/M (1-2 sessions)
Goal: Record IDs in entity-profile become clickable for the 12 thing types that currently have no page. Detail page shows thing metadata and source-check verdicts.
/things/[id]/page.tsx— server component using existingGET /api/things/:idandGET /api/source-checks/verdicts/:type/:idGET /api/things/:id— no sourceId fallback needed (id==sourceId for all 12 types without pages)decodeURIComponent()on params (copy source-checks pattern)robots: { index: false },revalidate = 300(shorter than 3600 for debug tool freshness)getRecordHref()insource-checks-shared.tsx: return/things/${recordId}for thedefaultcase (types that currently returnnull)/source-checks/URL construction withgetRecordHref(section.recordType, idStr)— returns dedicated page for 8 types,/things/:idfor 12 types. Remove verdict-gating on the ID link (currentlysection.recordType && verdict ?— should besection.recordType ?). Keep verdict-gating on the SourceCheckDot only.InferResponseType<>from Hono RPC with the existingThingsRoutetype export for type-safe API responses (per mandatory RPC pattern)page.test.tsx: render with full data, null fields, no verdictsQuality gates:
pnpm buildpasses,pnpm testpasses. Verify: (1) personnel thing detail renders, (2) entity thing shows "View full page" link to wiki, (3) thing with no verdict shows "Not yet checked", (4) entity-profile record IDs are clickable for personnel/investment/equity-position types.Exit criteria: Navigate from entity-profile → click any record ID → see thing detail or dedicated page.
Phase 2: Full source record display — S (1 session)
Goal: Detail page shows all columns from the source table, not just thing-level metadata.
record-lookup.ts— Hono RPC route with method-chaining,export type RecordLookupRoute{ personnel, grants, funding_rounds, investments, equity_positions, divisions, division_personnel, funding_programs, benchmarks, benchmark_results, publications, policy_stakeholders, entity_events, entity_assessments, research_area_organizations }. Entities, facts, and resources excluded (they have dedicated pages/different PK schemes).GET /:sourceTable/:sourceId— query source table, return cleaned row + resolved entity namesapp.tsrecord-lookup.test.ts: test 3-4 table types, test unknown table → 400, test entity name resolutionQuality gates:
pnpm buildpasses,pnpm testpasses. Verify detail page shows full personnel row, full grant row, entity refs link correctly.Exit criteria:
/things/:idshows complete source record with all columns.Phase 3: Search/index page — S (1 session)
Goal: Browsable entry point for discovering things by search and type filter.
pnpm crux tb ids allocate things-dashboardthings/page.tsx(redirect) +things-table.tsx(client component)GET /api/things/search, type filter dropdown, paginated list →GET /api/things//things/:idQuality gates:
pnpm buildpasses. Dashboard loads with data, search returns results, type filter works, pagination works.Exit criteria: Dashboard browsable at
/wiki/E<new>.Scope Cuts
thingHref()changes — Join-table types continue to returnnullin search. Things discoverable only via entity-profile links./source-checksrename — Issue Rename /source-checks to /records — unified record detail page #3923 deferred. Independent routes, zero breakage.childrenCountas a stat with link to entity-profile.Quality & Verification Infrastructure
Tests:
apps/web/src/app/things/[id]/__tests__/page.test.tsx— Phase 1: render with full data, null fields, no verdicts, entity thing (shows "View full page"), personnel thing (no dedicated page)apps/wiki-server/src/__tests__/record-lookup.test.ts— Phase 2: test personnel, grants, unknown table → 400, entity name resolutionMigration verification: N/A — no schema changes.
UI verification:
/things/<personnel-thing-id>— shows thing metadata + verdicts (or "Not yet checked")/things/<entity-thing-id>— shows "View full page" link to wiki page/things/<thing-with-colon-id>— URL encoding works correctly/things/:idDeploy steps: No new env vars. No manual migrations. No post-deploy checks beyond standard CI.
Documentation: Update
.claude/rules/internal-dashboards.mdto list Things Index after Phase 3.Monitoring:
revalidate = 300. If wiki-server is down, pages show error state (same pattern as source-checks).Risks & Mitigations
things.id!=things.sourceIdfor entities/resources — entity-profile linking breaks/things/:idfor types without pages. Verified: for all 12 such types,id == sourceIdalways holds. No fallback needed./source-checks/URLs/things/:idis independent.encodeURIComponent()on build,decodeURIComponent()on read.Open Questions
/things/:idfor join-table types? Currently deferred. Could be enabled later by updatingthingHref()with careful filtering to avoid result flooding.Rejected Approaches
A: Rename source-checks to /records — 52 files with hard-coded
/source-checks/URLs. Blast radius too high.C1: Ghost Links (hover popovers) — Records need permanent, shareable URLs.
C2: Parasitic on entity-profile — E1929 is 500+ lines, unintuitive URLs, dual code paths for orphans.
C3: Rename & subsume source-checks — Couples verification and data concerns; same blast radius as A.
Full dashboard (original Phase 3) — Stats bars, filter tabs, DataTable matching the deleted version. Reduced to minimal search page to avoid repeating the create-delete cycle.
Audit log on detail page — Only 3/20 types write entries. Empty section for 85% of records is misleading. Deferred.
Children tree on detail page — Duplicates entity-profile's per-entity record display. Replaced with stat line + link.
Red Team Log
Technical Critic (4 blocking, 3 significant)
/things/:id. Only types where id==sourceId go to things.UX Critic (3 high, 2 medium)
/thingsmatches the PG table name and avoids confusion with issue Rename /source-checks to /records — unified record detail page #3923's proposed/records. Internal tool URL, not user-facing./things/:idonly as fallback.Scope Critic (5 cuts recommended)
All reactions