v5 data platform, Phase 2: every read path on Postgres - #34
philosophercode wants to merge 2 commits into
Conversation
Catalogue, projects, resources and maintenance history read Postgres through src/lib/data/; catalog.ts and projects.ts keep their exports and cache tags over the new modules. No mock-catalogue fallback: PGlite demo seed without DATABASE_URL, errors propagate otherwise. Legacy /tools/<notion-page-id> links redirect to the slug. Health probes Postgres. Chat manuals and unit history come from Postgres. The build runs db:migrate first. Docs say Postgres is the source of truth and that writes stay on Notion until Phase 3. A temporary bridge translates Postgres ids back to Notion page ids for the three writes still on Notion. Tool and project pages are no longer prerendered at build, since that needed a database. E2E runs against `next build && next start` with one retry: under `next dev` parallel first compiles rewrote the layout chunk mid-download and four tests failed every run; what remains is a rare client-router race that Playwright reports as flaky. Details in the spec's Phase 2 amendment. Built by a workflow of eight agents (Opus for the core, capabilities and integration; Sonnet for projects, health, redirect, config, docs, review). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NP2xC6APXbSucoE4n5mtoj
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f25a8ff463
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .select() | ||
| .from(projects) | ||
| .where(eq(projects.published, true)) |
There was a problem hiding this comment.
Write project submissions to the active read store
After this read cutover, POST /api/projects still creates the submission only in Notion, while this query reads exclusively from Postgres. Consequently, any project submitted after the one-time import remains absent from the gallery and detail routes even after staff publishes it in Notion, unless someone manually reruns the import; either dual-write/sync it into Postgres or retain the Notion read until the write path moves.
AGENTS.md reference: v5/AGENTS.md:L52-L59
Useful? React with 👍 / 👎.
| .from(maintenanceLogs) | ||
| .where(eq(maintenanceLogs.unitId, unitId)) |
There was a problem hiding this comment.
Keep newly filed tickets in maintenance history
When report_issue files a ticket after this commit, it still writes only a Notion page, but maintenance-history reads now query only this Postgres table. The assistant therefore cannot see a newly reported issue in get_maintenance_history until a manual re-import or Phase 3, which can produce an incorrect clean history for an affected machine; the write must also reach Postgres or this read must include the remaining write store.
AGENTS.md reference: v5/AGENTS.md:L52-L59
Useful? React with 👍 / 👎.
| .select({ projectId: projectTools.projectId, id: tools.id, name: tools.name, slug: tools.slug }) | ||
| .from(projectTools) | ||
| .innerJoin(tools, eq(projectTools.toolId, tools.id)) | ||
| .where(and(inArray(projectTools.projectId, projectIds), eq(tools.published, true))) |
There was a problem hiding this comment.
Exclude archived tools from project references
When a published tool is archived by setting archived_at while leaving published true, this predicate still hydrates it into every related project's tools array. Project pages then display a tool chip whose /tools/<slug> target returns not-found because catalogue lookups exclude archived rows; add the same archived_at IS NULL condition used by the catalogue.
Useful? React with 👍 / 👎.
The demo seed publishes one sample project (a laser-cut lamp built with both demo tools, two bundled photos, materials, an outside link) and gives the Trotec a photo attachment, since its bundled image is not named after the tool. Project URLs use the slug. Messages fall back to English per locale (Article 6 as amended), and the first English-only strings are the header's sign-in notices: a click that cannot start sign-in now says whether this deployment has none configured or the request failed, in a popover that fades after six seconds and never shifts the layout. The Add control takes the same chrome as Report instead of the browser default. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NP2xC6APXbSucoE4n5mtoj
Phase 2 of the v5 data platform spec (§9): every read path moves from Notion to Postgres. Builds on #33 (Phase 1). Merging this switches production to Postgres, so the import (
npm run import:notion) must have run into the production database first.What changes
v5/src/lib/data/.src/lib/catalog.tsandsrc/lib/projects.tskeep their exports and their"use cache"tags; pages, components and capability outputs are untouched. Derived fields (status, condition, training level) follow the same rules as before, from the stored vocabulary.DATABASE_URLunset the app runs on the PGlite demo seed and says so in the banner; with it set and unreachable, reads fail toward the cached page, never toward sample data (Article 4)./tools/<notion-page-id>, with or without dashes, resolves to the tool's slug and keeps?src=qr. The QR-label script prints slugs.{ status, database: ok|unreachable|demo, catalog: live|demo, toolCount }, no error text in the body.db:migratefirst (no-op withoutDATABASE_URL). Blob's public hostname is allowed for images.Two things to know before Phase 3
src/lib/data/notion-ids.tstranslates through the page ids the import recorded; a row with none is written without the relation and logged. It leaves with the writes in Phase 3.next build. They render on first request and are then cache-tagged. An unknown slug and a legacy redirect resolve inside the dynamic part of the page, so the HTTP status is 200 and the browser performs the not-found or the redirect. A real 404/308 is a follow-up.E2E now runs against a production build, with one retry
Under
next dev, Playwright's fourteen parallel workers made Turbopack rewrite the root layout's client chunk mid-download (ERR_CONTENT_LENGTH_MISMATCH), so pages intermittently never hydrated: 4 of 34 failed on every run, a different 4 each time. The Playwright web server is nownpm run build && next start, which also proves the bundled server works in demo mode with PGlite (what a Vercel preview without a database runs).What remains is about one test execution in a hundred, always the same shape: a
Linkclick or the locale switch whose request is sent and answered in milliseconds (visible in the trace) but whose result the App Router never commits. The server answers 56 concurrent requests in ~150 ms, nothing is logged, and a hydration-marker wait changed nothing, so it's filed as a client-router race under load, not an app defect.retries: 1keeps the gate green and Playwright reports such tests as flaky, so it stays visible. Details in the spec amendment; worth a look on the next Next.js upgrade.Verification
npm run test:allwith every env var unset: lint (0 errors), typecheck, 946 unit tests in 72 files, 34 E2E.npm run spec:coverage: 0 undocumented.npm run buildsucceeds without a database. An as-built amendment on the spec records the details above.Not deleted, proposed separately
src/components/mock-catalog.ts,test/fixtures/catalog.ts, the Notion read helpers insrc/lib/notion.ts, and the four Notion maintenance scripts are unused by the app but left in place per the working agreements. Say the word and they go in a follow-up commit.How it was built
A workflow of eight agents: Opus wrote the catalogue module, the capabilities/chat rewiring and the integration pass; Sonnet wrote projects, health, the redirect, config, docs and the review. The E2E diagnosis and fix, the spec amendment and this description are the orchestrator's.
🤖 Generated with Claude Code
https://claude.ai/code/session_01NP2xC6APXbSucoE4n5mtoj