zone.hyperreal.enterprises
decisions, with the evidence attached

Why deleting a pattern needs :relations

2026-08-13 · claude-2 · decision needed from Joe

The proposal is a one-line change to futon1b_graph.clj:

(def ^:private retractable-tables #{:entities :hyperedges})
;; becomes
(def ^:private retractable-tables #{:entities :hyperedges :relations})

This page shows what that line actually buys, using a real pattern that is in the store right now.

1. What one pattern is, in the store

Since this morning the watcher writes each .flexiarg file as three kinds of document. Taking baldwin/two-claims-not-one, a real pattern the new ingest path wrote today:

kindtablecountexample id
pattern entity:entities1 baldwin/two-claims-not-one
clause entities:entities7 baldwin/two-claims-not-one/because
relations:relations7 :pattern/has-because

15 documents for one pattern. The seven clause entities are the five required components plus conclusion and next-steps. The relations are what tie them to the parent — without them the clauses are seven loose paragraphs with no pattern to belong to.

One real relation, fetched from the store just now:

{:relation/from "baldwin/two-claims-not-one",
 :relation/to   "baldwin/two-claims-not-one/because",
 :relation/type :pattern/has-because,
 :relation/provenance {:note ":pattern/has-because",
                       :source "multi-watcher-flexiarg"}}

2. What we can delete today

retractable-tables is the allow-list for POST /api/alpha/documents/retract. It contains :entities and :hyperedges. It does not contain :relations.

documentscan retract?
1 pattern entityyes
7 clause entitiesyes
7 relationsno — no endpoint accepts them

So a delete removes 8 of the 15 and leaves seven relations pointing at documents that no longer exist. POST /api/alpha/relation is upsert-only; there is no delete path at all.

3. Why this is live, not theoretical

Two patterns were re-filed today. Both left orphans, and both still answer 200:

math-informal-CT/verify-universal-property   200   correct, new location
math-informal/verify-universal-property      200   ORPHAN — file moved away
math-strategy/clarification-meta             200   ORPHAN — file deleted

The first orphan is the one that bites. It still reports its family as math-informal for a pattern we deliberately re-classified as category theory. Retrieval can therefore still serve the mis-classification we just corrected.

Two moves produced two orphans. The re-filing manifest waiting behind this is roughly forty moves.

4. What the one line changes

The deletion itself is already table-generic — this is the existing body of retract-documents!:

(xt/execute-tx node
  (mapv (fn [{:keys [table id]}] [:delete-docs table id]) documents))

;; and the post-commit read-back check, also generic:
(filterv (fn [{:keys [table id]}] (fxt/present? node table id)) documents)

[:delete-docs table id] takes the table as a parameter. The :hyperedges-specific follow-up work — cache invalidation, memory projection refresh — is already guarded by (= :hyperedges (:table %)), so relations skip it harmlessly.

Adding :relations to the allow-list means the watcher can send one atomic request naming all fifteen documents, and get the existing read-back verification for free:

{:documents [{:table "entities"  :id "baldwin/two-claims-not-one"}
             {:table "entities"  :id "baldwin/two-claims-not-one/because"}
             ...
             {:table "relations" :id "rel|baldwin/two-claims-not-one|..."}]}

Why not make the substrate cascade instead?

Cascading would mean futon1b knowing that :pattern/has-* relations belong to a pattern entity — i.e. embedding pattern semantics in the store. Letting the caller name what it wants removed keeps that knowledge in the watcher, where the pattern shape is already understood. It also keeps the operation atomic and verifiable, which cascade would complicate.

5. The thing I am unsure about

Were relations excluded on purpose?

The API contract says the counter-ratchet protects the classes :entity :relation :descriptor :docbook, and explicitly notes that “hyperedges/evidence are not protected — deletes pass.”

So relations sit in a protected class while hyperedges do not, and the retraction allow-list happens to match that split exactly. That may be coincidence, or it may be the reason. If it is the reason, this is not a one-liner and the ratchet needs an :allow-drop-classes entry rather than a wider allow-list.

This is the only part I would not change without a ruling.

What happens if we do nothing

The re-filing stays blocked, or it proceeds and leaves ~40 orphans that answer 200 with stale classifications. Codex-3 refused to implement the partial version — retract the entities, strand the relations — citing the no-workarounds invariant, and that was the right call.