The ledger pipe asks the runtime through stdio.BusState (it answered ErrNoPipe before), an operation names the call the runtime proved (stdio.CallIDOf), and the page takes the runtime's word on its consumer (stdio.OnHeartbeat): a beat is every subscription heard, a missed heartbeat marks each subscribed source at once with the runtime's why. Rows in TestASourceThatDoesNotAnswerIsSaidAndAskedAgainOnABackoff and TestTheLedgerStoreRefusesWhatTheLedgerWouldHaveToTakeBack; each failed before the change. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
98 lines
3.4 KiB
Go
98 lines
3.4 KiB
Go
package main
|
|
|
|
// The module's reach to its bus state (novox/hq ADR 0595 §9, ADR 0597 §10): the calls of mesh-sdk's buscalls, made over
|
|
// the bundle's pipe to the tool runner, and the module's states, made through the SDK's State. Both are interfaces here,
|
|
// so the ledger's store is tested against a ledger played in the test and not against a bus.
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
)
|
|
|
|
// Pipe makes one call of buscalls on the tool runner: the method, its request, and where its answer is decoded.
|
|
type Pipe interface {
|
|
Call(method string, request, answer any) error
|
|
}
|
|
|
|
// KV is one of the module's states (stdio.KeptState is one).
|
|
type KV interface {
|
|
Get(key string) (*stdio.StateEntry, error)
|
|
Put(key string, value any) (uint64, error)
|
|
Create(key string, value any) (uint64, error)
|
|
Update(key string, value any, revision uint64) (uint64, error)
|
|
Delete(key string) error
|
|
Keys() ([]string, error)
|
|
}
|
|
|
|
// States are the module's states by their local names in `requires`.
|
|
type States interface {
|
|
State(name string) KV
|
|
}
|
|
|
|
// sdkStates are the module's states through the SDK.
|
|
type sdkStates struct{}
|
|
|
|
func (sdkStates) State(name string) KV { return stdio.State(name) }
|
|
|
|
// callKey carries, in a context, the id of the tool call an operation serves: the tool runner stamps the caller it
|
|
// proved for that call as the operation's author (buscalls.MetaCall, ADR 0597 §7).
|
|
type callKey struct{}
|
|
|
|
// withCall is ctx naming the tool call in flight.
|
|
func withCall(ctx context.Context, call string) context.Context {
|
|
if call == "" {
|
|
return ctx
|
|
}
|
|
return context.WithValue(ctx, callKey{}, call)
|
|
}
|
|
|
|
// callOf is the tool call ctx names, or "" for the module's own act.
|
|
func callOf(ctx context.Context) string {
|
|
s, _ := ctx.Value(callKey{}).(string)
|
|
return s
|
|
}
|
|
|
|
// sdkPipe is the pipe through the SDK: each call on the module's own ledger is asked of the runtime, which serves it
|
|
// from the module's own bindings only (stdio.BusState, ADR 0595 §9).
|
|
type sdkPipe struct{}
|
|
|
|
// busState is the runtime's call on the module's own bus state: stdio.BusState, a test's stand-in.
|
|
var busState = stdio.BusState
|
|
|
|
func (sdkPipe) Call(method string, req, answer any) error {
|
|
raw, err := busState(method, req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if answer == nil || len(raw) == 0 {
|
|
return nil
|
|
}
|
|
return json.Unmarshal(raw, answer)
|
|
}
|
|
|
|
// callIDOf is the id of the tool call that was handed args, as the tool runner names it in the call's `_meta` under
|
|
// buscalls.MetaCall (stdio.CallIDOf): empty for the module's own act, whose operation carries the module's account alone.
|
|
var callIDOf = stdio.CallIDOf
|
|
|
|
// noteCall holds the call a caller's write serves for as long as it runs, and ctxFor names it to the ledger: the tool
|
|
// runner stamps the caller it proved for that call as the operation's author (ADR 0597 §7), never a name the caller or
|
|
// the module gives. A caller's writes are serialised by the module, so one note per proven caller is the call in flight.
|
|
func (k *KB) noteCall(caller string, args map[string]any) func() {
|
|
id := callIDOf(args)
|
|
if id == "" || !proven(caller) {
|
|
return func() {}
|
|
}
|
|
k.calls.Store(caller, id)
|
|
return func() { k.calls.Delete(caller) }
|
|
}
|
|
|
|
// ctxFor is the context of a write by caller: the call it serves, when it serves one.
|
|
func (k *KB) ctxFor(caller string) context.Context {
|
|
if id, ok := k.calls.Load(caller); ok {
|
|
return withCall(context.Background(), id.(string))
|
|
}
|
|
return context.Background()
|
|
}
|