Tighten the canonical blueprint contract: byte-exact emit across surfaces + value ergonomics #164
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The 2026-06-30 milestone fieldtest (
fieldtests/milestone-topology-as-data/) surfaced two underspecified corners of the C24 "canonical, versioned" blueprint contract. Neither is a behavioural bug; both are contract gaps worth tightening.Trailing-newline divergence across emit surfaces.
aura graph buildframes its output with a trailing\n; the libraryblueprint_to_jsondoes not (648 vs 647 bytes; content character-identical). Validated:diff <(aura graph build < ops.json) lib_emit.jsondiffers only by the final newline. The ledger calls the format "canonical" but does not state whether the canonical artifact includes a trailing newline. This becomes load-bearing once content-addressed topology identity lands (#158): a content hash of the same blueprint would differ by which surface emitted it.The loaded
Compositevalue carries noDebug/PartialEq.blueprint_from_jsonreturnsResult<Composite, LoadError>, butCompositederives neither, so{:?},unwrap_err(), and composite-level equality assertions do not compile — every identity / round-trip check must route through ablueprint_to_jsonstring compare. The ledger frames the blueprint as a first-class data value the World "owns, stores, mutates, structurally searches"; value-like ergonomics (compare, debug-print) are implied but unspecified.Acceptance:
Compositevalue carriesDebug/PartialEq, or that JSON-string-via-blueprint_to_jsonis the intended equality surface.Design decisions (cycle 0091, #164)
Two derivable forks, resolved with rationale. The second discharges acceptance
box 2's "or" arm.
Fork 1 — the canonical artifact carries no trailing newline. The library
blueprint_to_jsonreturns the JSON value with no trailing newline (647 bytesfor the SMA-cross fixture);
aura graph buildadded one viaprintln!(648).Resolution: the library serializer is the single canonical source, and
aura graph buildswitches toprint!to emit those exact bytes. Basis(derived): content-addressed topology identity (#158) is defined as a hash over
the canonical
blueprint_to_jsonform, so the CLI is a transport that must notmutate the canonical bytes, and a JSON value carries no line terminator. The
cycle-0088
.out.jsongoldens are re-recorded without the trailing newline; therule is stated in the C24 ledger entry. The loader keeps tolerating a trailing
newline (lenient parse, Tier-1 robustness), so a newline-bearing input still
loads.
Fork 2 — the canonical JSON is the blueprint equality/identity surface; no
second in-memory
PartialEq/Debugis added.Composite/BlueprintNodecannot derive
Debug/PartialEq: the underlyingPrimitiveBuilder(
aura-corenode.rs) holds abuild: Box<dyn Fn(&[Cell]) -> Box<dyn Node>>closure, which is neither
DebugnorPartialEq. Equality could therefore onlybe defined over the data fields (type identity + schema + bound params) via
manual impls eliding the closure. Resolution: do not add a second equality
notion;
blueprint_to_jsonis the canonical equality / identity surface. Basis(derived): the canonical serialization already defines blueprint identity (it is
exactly what #158 content-addresses and what round-trip checks compare); a
separate in-memory
PartialEqwould be a redundant second source of truth thatmust be kept in lockstep with the serialization — a drift hazard for no
functional gain. Test-side identity / round-trip checks compare the canonical
JSON string. This records the decision under acceptance box 2's "or
JSON-string-via-
blueprint_to_jsonis the intended equality surface" arm.