Skip to content

fix(schema): cascade modeling provenance and permission on delete - #14

Merged
mosoriob merged 1 commit into
mainfrom
fix/99-modeling-provenance-cascade
Aug 10, 2026
Merged

fix(schema): cascade modeling provenance and permission on delete#14
mosoriob merged 1 commit into
mainfrom
fix/99-modeling-provenance-cascade

Conversation

@mosoriob

Copy link
Copy Markdown
Collaborator

Fixes the delete deadlock in mintproject/monorepo#99.

The problem

The user role may delete a problem_statement, task or thread only while the row still carries a CREATE provenance event from that user:

delete_permissions:
- role: user
  permission:
    filter: { events: { _and: [ {event: {_eq: CREATE}}, {userid: {_eq: X-Hasura-User-Id}} ] } }

Its provenance and permission FKs were ON DELETE RESTRICT. That makes the permission unsatisfiable in both directions:

Order Result
provenance first, then the row (what every client does) the row loses its own delete permission, the delete matches 0 rows, and delete_*_by_pk returns null — a success with no errors key
the row first, then provenance Postgres refuses it: violates foreign key constraint

Both orders were run against a live MINT database. Neither works. The row is orphaned: invisible to its owner, because the select filter reads the same provenance, and still visible to anonymous.

The fix

Six FKs become ON DELETE CASCADE:

  • problem_statement_provenance, problem_statement_permission
  • task_provenance, task_permission
  • thread_provenance, thread_permission

The 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_model stay RESTRICT, so a client still has to delete the tree deliberately.

Metadata is unchanged. No hasura metadata apply is needed.

Verification

Applied to the mint.local dev cluster and rehearsed there: a full problem statement > task > thread > thread_model > thread_data > dataslice tree 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-react that 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.

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
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