S1: seat settle gains a delayed nak and its capability, for a step given again (issue 1748)
mesh/delivery merged: source.main moved to novox/mesh-sdk@ccf6cf460dbd
mesh/delivery-group group fix/1748-transport-retry merged: every member is merged
mesh/check checking ca46b8938f90 on ccf6cf460dbd | novox/mesh-sdk#183 in group fix/1748-transport-retry

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-09 22:48:34 +01:00
parent 366c4ab507
commit ca46b8938f
2 changed files with 30 additions and 2 deletions
+18
View File
@@ -86,8 +86,15 @@ const (
SettleNak = "nak" SettleNak = "nak"
SettleTerm = "term" SettleTerm = "term"
SettleProgress = "progress" SettleProgress = "progress"
// SettleNakAfter hands the message back to be delivered again after a delay (SeatSettleAfter): the bus's own
// delayed redelivery, so no part keeps a timer for it.
SettleNakAfter = "nak-after"
) )
// CapSeatSettleDelay is the capability a tool runner states when it applies SettleNakAfter; a module that settles with
// it asks it in its manifest's capabilities, so its want waits on a node whose runner does not state it.
const CapSeatSettleDelay = "seat-settle-delay"
// SeatPull pulls at most one message from a held seat's worker the module's membership lists, waiting at most wait // SeatPull pulls at most one message from a held seat's worker the module's membership lists, waiting at most wait
// (none when 0, at most 25 s): nil when none came. Unlike SeatTake nothing is fetched ahead: a build module pulls an // (none when 0, at most 25 s): nil when none came. Unlike SeatTake nothing is fetched ahead: a build module pulls an
// ask only when it has a free slot (novox/hq ADR 0525 §4; issue 1887 A11). A message never settled is delivered again // ask only when it has a free slot (novox/hq ADR 0525 §4; issue 1887 A11). A message never settled is delivered again
@@ -124,6 +131,17 @@ func SeatSettle(id, fate string) error {
return err return err
} }
// SeatSettleAfter hands the message pulled under id back to be delivered again after delay (SettleNakAfter). The
// runtime refuses a delay of 0 or below; a runtime that does not state CapSeatSettleDelay refuses the fate.
func SeatSettleAfter(id string, delay time.Duration) error {
c, err := live()
if err != nil {
return err
}
_, err = c.ask("mesh/seat.settle", map[string]any{"id": id, "fate": SettleNakAfter, "delay_ms": delay.Milliseconds()}, seatTimeout)
return err
}
// SeatRecord reads one key of a record a seat keeps for this module, under its own name, or nil. // SeatRecord reads one key of a record a seat keeps for this module, under its own name, or nil.
func SeatRecord(bucket, key string) (*StateEntry, error) { func SeatRecord(bucket, key string) (*StateEntry, error) {
c, err := live() c, err := live()
+12 -2
View File
@@ -23,7 +23,10 @@ func TestABundlePullsAndSettlesThroughTheRuntime(t *testing.T) {
return nil, err return nil, err
} }
got <- p got <- p
return map[string]any{}, SeatSettle(p.ID, SettleAck) if err := SeatSettle(p.ID, SettleAck); err != nil {
return nil, err
}
return map[string]any{}, SeatSettleAfter("p2", time.Minute)
}}}, inR, outW) }}}, inR, outW)
}() }()
lines := bufio.NewScanner(outR) lines := bufio.NewScanner(outR)
@@ -59,8 +62,15 @@ func TestABundlePullsAndSettlesThroughTheRuntime(t *testing.T) {
} }
asked = next() asked = next()
params, _ = asked["params"].(map[string]any) params, _ = asked["params"].(map[string]any)
if asked["method"] != "mesh/seat.settle" || params["id"] != "p1" || params["fate"] != "ack" { if asked["method"] != "mesh/seat.settle" || params["id"] != "p1" || params["fate"] != "ack" || params["delay_ms"] != nil {
t.Fatalf("settled %v", asked) t.Fatalf("settled %v", asked)
} }
send(map[string]any{"jsonrpc": "2.0", "id": asked["id"], "result": map[string]any{}}) send(map[string]any{"jsonrpc": "2.0", "id": asked["id"], "result": map[string]any{}})
// S1 (issue 1748): a delayed nak names its fate and its delay in milliseconds.
asked = next()
params, _ = asked["params"].(map[string]any)
if asked["method"] != "mesh/seat.settle" || params["id"] != "p2" || params["fate"] != "nak-after" || params["delay_ms"] != 60000.0 {
t.Fatalf("settled after a delay %v", asked)
}
send(map[string]any{"jsonrpc": "2.0", "id": asked["id"], "result": map[string]any{}})
} }