← 1.0.0 backlogs ISS-32

A collection can be dropped. An operation, once declared, is in that database forever.

Type
Missing capability — an asymmetry nobody chose
Found while
SAPE-9's dossier
Triage
Needs a decision — see below; it is not obviously a bug
Status
Open
Severity
It makes one of SAPE-11's acceptance criteria unobservable

What was measured

Store.Drop removes a collection — internal/store/store.go:367. There is no equivalent for an operation: grep -rniE "dropoperation|removeoperation|deleteoperation" returns zero, against a control that finds Drop immediately.

So a declared name stays in that database for the life of the file. Redeclaring it adds a version; it does not replace or retire anything, and an older version stays reachable by number through InvokeVersion.

Why it matters more now than it did yesterday

SAPE-10 landed signed bundles, and a bundle is a vocabulary somebody else wrote. An uninstalled bundle leaves its operations behind, because there is nothing to uninstall with. The bundle format itself is additive by design and says so; this is the other half of that sentence, and it was not written down anywhere.

It also reaches a ticket that is open right now: SAPE-11's second acceptance criterion asks for a removal to be confirmed immediately in the listing. There is no code path that could produce that observation today.

Why this is a decision rather than a bug

A declaration is not just a definition; it is a name callers already use. Removing one breaks every caller of it, silently, at their next call — which is exactly the shape of failure this whole product is built to make impossible. So "operations cannot be removed" may be the right answer, deliberately, and the fault is only that nobody wrote it down.

The alternatives are not equal, and the distinction is worth stating because it is easy to blur:

The part that cannot wait

Only this: if retiring needs a field on the declaration, that field freezes at the tag — the same argument SAPE-9's dossier makes about recording who declared an operation, and it lands on the same decision at the same moment. The rule can wait; a field cannot.

The field landed; the rule did not

A declaration now records the identity that declared it. store.Operation carries an optional declaredBy with a kind and an identity — the ed25519 key that signed the bundle it arrived in, or the caller's actor otherwise. It is assigned by the store, never taken from the caller's own JSON, and each version records the identity that declared that version.

So the half of this issue that froze at the tag is settled, and the half that did not is still open. Nothing can remove a declared operation. A name is in that database for the life of the file, and — since SAPE-9 — a namespace a key has taken is a namespace that key holds forever, because a claim is released by nothing.

The reason the field could not wait turned out to be sharper than the one written above. A dump carries collections and operations and no attribution of any kind, so before this a restored database held every operation and knew who declared none of them. Putting the identity on the declaration means the dump carries it for free — measured by applying, dumping, and restoring into a different empty database. One thing a restore still forgets: the namespace claims themselves, which remains a dump-format change and its own ticket.