store.DeclareOperation could only be reached through sapedb apply, and that
command opens the database file directly and takes the exclusive lock — a run while the server
is up is refused. So adding one operation meant stopping the server, applying, and starting it again:
every live connection dropped, for a change that writes one key.
Frame type 13, Declare (internal/protocol/frame.go:57),
sends the same declaration down a connection that already exists.
internal/server/declare.go is the handler.
It is not a second, looser way in:
Explore is — by answering the
welcome's challenge with the server's own secret. A connection string says which database to reach,
never what may be declared once there.s.reach, exactly as a call does: being an operator
says what you may do, never which database you may do it to.store.DeclareOperation — the function sapedb apply calls,
which begins with validateOperation. There is no second copy of the rules here and
nothing is normalised on the way in.
Frame numbers 1…13 freeze at the tag. Adding a frame type afterwards is additive and cheap;
leaving a gap where Declare should have been, and filling it later, is not. And the
product's whole claim is that a declaration is the only way into a database — a product where
declaring one requires taking the database down is a claim with an asterisk on it.
TestDeclaringNeedsMoreThanAConnectionString
(internal/server/declare_test.go:112) — the operator proof, not the connection
string, is what opens this path.TestDeclaringTheSameNameAgainIsANewVersionAndTheOldOneStillRuns
(declare_test.go:160) and
TestADeclarationOverTheWireSurvivesTheServerBeingRestarted
(declare_test.go:365).TestDeclaringFromManyConnectionsAtOnceIsOneWriterAtATime
(declare_test.go:281) — this takes the write lock, not the read one, and that is
measured rather than asserted.apply:
TestDeclaringOverTheWireRefusesExactlyWhatApplyRefuses
(internal/cli/declare_apply_test.go:44) runs one table down both paths;
TestAScanDeclaredOverTheWireMustSayHowManyRowsItMayReturn
(declare_test.go:50) and
TestACountDeclaredOverTheWireMustSayHowFarItWalks
(internal/server/count_limit_test.go:27) cover the two limit rules.TestEveryRequestFixtureDecodesIntoTheStructThatServesIt
(internal/server/fixture_decode_test.go:28) is the general form of a guard that existed
for exactly one frame, which is why the others were free to drift. It found one: the invoke case in
fixtures/frames.json carried {"op": ...} while call's field
has always been json:"command". Both sides round-tripped perfectly because both were
reading the same wrong file, and that file is published as the conformance specification other
languages build against. Fixed in 59909d0.TestDeclaringOverTheWireGoesIntoTheLogWithAnActor
(internal/server/declare_actor_test.go:41) — 44b8298 gave
DeclareOperation a Caller so the change log says who.declaring carries an
Operation and nothing else; Store.Declare(spec) is still reachable only
through sapedb apply, with the server stopped. That is SAPE-14,
still open, and it is why ISS-13 is narrower than it looks.declare.go:107) and not to
invoke, which writes to the same database through the same store —
ISS-8.internal/store's Explore says it validates
a typed access "with the same validation a declaration gets"; for the limit rule that has never been
true, because asOperation forces Limit to MostRows before
validateOperation runs. Right answer for a shell, wrong answer for a declaration, and
the warning is written next door rather than fixed.declare.go:30 cites
TestDeclareRefusesExactlyWhatApplyRefuses, a name that does not exist in the tree. The
test is real; it is called TestDeclaringOverTheWireRefusesExactlyWhatApplyRefuses and
it lives in internal/cli. The CHANGELOG has the name right.