fix(schema): cascade modeling provenance and permission on delete - #14
Merged
Conversation
The user role may delete a problem statement, a task or a thread only
while the row still carries a CREATE provenance event from that user.
The provenance and permission FKs were ON DELETE RESTRICT, so that
permission could never be satisfied:
- delete the provenance first, and the row loses its own delete
permission, so the delete matches 0 rows and returns success
- delete the row first, and Postgres refuses it
Both orders were run against a live database; neither works. Making the
six FKs ON DELETE CASCADE breaks the tie. The client deletes only the
row, while the CREATE event is still there to authorise it, and the
dependants go with it. Provenance and permission rows have no meaning
once their subject is gone, so cascading is also what they mean.
Metadata is unchanged.
Fixes mintproject/monorepo#99
This was referenced Aug 10, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the delete deadlock in mintproject/monorepo#99.
The problem
The
userrole may delete aproblem_statement,taskorthreadonly while the row still carries a CREATE provenance event from that user:Its provenance and permission FKs were
ON DELETE RESTRICT. That makes the permission unsatisfiable in both directions:delete_*_by_pkreturnsnull— a success with noerrorskeyviolates foreign key constraintBoth orders were run against a live MINT database. Neither works. The row is orphaned: invisible to its owner, because the
selectfilter reads the same provenance, and still visible toanonymous.The fix
Six FKs become
ON DELETE CASCADE:problem_statement_provenance,problem_statement_permissiontask_provenance,task_permissionthread_provenance,thread_permissionThe client then deletes only the row, while its CREATE event is still there to authorise it, and Postgres removes the dependants. A provenance or permission row has no life of its own once its subject is gone, so cascading is also what it means.
The structural FKs are untouched —
task -> problem_statement,thread -> task,thread_data,thread_modelstayRESTRICT, so a client still has to delete the tree deliberately.Metadata is unchanged. No
hasura metadata applyis needed.Verification
Applied to the
mint.localdev cluster and rehearsed there: a fullproblem statement > task > thread > thread_model > thread_data > dataslicetree deletes cleanly under the exact predicates Hasura compiles each root field's permission into, all six dependants cascade, and the deferred FK check passes. Run inside a rolled-back transaction; no residue.Callers
This migration alone changes nothing. A client must also stop deleting the provenance and permission rows first. For
ui-reactthat is mintproject/monorepo#115. The Lit UI (ui/src/queries/*/delete.graphql) has the identical fault and is not being fixed — it is unrouted and being retired. It is unaffected either way, because its deletes were already no-ops.