The promise this product makes — a caller never writes an expression, so a caller always knows what a call costs — is a promise made to an application, through the driver. The operator's shell is a different tool with a different audience, and it has inherited that promise by default rather than by decision.
That may well be right. An operator who can do things the application cannot is how most databases end up with a query language nobody can bound. But "we thought about the operator and concluded the same rules apply" and "the operator got the application's rules because nobody asked" produce identical code and completely different futures.
Store.Explore compiles every command typed at the prompt into an
Operation — asOperation in internal/store/explore.go.
So the operator's vocabulary is exactly the application's shapes, by construction.
And every ad-hoc read through the shell writes to the change log and commits:
s.record(Change{
Kind: ChangeRead,
Collection: access.Collection,
Key: access.Key,
Operation: &operation,
By: Attribution{Operation: "explore", Actor: caller.Actor},
})
s.Commit()
The rows themselves are deliberately not recorded, and the reason in the code is good: a log holding everything anybody read would be a second copy of the database, growing fastest exactly when somebody is investigating. But that is an argument about what is written. It is not an argument about whether a read writes at all, and the second question was never asked out loud.
It is not a proposal to give the operator a query language. That would be the same
mistake in the other direction, and it is the thing the whole design avoids. The ad-hoc access an
operator already has — get, scan, count — is
bounded and typed, and nothing here argues for unbounding it.
It is also not a claim that the audit trail is wrong. Recording what an operator looked at is a real property with real value, and a database that quietly forgets who read what is worse than one that remembers.