Phase 5a — The Cascade: five bugs, one shape
25 JUL AT 10:39 AM

Phase 5a — The Cascade: five bugs, one shape

0 LOVES 0 VIEWS
Five live, pre-existing bugs, all the same shape: a lifecycle operation — a block delete, a status transition, a post delete — meets an unadvertised NoAction foreign key and 500s or silently does the wrong thing. Found while grounding the write path, fixed per module, re-smoked including Flutter. It lands before Phase 5 so a live 500 isn't gated behind the assertion work.

A cascade is fine until one link in it hits a wall it did not know was there. Phase 5a is five such walls — five live, pre-existing bugs found while grounding the write path, all with the same shape: a lifecycle operation meets a NoAction foreign key that the code did not advertise, and either 500s or silently does the wrong thing.

Why fix them as their own phase, not a bug ticket: the delete/archive work (5c/5d) has to add assertions as one more block dependent — and it cannot do that cleanly on top of a cascade that already 500s. So 5a clears the ground first. It is also where archive-not-delete gets applied, not decided: the FK table already enforces it (a post pinned by its blocks can never be hard-deleted), so a briefly-added delete route was fully reverted — 5a just makes every path honor what the schema already says.

The five bugs
One shape, five walls — each a lifecycle op that meets an unadvertised NoAction FK.
1
Bug 1 · Step-block delete 500s everywhere
Deleting a step block 500s on every surface, including Studio, because a UserBlockInteraction row references it with a NoAction FK the delete never cleared. Fix: clear the interaction rows first, in the delete transaction — applied per module, not via a shared helper.
The shape: a lifecycle op meets an unadvertised NoAction FK.
2
Bug 2 · Tag-block delete 500s via API and Flutter
The same NoAction-FK shape on tag blocks: the TagBlockItem join pins the block, so deleting a tag block through the API or Flutter surfaces FK-errored. The surfaces disagreed — Studio had been patched, the others had not. Fix: clear the join rows first on every delete path, each module carrying its own copy of the rule.
Divergence made visible: the same defect, fixed on one surface, still live on two.
3
Bug 3 · Publish blindly un-archives
A status transition that ignored direction: on kontent and the API, publishing a post just set status to published — so publishing an already-archived post silently un-archived it. Fix: the transition checks where it is coming from, and publish never resurrects an archived post by accident.
Not an FK 500 this time — a silent wrong-thing, the other face of the same shape.
4
Bug 4 · A category cascade can never delete a post
The category bulk-delete offers a delete action for its posts — but any post with blocks is pinned by a NoAction FK, so delete FK-errors every time. Worse, the cascade hardcoded the delete action, silently discarding an editor who chose reassign. Fix: the cascade archives posts to the default category instead of hard-deleting — applying archive-not-delete, which the schema already enforces.
The same defect class with no divergence at all: post-delete exists only here.
5
Bug 5 · Archive-direction skipped the sentinel
The mirror of bug 3, surfaced in the pre-commit review: archiving a post through UpdatePost skipped the -1 sentinel that signals the archive direction, so the transition could move the wrong way. Fix: honor the sentinel on every status-changing path. Finding it in review — not in prod — is exactly what the pre-commit gate is for.
Four found up front; the fifth caught by the pre-commit review, before it shipped.
Total Recall — the AI Memory Layer (master)
Total Recall — the AI Memory Layer (master)
Jul 25, 2026 Total Recall
← Back to Total Recall