InvokeVersion, so an older declaration is reachable
Declaring a name that is already declared writes a new version and leaves every older one stored and
runnable — that is DeclareOperation's behaviour, and it is what makes an audit
record naming an operation readable years later. The wire has carried a version field since before
this ticket: internal/server's call struct decodes it and
store.Store.Invoke takes it.
Nothing in either Go client could set it. So every version a redeclaration left behind was stored, readable, runnable by the engine, and unreachable by anybody holding the published package: the data was there, the declaration was there, and there was no way to ask for it.
Client.InvokeVersion(name, version, args) is added on internal/wire
(internal/wire/wire.go:328) and forwarded on the public package
(sapedb.go:152). Zero means the newest, which is what Invoke now asks for.
The field is left off the request entirely when it is zero, matching the omitempty on the
server's struct and on the TypeScript client's request body — sending an explicit 0 where every
other client sends nothing is a difference with no meaning that somebody would one day have to
explain.
It is a name on a published surface, and it is the only way to reach a feature the server already had.
It is a second method rather than a third parameter on Invoke, because
Invoke's signature is part of a surface this repository has already published; taking it
back would break every caller for a field almost none of them pass. The two alternatives were worse:
a variadic version ...int compiles for Invoke(name, args, 1, 2, 3) and reads
in the documentation as something it is not, and an options struct means either a second method anyway
or the same breaking change with more ceremony. One extra name is the honest price — and after a
tag, adding it would cost a minor version rather than nothing.
TestAnOlderVersionIsStillCallableThroughThePublicPackage
(declare_live_test.go:321) declares three versions of one scan against a live daemon
and tells them apart by row count — not by reading back the version number
the server echoed, which would pass on a client that ignored the field.TestTheSurfaceIsExactlyTheseThirtyThreeNames (sapedb_test.go:44) lists
InvokeVersion among the nine *Client methods, so it cannot be dropped or
renamed silently. The guard's own name was renamed twice to match the number it guards.TestClientWrapperForwardsWithoutDroppingFields (sapedb_test.go:231)
— the public package is a wrapper, and a wrapper that drops a field is the failure mode this
catches.
The same work fixed a bug the wrapper test was the first thing to expose: Invoke sent its
body under "arguments" where the server's call struct decodes
"args". Nothing in this repository or its TypeScript sibling had ever invoked a declared
operation through this client — the shell only calls Explore and
WhatIsHere — so the server silently saw no arguments at all on every call.
Step.Version already does for composed operations.omitempty on the TypeScript request body, which is why omitting the zero mattered.