fix(therapies): stop the catalogue generator consuming its own output (#180) and serve aliases by rewrite (#177) - #1886
Conversation
build-therapies-index.mjs read public/therapy-compass-data/therapies.json as its source and also wrote that same path as legacyFullTarget. Source and target were one file, so every run consumed its own output. That was not merely untidy. curatedFull nulls every tag-echo modality (curatedModality), so the first run overwrote the author's raw modality values with the scrubbed ones, and every later run re-read the scrubbed copy. It survived only because the scrub happens to be idempotent — the raw input was still destroyed, recoverable from git history alone. Move the hand-edited catalogue to src/data/therapies-source.json, outside the directory this generator writes, and read only from there. The file in public/therapy-compass-data/ is now output, never input. Also add the new path to .prettierignore. At its old path it was covered by the public/therapy-compass-data/ entry; without an entry Prettier pretty-prints the compact single-line catalogue into ~17k lines, which is the churn #179 fixed on the generated side. Verified: regenerated output is byte-identical to the previous alias (hashed filenames unchanged), the source stays byte-identical to its original across two further re-runs, check:therapy-data-index passes, and the 28 therapy contract tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STG6AU5J4gxrFJagP4pRti
…ating bytes public/therapy-compass-data/ shipped each catalogue twice: therapies.json, therapies-index.json and therapies-home.json were written byte-identical to their content-addressed twin. Git stores one blob per identical pair, so history was unaffected, but the working tree and every Docker image carried both — 2.81 MB, and 5.34 MB while the one-deploy grace generation is retained. The alias URLs themselves are load-bearing: useTherapyData falls back to them when a bundle older than the grace generation names a hashed file that no longer exists, so they cannot simply be dropped. Serve them from next.config.ts rewrites onto the current hashed filename instead, and stop writing the files. afterFiles rather than beforeFiles: no file exists at the alias paths now, so the rewrite is reached once the static handler finds nothing, and nothing legitimate is shadowed. build-therapies-index.mjs --check now fails if an alias file reappears, since a real file would win over an afterFiles rewrite and then go stale at the next regeneration. The generator writes content-addressed assets directly from the generated bytes rather than copying a just-written alias, so projectionBytes/fullCatalogueBytes are now the single definition of each payload's exact bytes. Verified against a running server: all three alias URLs return 200 with content byte-identical to their hashed asset; aliases keep Cache-Control max-age=0, must-revalidate while the hashed asset keeps max-age=31536000, immutable, so the alias does not inherit the destination's immutable policy; the retained grace generation still returns 200. Hashed filenames are unchanged by this refactor, proving the direct-write path is byte-identical. public/therapy-compass-data/ drops from 8.0M to 5.3M. npm run build succeeds and the stray-alias guard fires. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STG6AU5J4gxrFJagP4pRti
Both rows are resolved by the two preceding commits. Also corrects three rows that cited the catalogue paths this work moved — docs:check-links caught them. #175 mattered most: it instructed a future reader to curate modality values in public/therapy-compass-data/therapies.json, which no longer exists. It now names src/data/therapies-source.json, the only hand-edited catalogue file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STG6AU5J4gxrFJagP4pRti
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughThe PR moves the therapy catalogue source outside generated output, creates content-addressed assets, routes stable aliases through Next.js rewrites, packages generated mappings in Docker, and validates projected documentation ledger content. ChangesTherapy catalogue asset pipeline
Documentation ledger projection
Estimated code review effort: 3 (Moderate) | ~25 minutes Mergeability Score: ⚪ Minimal · up to The change separates catalogue source data from generated output and serves legacy aliases through rewrites while preserving URLs and payload bytes. Normal checks and targeted asset verification pass, so no actionable merge-blocking risk remains. Sequence Diagram(s)sequenceDiagram
participant Client
participant NextConfig
participant HashedAssets
Client->>NextConfig: request therapies.json alias
NextConfig->>HashedAssets: rewrite to generated hashed asset
HashedAssets-->>Client: return catalogue response
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed: dependency version conflict. Check your lock file or package.json. Comment |
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STG6AU5J4gxrFJagP4pRti
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4935d26a30
ℹ️ 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".
|
@codex resolve actionable Codex review findings for this pull request and current head using the repository instructions. This is the pull request's single automatic repair pass: do not perform a fresh review, create new standalone findings, or request another review. Work only the existing unresolved Codex threads on the current head. The only repository destination is BigSimmo/Database, and the only branch destination is the pull request head branch claude/therapy-catalogue-alias-rewrite at starting commit 31c8d0d; never publish fixes to a detached or synthetic work branch and never create a stacked pull request. Use the authenticated GitHub connector to commit each approved fix to BigSimmo/Database:claude/therapy-catalogue-alias-rewrite, then verify that the pull request head contains the published commit before reporting success. Always fix P0 and P1 findings. For P2 and lower findings, fix only clear, scoped, low-risk issues; otherwise disposition them with a concise reason. For a fixed thread, reply with as the first line and as the second line. For a no-code disposition, use followed by . These result markers authorize the workflow to close that exact thread only after it verifies a fixed commit is the pull request head; a local-only commit is not a fix. If publication or verification fails, do not use either result marker, do not claim success, and leave the thread open with the blocker. If human input or new authorization is required, do the same. Finish only after every actionable thread is fixed or dispositioned and closed, or explicitly left open for a human decision. Do not update the branch from main, address unrelated reviews, broaden scope, or create more than one scoped fix commit. Do not use external APIs, paid services, credentials, dependency changes, or broad refactors unless explicitly authorized. Add targeted tests where behavior changes and run the narrowest relevant validation. |
|
Codex Review: Didn't find any major issues. Keep them coming! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
|
Addressed the review finding on the current head |
|
@claude Work only the current unresolved P1 review finding on this PR at head |
|
Claude encountered an error after 2s —— View job I'll analyze this and get back to you. |
|
Hi @BigSimmo — thanks for the @claude mention. Open this in Claude Code on the web to continue here: https://claude.ai/code Context: Automated backlink from the Claude backlink workflow. Claude Code can pick up the mention and push a scoped fix to the branch. |
|
@claude The P1 Docker/Next-config fix is already verified on head |
|
Hi @BigSimmo — thanks for the @claude mention. Open this in Claude Code on the web to continue here: https://claude.ai/code Context: Automated backlink from the Claude backlink workflow. Claude Code can pick up the mention and push a scoped fix to the branch. |
|
Claude encountered an error after 2s —— View job I'll analyze this and get back to you. |
|
@claude Please main-sync this PR from exact head Preserve the Therapy catalogue alias rewrite and its generated-asset contract. Retain the already-resolved production runner fix: every local module imported by Run the Therapy index build/drift checks, focused mode-wiring and railway-config tests, typecheck, production build or the smallest deployment boot/config gate, and format. Report the new head and exact outcomes. |
|
Hi @BigSimmo — thanks for the @claude mention. Open this in Claude Code on the web to continue here: https://claude.ai/code Context: Automated backlink from the Claude backlink workflow. Claude Code can pick up the mention and push a scoped fix to the branch. |
|
Claude encountered an error after 2s —— View job I'll analyze this and get back to you. |
|
@copilot Merge Preserve the Therapy catalogue canonical-source move, generated-asset drift contract and alias rewrites. Retain the resolved production-runner requirement: every local Run Therapy index build/drift checks, focused mode-wiring and railway-config tests, ledger/issues integrity/write-discipline, typecheck, production build or smallest deployment boot/config gate, and format. Report the pushed 40-character SHA and exact outcomes. |
Preserve the canonical Therapy source move, rewrite-served aliases, Docker runner closure and focused regressions while retaining current-main ledgers and issue history.
CI triageCI failed on this PR. Automated classification of the 2 failed job(s):
Compared with main CI run #10417 (cancelled). Classification is evidence routing, not permission to ignore a failure. Exact quarantined Playwright identities remain governed by the flake ledger. |
|
@codex is this PR safe and is it deleting any valuable code? |
|
Codex Review: Didn't find any major issues. Swish! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
Codex review-and-fix final summary
I did not merge or close the PR. GitHub's pre-existing auto-merge was left untouched and merged the PR externally after the final exact-head required checks passed. |
Summary
#180— the generator consumed its own output.build-therapies-index.mjsreadpublic/therapy-compass-data/therapies.jsonas its source and also wrote that same path aslegacyFullTarget. This was not merely untidy:curatedFullnulls every tag-echomodality(curatedModality), so the first run overwrote the author's raw modality values with the scrubbed ones and every later run re-read the scrubbed copy. It survived only because the scrub happens to be idempotent — the raw input was still destroyed, recoverable from git history alone. The hand-edited catalogue moves tosrc/data/therapies-source.json, outside the directory this generator writes;public/therapy-compass-data/is now output only.#177— the aliases duplicated every payload.therapies.json,therapies-index.jsonandtherapies-home.jsonwere written byte-identical to their content-addressed twin, costing a second copy in the working tree and in every Docker image — 2.81 MB, and 5.34 MB while the one-deploy grace generation is retained. They are now served bynext.config.tsrewrites onto the current hashed filename, exactly the remediation the row specified.public/therapy-compass-data/drops 8.0M → 5.3M.useTherapyDatafalls back to them when a bundle older than the grace generation names a hashed file that no longer exists, so the URLs must keep working; only the duplicated bytes are gone.#180and#177, and corrects three rows that cited the moved paths (docs:check-linkscaught them).#175mattered most — it instructed a future reader to curate modality values inpublic/therapy-compass-data/therapies.json, which no longer exists; it now namessrc/data/therapies-source.json.Two notes on judgement calls. The two rows interact in a way neither acknowledges — the
therapies.jsonalias was the generator's source, so#180could not be fixed without deciding#177's question; fixing#180alone briefly makes the duplication worse (four copies), which is why both land together. And.prettierignoreneeded a new entry: at its old path the source was covered bypublic/therapy-compass-data/, and without one Prettier expands the compact 2.5 MB catalogue to ~17k lines — the churn#179fixed on the generated side.Verification
npm run verify:pr-localBehavioural proof, against a running server:
The alias does not inherit the destination's
immutablepolicy — that was the main risk of serving it by rewrite. The compiled production.next/routes-manifest.jsoncarries all threeafterFilesrewrites onto the current hashed assets plus both header rules, so this is not dev-only behaviour.Also verified: hashed filenames are unchanged by the refactor, proving the direct-write path is byte-identical to the previous copy-a-written-file path; the source stays byte-identical across repeated regenerations;
check:therapy-data-indexpasses and its new stray-alias guard fires (Therapy alias therapies-index.json is a duplicate file, exit 1).UI verification not run in full:
npm run verify:uiwas not run. The change is to how a static asset is served, and it was proven directly at the URL level (status, bytes, cache headers) plus 25 therapy loader/data-recovery DOM tests and the mode-wiring contract, which is more targeted evidence for this diff than a browser journey. Flagging it sinceclassifyPullRequestFilesreportsui: true.Provider access: none.
npm run startrefuses to boot without real Supabase env, so production serving was confirmed from the compiled routes manifest rather than by pointing a server at the live project.Risk and rollout
therapy-compass-data-recovery.dom.test.tsx. Rewrite destinations are baked into the routes manifest at build time fromgenerated-assets.ts, so a catalogue regeneration must be accompanied by a build — which deploy always does.git reverteach commit independently; they share no file except the ledger. Reverting only the#177commit restores the alias files and the duplicated bytes; reverting only the#180commit restores the old source path.Clinical Governance Preflight
Clinical KB Database(sjrfecxgysukkwxsowpy)Every item holds trivially: this changes where catalogue bytes are stored and how a static URL resolves, not what the catalogue says. The served payloads are byte-identical before and after, verified by
cmpon all three assets and by unchanged content hashes.Notes
RAG impact:line omitted deliberately —classifyPullRequestFilesreportsragRanking: false, and no retrieval, ranking, selection or scoring surface is touched.#175(therapy modality curation) remains open and is now correctly pointed atsrc/data/therapies-source.json. That row is the clinical follow-up: 205/205 records still carry no curated modality, and deciding between curating values and dropping the field needs the psychiatrist.Generated by Claude Code
Summary by CodeRabbit
Improvements
Documentation
Tests