Repository navigation
A graph with a number in an any slot cannot be loaded: checksum mismatch #734
Description
Regression introduced by: #713 (merged 2026-08-27T17:50:06Z, d5b1375)
Fixed by: #712's envelope work — same PR, but tracked here because the breakage is independent of it.
A graph holding a number in an any slot cannot be loaded. LoadGraph rejects it as tampered.
Measured
An any slot holding a string or a bool reloads cleanly. One holding an integer or a float does not:
LoadGraph(json): op.LoadGraph: checksum mismatch:
document "sha256:0d223c4d2bcc047dc3d8334ef1afc2333e91d716543286a3a8c769bfc800aea8",
recomputed "sha256:c38dcb7cf1d39b566dbceb75ca41f3e1942cb3740982b00db95a62d9a242b92b"
Covered by TestLoadGraph_AGraphWithAValueInAnAnySlotLoads (pkg/op/any_slot_types_test.go), which isolates
the failure by value kind.
Mechanism
#713 replaced json.Unmarshal(data, &p) with a UseNumber decoder so a serialized number could be read
against the declared type of the parameter it fills. An any parameter has no declared type, so
tryParseSerializedNumber matches no case and convertDirect returns the value untouched: the slot reloads
holding a json.Number.
The canonical form a checksum covers is computed from the RELOADED values via yaml.Marshal
(pkg/op/graph.go:850). json.Number is a named string type, so yaml renders it as a quoted scalar where a
float64 or int64 renders as a bare number. The recomputed checksum therefore differs from the stored one
and the document is refused.
Why it did not happen before #713
With plain json.Unmarshal, a number in an any slot came back as float64, and yaml.Marshal(float64(42))
emits 42 — byte-identical to what the original int64(42) or float64(42) produced. The checksum matched
precisely because the type loss was invisible to it. #713 did not create the type loss (#712 filed that, as a
silent defect); it converted a silent loss into a hard failure.
Severity
Higher than #712's P2. #712 describes a value arriving with the wrong type; this refuses the document
outright, and it is live on develop.
Not separately fixable
The fix is #712's envelope: the document records the type, the decoder resolves it to a concrete Go value, and
the canonical form reconciles. There is no smaller repair that does not guess — resolving a json.Number by
literal syntax, or falling back to float64, both infer at read time what the writer knew at write time.
Reverting UseNumber would regress #711.
Acceptance
- A graph with an integer in an
anyslot loads - A graph with a float in an
anyslot loads - Strings and bools in
anyslots keep loading - The reloaded graph's recomputed checksum matches the stored one, from json and from yaml
Activity
A value in an any slot carries its type, a resource slot records the …
David-Noble-at-work commentedon Sep 8, 2026
Fixed by #858 (a5b2fea), phase 2 of the #712 plan: an enveloped value is resolved at the decoder, so a graph with an integer or a float in an
anyslot loads and its recomputed checksum matches the stored one from JSON and from YAML. Pins:TestLoadGraph_AGraphWithAValueInAnAnySlotLoads,TestLoadGraph_JSONDocumentVerifies,TestLoadGraph_YAMLDocumentVerifies. The PR body's "Closes #712, #734, #735" closed only the first and last; closing by hand.