Files
mesh-delivery/modules/mesh-delivery-forge-bridge/internal/groups/owedask.go
T
jschoubben 0b597eb417 FB3, FB4, FB5: the bridge performs what the gate owes through the seat git and says each outcome, reads groups from the seat, and its owed tools
Taken over from the join (6ada2089, f1417ab0, 6e162a60, 3a4e62b0, 73fb42d7, ee15a280, a5476f62, 94a4e62a, 33907a81, e519cc87, e8f268c1) and fitted to the final
contracts: the seat's events are pull-updated, pull-merged, pull-closed, forge-delete; OwedEnded keeps only the fields §164 names; merge-pull
takes no ask. A merge act is performed only as a switched act (§169); from G the gate owes none (§167).

Builds: design 47 G §43, §45, §157, §161, §162, §163, §164, §167, §168, §169, §171, §250

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
2026-11-11 11:54:56 +01:00

96 lines
3.4 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.
OwedAsksBucket = 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)
}