Files
mesh-delivery/modules/mesh-delivery-forge-bridge/internal/groups/owedask.go
T
jschoubben 1883e6e3e4 D1 (forge bridge): the bridge names its own module from its credential's
Its states' buckets, its stream's subjects and its finding observer take the module its credential names (the tools: the module the runtime serves them as) instead of constants of its name; a guard test finds none in its code.

Builds: design 47 G A-296, §428

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 22:47:02 +01:00

97 lines
3.5 KiB
Go

package groups
import (
"encoding/base64"
"encoding/json"
"fmt"
"strings"
"time"
)
// What a person asks of the bridge's performing of what is owed (novox/hq design 47 *What the gate owes the forge*): the
// tools bridge_owed (its consumers' backlog), bridge_owed_again and bridge_owed_drop write an ask into the state
// owed-asks and wait for the bridge's answer there, as bridge_group_end_again does, since the bridge is the other program
// and serves no request subject (ADR 0437 §4).
const (
// OwedAsksState is the state of asks about what is owed, one key per kind of ask and act.
OwedAsksState = "owed-asks"
)
// OwedAsksBucket is its bucket on the bus, `<module>_<state>` of the module the bridge runs as, its credential's (A-296).
func OwedAsksBucket(module string) string { return module + "_" + OwedAsksState }
// The kinds of an owed ask.
const (
OwedAgain = "again"
OwedDrop = "drop"
OwedBacklog = "backlog"
)
// Backlog is one durable consumer of a seat's stream owed as the bridge reads it: the last event applied (its stream
// sequence), the events waiting and those delivered and not yet acknowledged, and the oldest of them.
type Backlog struct {
Stream string `json:"stream"`
Consumer string `json:"consumer"`
LastApplied uint64 `json:"last-applied"`
Waiting uint64 `json:"waiting"`
Held uint64 `json:"held"`
Oldest time.Time `json:"oldest,omitzero"`
OldestSecs int64 `json:"oldest-secs,omitempty"`
Error string `json:"error,omitempty"`
}
// OwedAsk is a value of owed-asks: what was asked (of the act Of said under ID, for again and drop), why, who and when,
// and once the bridge answered, when, what it came to (the outcome said as owed.ended) and its words; a backlog ask is
// answered with the consumers' backlog.
type OwedAsk struct {
Kind string `json:"kind"`
Of string `json:"of,omitempty"`
ID string `json:"id,omitempty"`
Why string `json:"why,omitempty"`
By string `json:"by,omitempty"`
At time.Time `json:"at"`
Answered time.Time `json:"answered,omitzero"`
Outcome string `json:"outcome,omitempty"`
Answer string `json:"answer,omitempty"`
Backlog []Backlog `json:"backlog,omitempty"`
}
// Done says the bridge answered it.
func (a OwedAsk) Done() bool { return !a.Answered.IsZero() }
// NewOwedAsk is an ask's key and value: a backlog ask under the key backlog, an act's under its kind, its seat and its id
// (unpadded base64url, as an id may hold what a key may not). A drop says why.
func NewOwedAsk(kind, of, id, why, by string, at time.Time) (string, []byte, error) {
a := OwedAsk{Kind: kind, Of: of, ID: id, Why: strings.TrimSpace(why), By: by, At: at.UTC()}
key := kind
switch kind {
case OwedBacklog:
a.Of, a.ID = "", ""
case OwedAgain, OwedDrop:
if of == "" || id == "" {
return "", nil, fmt.Errorf("an owed act is named by the seat that said it and its id")
}
if kind == OwedDrop && a.Why == "" {
return "", nil, fmt.Errorf("a drop says why")
}
key = kind + "." + of + "." + base64.RawURLEncoding.EncodeToString([]byte(id))
default:
return "", nil, fmt.Errorf("no owed ask %q: again, drop or backlog", kind)
}
v, err := json.Marshal(a)
return key, v, err
}
// DecodeOwedAsk reads a value of owed-asks.
func DecodeOwedAsk(data []byte) (OwedAsk, error) {
var a OwedAsk
if err := json.Unmarshal(data, &a); err != nil {
return a, err
}
switch a.Kind {
case OwedAgain, OwedDrop, OwedBacklog:
return a, nil
}
return a, fmt.Errorf("no owed ask %q", a.Kind)
}