Skip to content

v5 data platform, Phase 2: every read path on Postgres - #34

Open
philosophercode wants to merge 2 commits into
v5/data-platform-phase-1from
v5/data-platform-phase-2
Open

philosophercode wants to merge 2 commits into
v5/data-platform-phase-1from
v5/data-platform-phase-2

Conversation

@philosophercode

Copy link
Copy Markdown
Owner

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

  • Catalogue, projects, resources, maintenance history read Postgres through new query modules in v5/src/lib/data/. src/lib/catalog.ts and src/lib/projects.ts keep 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.
  • No mock-catalogue fallback. With DATABASE_URL unset 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).
  • Legacy links keep working. /tools/<notion-page-id>, with or without dashes, resolves to the tool's slug and keeps ?src=qr. The QR-label script prints slugs.
  • Health probes Postgres: { status, database: ok|unreachable|demo, catalog: live|demo, toolCount }, no error text in the body.
  • Chat manuals come from Postgres resources and Blob URLs. Capabilities read maintenance history from Postgres, reporter name only.
  • Build runs db:migrate first (no-op without DATABASE_URL). Blob's public hostname is allowed for images.
  • Docs (root and v5 AGENTS.md, CLAUDE.md, handover.md, TESTING.md, test/README.md) now say Postgres is the source of truth and that write paths are still on Notion until Phase 3.

Two things to know before Phase 3

  1. A temporary bridge for the writes still on Notion. Tickets, corrections and project submissions still create Notion pages with relations to Notion page ids, while the app now hands around Postgres uuids. src/lib/data/notion-ids.ts translates 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.
  2. Tool and project pages are no longer prerendered at build, because that needed a reachable database during 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 now npm 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 Link click 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: 1 keeps 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:all with every env var unset: lint (0 errors), typecheck, 946 unit tests in 72 files, 34 E2E. npm run spec:coverage: 0 undocumented. npm run build succeeds 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 in src/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

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
Copilot AI lite review requested due to automatic review settings September 15, 2026 03:02
@vercel

vercel Bot commented Sep 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
makerlab-tools Ready Ready Preview Sep 15, 2026 3:28am UTC
makerlab-tools-v5 Ready Ready Preview Sep 15, 2026 3:28am UTC

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +30 to +32
.select()
.from(projects)
.where(eq(projects.published, true))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment on lines +88 to +89
.from(maintenanceLogs)
.where(eq(maintenanceLogs.unitId, unitId))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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)))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants