Problem
The changes route and the digest enrichment will be covered by mocks only. No test drives them against a real Postgres, so a wiring break reaches production unseen.
The repo's two end-to-end habits also disagree with each other. A new script that copies either one repeats the fault.
e2e-versions.ts has no package script. CI calls the path directly at .github/workflows/backend-ci.yml:108: run: bun --env-file=../../.env.local scripts/e2e-versions.ts. A reader of package.json cannot see that this script exists.
test:e2e:duplicate-media is the mirror image. apps/hocuspocus.server/package.json:22 defines it. No workflow under .github/workflows/ mentions it, so it never runs in CI.
Part of #169. Blocked by the compute service and its REST route.
What to do
Add apps/hocuspocus.server/scripts/e2e-document-changes.ts. Follow scripts/e2e-content-inject.ts: a standalone script, not an extension of e2e-versions.ts.
Give it both halves of the wiring, so it repeats neither fault above:
- a package script
test:e2e:document-changes beside apps/hocuspocus.server/package.json:20, in that same shape: bun --env-file=../../.env.local scripts/e2e-document-changes.ts
- a step in the
backend-tests job, the only job in the workflow, declared at .github/workflows/backend-ci.yml:14. Run bun run test:e2e:document-changes, matching the step at .github/workflows/backend-ci.yml:100-102. Put it after the change-attribution step at .github/workflows/backend-ci.yml:113-115, which is the end of the file.
Five scenarios:
- Create a document with a title and four sections. Record the time as T1. Edit two sections, add one, remove one. Poll for the version rows. Then request
/changes?since=<T1>&scope=headings. Assert those statuses, the tree placement, and non-zero word deltas. Assert a summary equal to the per-section sums.
- A window with no edits returns
changed: false and an empty sections array.
- A baseline version row with no
toc-id values, written straight through Prisma, plus a stamped head. Unedited sections must read unchanged, never a whole-document added wall.
- No bearer returns 401. A tombstoned document returns 404.
- A
since older than the retention floor. The response baseline.createdAt echoes the version row actually used as the anchor.
Do not add a test path list. apps/hocuspocus.server/package.json:16 is "test": "bun test",. The comment at .github/workflows/backend-ci.yml:35-36 records that an enumerated path list once dropped a whole __tests__/integration directory from the gate.
Documentation pass:
apps/hocuspocus.server/API.md — a Contents entry, and a ## Document changes section. The ## Document versions section starts at line 370 and ends at line 638, and ## Document conversion starts at line 639, so the new section goes between them. The Contents list is numbered at lines 12-25, so a new entry after item 6 renumbers items 7-14.
- Cover the window semantics, both response shapes, the status table, the section matching rules, and the honest attribution limits.
apps/hocuspocus.server/CLAUDE.md — the new module, and the worker-side, fail-soft enrichment.
apps/hocuspocus.server/Readme.md — the module line. The module list is at line 70, and the per-module prose sits beside line 104.
apps/hocuspocus.server/scripts/documents.http — request examples, beside the # Document versions (service-role only) block at line 146.
- A new OpenAPI path file
apps/hocuspocus.server/src/modules/openapi/domain/paths/documentChanges.ts, beside documentVersions.ts in that directory. Spread it into the paths object beside ...documentVersionsPaths, at apps/hocuspocus.server/src/modules/openapi/domain/document.ts:77.
Acceptance
Notes
CI sets SUPABASE_URL to a closed port. The value is http://127.0.0.1:9 at .github/workflows/backend-ci.yml:67. Any assertion needing a real Supabase session reports as skipped there, exactly like the two existing scripts. So a green CI step is weaker evidence than a local run. Keep those assertions skippable, and print the skip in the script output.
The queue-to-email leg is deliberately out of scope here. It needs Supabase cron plumbing, so it cannot run inside this script.
Problem
The changes route and the digest enrichment will be covered by mocks only. No test drives them against a real Postgres, so a wiring break reaches production unseen.
The repo's two end-to-end habits also disagree with each other. A new script that copies either one repeats the fault.
e2e-versions.tshas no package script. CI calls the path directly at.github/workflows/backend-ci.yml:108:run: bun --env-file=../../.env.local scripts/e2e-versions.ts. A reader ofpackage.jsoncannot see that this script exists.test:e2e:duplicate-mediais the mirror image.apps/hocuspocus.server/package.json:22defines it. No workflow under.github/workflows/mentions it, so it never runs in CI.Part of #169. Blocked by the compute service and its REST route.
What to do
Add
apps/hocuspocus.server/scripts/e2e-document-changes.ts. Followscripts/e2e-content-inject.ts: a standalone script, not an extension ofe2e-versions.ts.Give it both halves of the wiring, so it repeats neither fault above:
test:e2e:document-changesbesideapps/hocuspocus.server/package.json:20, in that same shape:bun --env-file=../../.env.local scripts/e2e-document-changes.tsbackend-testsjob, the only job in the workflow, declared at.github/workflows/backend-ci.yml:14. Runbun run test:e2e:document-changes, matching the step at.github/workflows/backend-ci.yml:100-102. Put it after the change-attribution step at.github/workflows/backend-ci.yml:113-115, which is the end of the file.Five scenarios:
/changes?since=<T1>&scope=headings. Assert those statuses, the tree placement, and non-zero word deltas. Assert a summary equal to the per-section sums.changed: falseand an emptysectionsarray.toc-idvalues, written straight through Prisma, plus a stamped head. Unedited sections must readunchanged, never a whole-documentaddedwall.sinceolder than the retention floor. The responsebaseline.createdAtechoes the version row actually used as the anchor.Do not add a test path list.
apps/hocuspocus.server/package.json:16is"test": "bun test",. The comment at.github/workflows/backend-ci.yml:35-36records that an enumerated path list once dropped a whole__tests__/integrationdirectory from the gate.Documentation pass:
apps/hocuspocus.server/API.md— a Contents entry, and a## Document changessection. The## Document versionssection starts at line 370 and ends at line 638, and## Document conversionstarts at line 639, so the new section goes between them. The Contents list is numbered at lines 12-25, so a new entry after item 6 renumbers items 7-14.apps/hocuspocus.server/CLAUDE.md— the new module, and the worker-side, fail-soft enrichment.apps/hocuspocus.server/Readme.md— the module line. The module list is at line 70, and the per-module prose sits beside line 104.apps/hocuspocus.server/scripts/documents.http— request examples, beside the# Document versions (service-role only)block at line 146.apps/hocuspocus.server/src/modules/openapi/domain/paths/documentChanges.ts, besidedocumentVersions.tsin that directory. Spread it into thepathsobject beside...documentVersionsPaths,atapps/hocuspocus.server/src/modules/openapi/domain/document.ts:77.Acceptance
apps/hocuspocus.server/package.jsonholdstest:e2e:document-changes, and.github/workflows/backend-ci.ymlholds a step that runsbun run test:e2e:document-changes.GET /openapi.jsonlists/api/documents/{documentId}/changes.API.mdhas a## Document changessection, reachable from the Contents list.bun run testinapps/hocuspocus.serveris green, with no edit to thetestscript.Notes
CI sets
SUPABASE_URLto a closed port. The value ishttp://127.0.0.1:9at.github/workflows/backend-ci.yml:67. Any assertion needing a real Supabase session reports as skipped there, exactly like the two existing scripts. So a green CI step is weaker evidence than a local run. Keep those assertions skippable, and print the skip in the script output.The queue-to-email leg is deliberately out of scope here. It needs Supabase cron plumbing, so it cannot run inside this script.