Pin the node-engine at its pull request's generation refusal, so the validator reads a declaration's generation (hq issue 234)

This commit is contained in:
2026-10-11 11:23:12 +02:00
parent 3b6b54a501
commit 7bd04342b6
5 changed files with 54 additions and 11 deletions
+29 -1
View File
@@ -1378,6 +1378,20 @@ type Declaration struct {
// what it wrote for it — its resources are absent from the declaration, and absence would
// otherwise read as removal.
LeftOut []string
// Generation is the assignment generation this declaration was composed from: the controller's
// counter, raised in the same transaction as every change to what is assigned where (novox/hq
// issue 234). The sequence orders arrival and cannot tell a later send that carries an older view
// of the assignments; this can. A node-engine refuses a declaration composed from a generation
// older than the highest it applied — unless it is a put-back — because applying it would
// undeclare what the mesh still assigns. Zero is a declaration from a controller that claims none,
// and is applied as before.
Generation int64
// PutBack says this declaration is a gate putting a machine back (novox/hq issue 234): it carries
// the generation of what it puts back, which may be older than what the machine applied, and is
// not refused for it.
PutBack bool
}
// LeftOutModuleOf says which left-out module a recorded resource belongs to, if any: its id is the
@@ -1542,6 +1556,11 @@ type envelope struct {
Epoch int64 `json:"epoch,omitempty"`
// LeftOut is optional on the wire too, and absent when nothing was left out (ADR 0163).
LeftOut []string `json:"left_out,omitempty"`
// Generation and PutBack are optional on the wire too (novox/hq issue 234): absent is a controller
// that claims no generation. **An older host refuses these keys**, decoding strictly; a controller
// sends them only to a host whose reports say `reads_generation`.
Generation int64 `json:"generation,omitempty"`
PutBack bool `json:"put_back,omitempty"`
}
func parse(raw []byte, allowActions bool) (*Declaration, error) {
@@ -1559,8 +1578,17 @@ func parse(raw []byte, allowActions bool) (*Declaration, error) {
}
d := &Declaration{Version: env.Version, For: env.For, Adoption: env.Adoption, Sequence: env.Sequence,
Epoch: env.Epoch, LeftOut: env.LeftOut}
Epoch: env.Epoch, LeftOut: env.LeftOut, Generation: env.Generation, PutBack: env.PutBack}
var problems []string
if env.Generation < 0 {
// Below zero would read as "none claimed" and pass every refusal of an older generation.
problems = append(problems, fmt.Sprintf("an assignment generation below zero (%d) is not one the "+
"mesh assigns", env.Generation))
}
if env.PutBack && allowActions {
// A put-back is a gate's, sent by the mesh; a carried bundle puts nothing back.
problems = append(problems, "a carried bundle says it is a put-back, and only the mesh's gate can say that")
}
if env.Sequence < 0 || env.Epoch < 0 {
// Below zero is no order any controller assigns, and read as "none claimed" it would let the
// declaration past every refusal of what is older.