Skip to content

feat(retention): purge hook for runtime extensions and organization file sweep - #472

Merged
MartinPTielemans merged 1 commit into
mainfrom
feat/qui-252-purge-extension-points
Oct 24, 2026
Merged

MartinPTielemans merged 1 commit into
mainfrom
feat/qui-252-purge-extension-points

Conversation

@MartinPTielemans

@MartinPTielemans MartinPTielemans commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Problem

purgeOrganization (slice 1, #471) deletes an organization's rows and the files its rows point at. A distribution that keeps its own tables referencing the organization or its users has no way to delete them in the same transaction, so the purge would hit a foreign key. Files stored before any row pointed at them (an interrupted render) are never found. And the purge isn't exported, so the hosted product can't call it.

Change

  • RuntimeExtension.onOrganizationPurge(tx, { organizationId, userIds }) runs inside the purge transaction, after the organization row is locked and before its rows and users are deleted. userIds are the sole-member users that are about to be deleted. tx is narrowed to $queryRaw/$executeRaw, the same narrowing the seller VAT confirmation hook uses. If the hook throws, the whole purge rolls back. Self-hosted installs register no extension, so nothing changes for them.
  • An optional DocumentArtifactStore.deleteOrganization(organizationId). purgeOrganization calls it after the ref-based deletes. The disk store removes the <root>/<organizationId>/ directory. Refs are <organizationId>/<kind>/<documentId>/<hash>.<ext>, and ids are limited to [a-zA-Z0-9_-]+, which the store already enforced on put and now shares with the sweep. So the sweep removes exactly one path segment and can't reach org-12 when purging org-1. It also refuses .., / and empty ids.
  • New package subpath @quits/oss/organization-purge exports purgeOrganization, deleteOrganizationRows, OrganizationPurgeResult and the hook types. The packed-artifact check already imports every export key.

Where this sits in QUI-252 (9 slices)

  1. oss: purge service (feat(retention): hard purge for one organization's data #471, merged)
  2. oss: extension hook, store sweep, export (this PR)
  3. oss: "Delete organisation" in Settings behind a capability
  4. to 8. hosted: retention tracking, owner notices, the deletion job using this hook and sweep, early deletion, backup expiry
  5. oss, optional: ZIP archive export

Linear

Refs QUI-252

Verification

  • vp test run -c vitest.integration.config.ts src/domain/__tests__/organization-purge.integration.test.ts src/trpc/routers/__tests__/onboarding-v2-export.integration.test.ts: 7 passed against a fresh PostgreSQL with the CI auth env. This covers:
    • the existing every-table purge test, which now also checks that an orphan file is swept;
    • the hook sees the organization row still present, with the right context;
    • a hook that writes and then throws rolls back both its write and the purge.
  • vp test run -c vitest.config.ts src/selfhost src/lib/runtime: 13 passed, including the new disk-store test. That test covers prefix-sharing ids (org-12, org-1_x, org), repeated and empty sweeps, and rejected ids.
  • vp lint --deny-warnings on the changed files: clean. bun run typecheck:app: clean.
  • CI: green on 58c2338.

Risk

  • No design change.
  • No copy change.
  • No production effect: nothing calls purgeOrganization yet.

New optional members on two public types, plus a new export subpath. These are additive, so existing implementations keep compiling.

@linear-code

linear-code Bot commented Oct 24, 2026

Copy link
Copy Markdown

QUI-252

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-24T12:39:56.120580Z
We couldn’t complete this review. Comment @codex review to try again.
58c2338 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@MartinPTielemans
MartinPTielemans merged commit 52a48be into main Oct 24, 2026
20 checks passed
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.

1 participant