Every entry in the change log has carried an Attribution since there was a log: a write
names the operation that made it and the actor it ran for, and even a read through the shell names
"explore" and the operator. 44b8298 closed the last gap for
operations:
// internal/store/ops.go:391 — DeclareOperation
if _, err := s.record(Change{
Kind: ChangeOperation,
Operation: &operation,
By: Attribution{Operation: "declare", Actor: caller.Actor},
}); err != nil {
Declaring a collection was not part of that change:
// internal/store/store.go:168 — Store.Declare(spec), a new collection
s.record(Change{Kind: ChangeDeclare, Collection: spec.Name, Spec: &spec})
No By. Store.Declare takes no Caller at all, so there is nothing
it could write.
grep -n "s.record(Change{" internal/store/*.go gives six non-test sites. Four carry
a By: ops.go:391 and two in explore.go (122, 231), plus the
write path. Three do not, and all three are in store.go:
store.go:168 — declaring a new collectionstore.go:256 — redeclaring one (index and rollup changes)store.go:361 — ChangeDrop, dropping a collectionAttribution.Actor is omitempty (internal/store/log.go:60),
so these entries carry no actor field at all — not an empty one. A reader cannot distinguish
"nobody recorded" from "the field did not exist yet".sapedb apply, which opens the file directly and takes the exclusive lock. The
Declare frame carries an Operation and nothing else
(internal/server/declare.go:45-53), and the desktop workbench says outright that it
cannot declare a collection. So today, whoever did it was holding the file — stood at the
machine, server stopped.TestDeclaringOverTheWireGoesIntoTheLogWithAnActor
(internal/server/declare_actor_test.go:41) and
TestApplyRecordsWhoDeclared (internal/cli/declare_apply_test.go:377).
There is no equivalent for a collection.Does it lose data, break a promise on a public surface, or stop somebody installing and running? No. The change log still records that the collection was declared, with its full spec and its LSN — only the who is missing. No wire shape depends on it, and nothing is blocked.
Defer, on the same reasoning that made the operation version tolerable before the
Declare frame existed: the one identity available on this path is the account the command was pointed
at, which is a label rather than a proof, because anybody holding the exclusive lock could have
written the bytes by hand. A label is still better than no claim at all — that is exactly the
argument 44b8298 made for apply — but the gap between "no claim" and
"weak claim" is smaller than the gap the operation path had, where the actor can now be a remote
host.
Declare for collections on the wire, and then a collection — or a
drop — can come from another host over a live connection, with the log recording nobody. The
fix belongs in SAPE-14's scope, not after it.Store.Declare's signature to take a Caller touches every caller
in the tree. Doing it now, while those callers are two, is cheaper than doing it when SAPE-14 has
added a third under time pressure.ChangeDrop should be fixed in the same pass, and arguably first — a dropped
collection with nobody's name on it is the entry somebody will actually go looking for.