Replies: 1 comment
|
Branch: Cross-link: sibling of #3945 (Things UI Universal Record Inspector — detail) and #3998 (Things Listing Enhancements — listing). #4005 covers the coverage dashboard / enrichment queue side of the same overall Things UI initiative. 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.
Uh oh!
There was an error while loading. Please reload this page.
TableBase Coverage Dashboard & Enrichment Queue — Implementation Plan
TL;DR
TableBase completeness data is CLI-only and ephemeral — there is no way to see coverage trends, prioritize enrichment work, or dispatch overnight jobs without re-running the scanner manually each time. This plan adds PG persistence for nightly scan results, a read-only Pattern A dashboard that computes a ranked task queue on-the-fly (using
entitiesjoined towiki_pagesfor parent-org importance), and a Phase 3 job-dispatch layer. Phase 1 persists scan results + adds a groundskeeper cron task. Phase 2 is the dashboard (coverage bars, trends, task queue ordered by org importance). Phase 3 adds dispatch and completion tracking. Top open question: whether to use the existing jobs queue or a standalone GitHub Actions workflow for overnight enrichment dispatch.Problem
The TableBase enrichment pipeline (
crux tb enrich) has no visibility layer: every time an engineer wants to know what's missing, they must re-run the CLI scanner (~90 seconds), and results are immediately discarded. There is no history of coverage over time, no ranked queue of high-impact tasks, no way to dispatch enrichment jobs from a dashboard, and no mechanism to avoid re-running already-completed tasks without manually checking.tablebase-completed.txt. Current coverage is approximately: personnel 6%, funding_rounds 4%, investments 5%, benchmarks 45%, grants 99%.Current State
crux/tablebase/scanner.ts--persistflag + wiki-server POST endpointcrux/tablebase/task-ranker.ts(100-completeness) × typeWeight × importanceMultiplier.tablebase-completed.txttablebase_scan_resultstabletablebase_enrichment_taskstablethingstablesource_id= record's own ID (not entity stableId)entitiestable insteadwiki_pages.reader_importancetablebase-enrichmentjob typeapps/wiki-server/src/routes/tablebase//api/tablebase-coveragein app.tsProposed Approach
Three incremental phases, each independently shippable:
Phase 1 — Persist scan results: Add
tablebase_scan_resultsPG table, wiki-serverPOST /api/tablebase-coverage/scan-resultsHono RPC route, modifyscanner.tsto POST per-table results incrementally (not in a single batch at the end), and add a nightly groundskeeper cron task.Phase 2 — Read-only coverage dashboard: Build a Pattern A dashboard that reads scan history from PG and shows: per-table coverage % with trend sparklines, and a ranked task queue computed on-the-fly via
SELECT … FROM entities e LEFT JOIN wiki_pages wp ON wp.wiki_id = e.wiki_idfor importance data. Importance for personnel/funding_rounds is inherited from the parent org'sreader_importance. No dispatch in Phase 2 — the dashboard is read-only.Phase 3 — Job dispatch + completion tracking: Add
tablebase_enrichment_taskstable, registertablebase-enrichmentjob handler in groundskeeper, add a "Dispatch" button with table-type filter, job count input, and estimated cost, wire inline job status in the task queue (JOIN withjobstable, client-side polling).Importance inheritance approach (Phase 2 key decision): Most personnel and funding_round records don't have wiki pages, so
wiki_pages.reader_importanceis NULL for them directly. The task queue inherits importance from the parent org: for a personnel record, join to the org entity viaentities.id = tet.entity_id, then getwp.reader_importancefrom the org's wiki page. An Anthropic employee inherits Anthropic's importance (90) even without their own wiki page. This separates "key researcher at top-tier org" from "unknown at obscure lab" — fallback 50 only applies when even the parent org has no wiki page.ASCII diagram — Coverage Dashboard (Phase 2):
Key Decisions
Use
entitiestable for importance JOIN, notthings.source_idthings.source_id(wrong — it's the record's own ID, not entity stableId), new entity registry tablethings.source_idfor personnel/grant records is the record's own PK, not the parent entity's stableId. The correct JOIN isentities e ON e.stable_id = tet.entity_id LEFT JOIN wiki_pages wp ON wp.wiki_id = e.wiki_id.Inherit parent org importance for personnel/funding_rounds
entity_idpointing to their parent org. Org importance (viawiki_pages.reader_importance) meaningfully ranks child records. Fallback 50 only applies when even the parent org has no wiki page.Task queue computed on-the-fly in Phase 2, not persisted
tablebase_enrichment_taskstable in Phase 2; materialized viewprofilesJSONB intablebase_scan_resultsalready has per-entity completeness. A ranked queue is just a JOIN of that data withentities+wiki_pages. Persisting it in Phase 2 adds a table, migration, and backfill complexity with zero Phase 2 benefit — status tracking only matters once dispatch exists (Phase 3).Scanner POSTs incrementally (per-table), not in a single batch
Phase 2 is read-only; dispatch button deferred to Phase 3
.tablebase-completed.txtstays authoritative for CLI through Phase 3${taskType}:${entityId}— the original inputs are not stored, so backfill requires enumerating all entity × taskType combinations and hash-matching. This complexity belongs in Phase 3 whentablebase_enrichment_tasksis created, not Phase 2.Architecture
apps/wiki-server/src/schema.tstablebase_scan_resultstable (Phase 1);tablebase_enrichment_tasks(Phase 3)apps/wiki-server/drizzle/0<N>_tablebase_scan_results.sqltablebase_scan_results(Phase 1)apps/wiki-server/drizzle/0<M>_tablebase_enrichment_tasks.sqltablebase_enrichment_tasks+ backfill script (Phase 3)apps/wiki-server/src/routes/tablebase/coverage.tsPOST /scan-results,GET /scan-results,GET /task-queue,POST /task-queue/dispatchapps/wiki-server/src/app.tsapp.route("/api/tablebase-coverage", coverageRoute)crux/tablebase/scanner.ts--persistflag; POST per-table results to/api/tablebase-coverage/scan-resultsincrementallycrux/commands/tablebase/scan.ts--persistflag to scannerapps/groundskeeper/src/tasks/tablebase-scan.tsapps/groundskeeper/src/config.tstablebaseScan: TaskConfiginterface +TASK_TABLEBASE_SCAN_ENABLEDenv varapps/groundskeeper/src/index.tstablebase-scancron task + log block entryapps/groundskeeper/src/tasks/tablebase-enrichment-handler.tstablebase-enrichmentjob type (Phase 3)apps/web/src/app/internal/tablebase-coverage/apps/web/src/app/internal/tablebase-coverage/page.tsx/wiki/E<N>apps/web/src/app/internal/tablebase-coverage/tablebase-coverage-content.tsxapps/web/src/app/internal/tablebase-coverage/task-queue.tsxcontent/docs/internal/tablebase-coverage.mdxapps/web/src/components/mdx-components.tsxTablebaseCoverageContentapps/web/src/lib/wiki-nav.tsinternalHref("tablebase-coverage")New PG table —
tablebase_scan_results(Phase 1):New PG table —
tablebase_enrichment_tasks(Phase 3):Task queue query (Phase 2 — on-the-fly, no
tablebase_enrichment_tasks):New Hono RPC route (
apps/wiki-server/src/routes/tablebase/coverage.ts):Implementation Phases
Phase 1: PG persistence + nightly scan — S/M
Goal: After this ships,
SELECT * FROM tablebase_scan_results ORDER BY scan_timestamp DESC LIMIT 5shows real data; nightly scan runs automatically.tablebase_scan_resultstable to schema.ts (withUNIQUE(scan_timestamp, table_name)andNUMERIC(5,2))POST /api/tablebase-coverage/scan-results(upsert semantics)GET /api/tablebase-coverage/scan-results?limit=Nroutescanner.ts: add--persistflag; POST per-table incrementally after each table completes (not batch at end)apps/groundskeeper/src/tasks/tablebase-scan.ts(02:00 UTC)apps/groundskeeper/src/config.ts: addtablebaseScan: TaskConfig+TASK_TABLEBASE_SCAN_ENABLEDenv varapps/groundskeeper/src/index.ts: register task + add to startup log block--persistflag triggers POSTQuality gates:
pnpm test,pnpm build,pnpm crux tb scan --persist --tables=grants --max=5→ verify row in DBExit criteria:
SELECT table_name, avg_completeness_pct FROM tablebase_scan_results ORDER BY created_at DESC LIMIT 5returns real data after manual scan with--persistPhase 2: Read-only coverage dashboard — M
Goal: After this ships, the dashboard at
/wiki/E<N>shows per-table coverage bars with 30-day trends, an overall coverage KPI, and a ranked task queue with parent-org importance inheritance.pnpm crux tb ids allocate tablebase-coverage-dashboardGET /api/tablebase-coverage/task-queue?table_name=&limit=50route (on-the-fly SQL with entities JOIN)tablebase-coverage-content.tsx(server component followinggroundskeeper-runs-content.tsxpattern): stat cards (overall coverage %, total entities, scan age), per-table coverage bars (usingcoverage-bars.tsxfrom entity-source-checks), sparkline trend charttask-queue.tsx(client component with polling, followingentity-source-checks/action-queue.tsxpattern): ranked table with org-inherited importance, table-type filter pills, "Showing N of M" count, stable secondary sort by entity_namecontent/docs/internal/tablebase-coverage.mdxwith allocated E-numberTablebaseCoverageContentinapps/web/src/components/mdx-components.tsxapps/web/src/lib/wiki-nav.tscrux tb scanCLI output ("View dashboard: https://www.longtermwiki.com/wiki/E")Quality gates:
pnpm build,cd apps/web && PLAYWRIGHT_BASE_URL=https://www.longtermwiki.com npx playwright test e2e/render-audit.spec.ts(add/wiki/E<N>to render-audit list), verify coverage bars render with real dataExit criteria: Dashboard loads showing coverage bars with % values matching
tablebase_scan_results; task queue shows Sam Altman / Dario Amodei near top (org importance 90); no console errorsPhase 3: Job dispatch + completion tracking — L
Goal: After this ships, clicking "Dispatch 10 jobs" creates
tablebase-enrichmentjobs that groundskeeper executes; task queue shows inlinedispatched/completed/failedstatus with live refresh.tablebase_enrichment_taskstable + migrationcrux/scripts/migrate-completed-tasks.ts: enumerate all current entity × taskType combinations, hash-match against.tablebase-completed.txt, insert matched rows withstatus='completed'; log orphaned IDs as warningsPOST /api/tablebase-coverage/task-queue/dispatchroute (table-type filter, count input, creates jobs, returns cost estimate based on taskType weights)tablebase-enrichment-handler.ts: callscrux tb enrich --entity-id=X --task-type=Y, marks taskcompletedorfailedtablebase-enrichmentin groundskeeper (analogous to existing job types)tablebase_enrichment_tasks+jobstable for inline status (color pills: pending/dispatched/running/completed/failed); link job_id to/internal/jobstask-queue.tsx: table-type filter pills, numeric job count input (default 10, max 50), cost estimate display ("~$X estimated"), confirmation dialogtask-queue.tsxpolling to refresh status every 30s when any tasks aredispatched/running.tablebase-completed.txtonce backfill is verified: remove from CLI reads, keep file for 1 release cycle then deleteQuality gates:
pnpm test, integration test dispatching 1 job with--table=grants→ verify job in DB withtype='tablebase-enrichment', verify task_queue showsstatus='dispatched'Exit criteria: End-to-end: dispatch button → job created → groundskeeper picks up → task shows
completedin dashboardScope Cuts
The following are explicitly NOT included:
profilesJSONB is stored intablebase_scan_resultsbut not surfaced in the Phase 2/3 UI. Expose viaGET /scan-results/profiles?table=Xin a future phase.pnpm crux fb source-checkpipeline.Quality & Verification Infrastructure
apps/wiki-server/src/__tests__/tablebase-coverage.test.ts: POST scan-result roundtrip, upsert idempotency, partial scan (3 tables → 3 rows), task-queue ordering by impact score, table-name filter, dispatch creates correct jobs.crux/__tests__/scanner-persist.test.ts:--persistflag calls wiki-server POST; incremental posting (per-table, not single batch at end).grep -r "tablebase-completed.txt" crux/ --include="*.ts"→ 0 results confirming deprecation is complete.pnpm crux tb scan --persist --tables=grants && psql $DATABASE_URL -c "SELECT table_name, avg_completeness_pct, created_at FROM tablebase_scan_results ORDER BY created_at DESC LIMIT 3;"Phase 3:pnpm crux gh issues start 0(noop) and verify one job dispatched + picked up within 10 min by groundskeeper./wiki/E<N>toapps/web/e2e/render-audit.spec.tspage list. Runcd apps/web && PLAYWRIGHT_BASE_URL=https://www.longtermwiki.com npx playwright test e2e/render-audit.spec.tsafter deploy.TASK_TABLEBASE_SCAN_ENABLED=truein groundskeeper env. Phase 2: No manual steps. Phase 3: Run backfill script after deploy:pnpm crux scripts migrate-completed-tasks. Verify task count withSELECT COUNT(*) FROM tablebase_enrichment_tasks WHERE status='completed'matches.tablebase-completed.txtline count (~196).content/docs/internal/data-architecture.mdxto mention new tables. Addcrux tb scan --persistto CLAUDE.md Quick Reference.tablebaseLastScanAge(hours since most recenttablebase_scan_resultsrow). Alert if >26h (missed nightly run).Risks & Mitigations
.tablebase-completed.txt/ PG divergence during transitiontablebase-enrichmentjob type dispatched before handler existsprofilesJSONB is per-table (not per-entity in one giant blob). Latest-scan CTE usesDISTINCT ON. Addidx_tet_status_impactin Phase 3. Benchmark query against ~700 entities in staging before Phase 2 deploy.things.wiki_idwas assumed correct for the JOIN (was broken in earlier plan draft)entities.stable_idfor JOIN, notthings.source_id.thingstable NOT used for entity importance lookup.reader_importance IS NULL. This is acceptable — zero-importance entities will appear near the bottom of the queue either way.Open Questions
SELECT FOR UPDATE SKIP LOCKED) or trigger a GitHub Actions workflow per batch? Jobs queue is consistent with all other job types but requires Phase 3 handler work. GHA workflow is simpler to implement but harder to monitor inline.TASK_TYPE_WEIGHTSin task-ranker.ts (personnel 1.5x = more expensive), but actual $ cost depends on model tier used by the enrichment command. Needs a cost-per-task table or a fixed approximate.Rejected Approaches
things.source_idfor entity importance JOIN: Rejected —things.source_idis the record's own PK for personnel/grant records, not the parent entity stableId. Must useentities.stable_idinstead.tablebase_enrichment_taskstable in Phase 2: Rejected — status tracking only matters once dispatch exists (Phase 3). Phase 2 computes the queue from scan results + entities on-the-fly..tablebase-completed.txtin Phase 2: Rejected — backfill requires rainbow-table enumeration over entity × taskType combinations (hashes are not reversible). This complexity belongs in Phase 3 with thetablebase_enrichment_taskstable.Red Team Log
Round 1 — Technical critic:
things.source_idis record's own ID, not entity stableId — JOIN for importance was broken. Fixed: useentitiestable..tablebase-completed.txthash IDs not reconstructable standalone — backfill requires enumeration. Moved entirely to Phase 3./api/tablebase/is code directory, not URL prefix. Fixed: mount as/api/tablebase-coverage.config.tsnot in architecture table. Fixed: added with note about 4 required changes.(scan_timestamp, table_name),avg_completeness_pctshould be NUMERIC(5,2), scanner should POST per-table incrementally. All fixed.Round 1 — Scope critic:
tablebase_enrichment_tasksnot needed in Phase 2. Accepted: removed, deferred to Phase 3.GET /scan-results?limit=N. Accepted..tablebase-completed.txtbackfill cut from Phase 2. Accepted.Round 1 — UX critic:
task-queue.tsxis a client component with polling.--persistscan.profilesJSONB is stored for this purpose.Tasks
All reactions