Content-id includes the composite debug-name — consider a topology-canonical form #171

Closed
opened 2026-07-01 03:06:15 +02:00 by Brummel · 2 comments
Owner

Finding (cycle 0094, #158 acc 1)

The content id (topology_hash / graph introspect --content-id) is SHA256 of the
canonical blueprint_to_json form, which INCLUDES the composite's debug name. So two
blueprints that express the same topology but differ only in their composite debug-name
get DIFFERENT content ids. Concretely: an op-script (graph build, composite named
"graph") and the Rust stage1_signal builder (named "stage1_signal") produce different
canonical forms — also a named vs unnamed Sub and an unbound vs bound Bias.scale — so
they never share a content id even when expressing the same SMA-cross topology.

Question

Should the content id key on a topology-canonical form that excludes non-load-bearing
debug symbols (the composite name, per invariant 11) rather than the byte-canonical
blueprint_to_json form? That would let the same topology dedup / content-address
regardless of authoring path (C11/C12), at the cost of a second canonical form and a
change to the shipped cycle-0092 topology_hash values (goldens).

Not urgent

Reproduction (acc 2, shipped) is unaffected — it stores + reloads byte-exact blueprints
keyed by the byte-canonical id, so the debug-name riding along is harmless. This only
matters for cross-authoring-path dedup / identity. Filed as a forward design item.

## Finding (cycle 0094, #158 acc 1) The content id (`topology_hash` / `graph introspect --content-id`) is SHA256 of the canonical `blueprint_to_json` form, which INCLUDES the composite's debug name. So two blueprints that express the same topology but differ only in their composite debug-name get DIFFERENT content ids. Concretely: an op-script (`graph build`, composite named "graph") and the Rust `stage1_signal` builder (named "stage1_signal") produce different canonical forms — also a named vs unnamed `Sub` and an unbound vs bound `Bias.scale` — so they never share a content id even when expressing the same SMA-cross topology. ## Question Should the content id key on a *topology-canonical* form that excludes non-load-bearing debug symbols (the composite name, per invariant 11) rather than the byte-canonical `blueprint_to_json` form? That would let the same topology dedup / content-address regardless of authoring path (C11/C12), at the cost of a second canonical form and a change to the shipped cycle-0092 `topology_hash` values (goldens). ## Not urgent Reproduction (acc 2, shipped) is unaffected — it stores + reloads byte-exact blueprints keyed by the byte-canonical id, so the debug-name riding along is harmless. This only matters for cross-authoring-path dedup / identity. Filed as a forward design item.
Brummel added the idea label 2026-07-01 03:06:15 +02:00
Author
Owner

Reconnoitred; remains a forward design item — crux fork captured (2026-07-01)

Reconnoitred under a C21-follow queue against the current tree. Confirmed
disjoint from other in-flight CLI work and not dependency-blocked, but it is
design-unsettled, so it stays a forward design item this round rather than being
opened. Capturing the fork so the eventual cycle starts from a populated space:

Golden re-baselining cost is ~zero. No 64-hex hash literal is pinned anywhere
(swept crates/docs/fieldtests); the content-id tests assert only len==64

  • hex + determinism + a distinguishing property (I64:4 vs 5 must differ).
    The one byte-canonical golden (crates/aura-engine/src/blueprint_serde.rs,
    signal_serializes_to_canonical_golden) is affected only if blueprint_to_json's
    byte form changes — which the issue argues against. This points at a separate
    hash-only canonicalization
    used by content_id/topology_hash, leaving
    blueprint_to_json and the byte-exact reproduction store untouched; under that
    shape no golden re-baselines and new tests are added (an op-script and the Rust
    stage1_signal builder share a content id).

The crux fork (why this is not a one-line derivation). "What a
topology-canonical form strips" has three axes, only the first settled:

  1. Composite debug-name — SETTLED strip (invariant 11: non-load-bearing
    symbol).
  2. Primitive instance names (named-vs-unnamed Sub) — likely strippable
    (edges wire by index; names are debug-only), but not explicitly ratified.
  3. Param bindings (unbound-vs-bound Bias.scale) — UNSETTLED and the crux.
    ParamSpec carries no default and Bias reads p[0] unconditionally, so an
    unbound param is a FREE sweep axis, not "use-default." Conflating unbound
    scale with bound scale=0.5 would merge an OPEN blueprint with a CLOSED
    instance — collapsing the sweep-store key's meaning and colliding with the
    shipped distinguishing test. So identity-canonicalization's treatment of param
    openness is a real semantic decision (dedup granularity + run identity), not a
    derivation from invariant 11.

Low urgency: reproduction (byte-exact store, byte-canonical key) is unaffected;
this only bites cross-authoring-path dedup / identity (C11/C12). Appropriate to
open as its own design cycle when cross-path dedup is wanted.

## Reconnoitred; remains a forward design item — crux fork captured (2026-07-01) Reconnoitred under a C21-follow queue against the current tree. Confirmed disjoint from other in-flight CLI work and not dependency-blocked, but it is design-unsettled, so it stays a forward design item this round rather than being opened. Capturing the fork so the eventual cycle starts from a populated space: **Golden re-baselining cost is ~zero.** No 64-hex hash literal is pinned anywhere (swept `crates`/`docs`/`fieldtests`); the content-id tests assert only `len==64` + hex + determinism + a *distinguishing* property (`I64:4` vs `5` must differ). The one byte-canonical golden (`crates/aura-engine/src/blueprint_serde.rs`, `signal_serializes_to_canonical_golden`) is affected only if `blueprint_to_json`'s *byte* form changes — which the issue argues against. This points at a **separate hash-only canonicalization** used by `content_id`/`topology_hash`, leaving `blueprint_to_json` and the byte-exact reproduction store untouched; under that shape no golden re-baselines and new tests are *added* (an op-script and the Rust `stage1_signal` builder share a content id). **The crux fork (why this is not a one-line derivation).** "What a topology-canonical form strips" has three axes, only the first settled: 1. **Composite debug-name** — SETTLED strip (invariant 11: non-load-bearing symbol). 2. **Primitive instance names** (named-vs-unnamed `Sub`) — likely strippable (edges wire by index; names are debug-only), but not explicitly ratified. 3. **Param bindings** (unbound-vs-bound `Bias.scale`) — UNSETTLED and the crux. `ParamSpec` carries no default and `Bias` reads `p[0]` unconditionally, so an *unbound* param is a FREE sweep axis, not "use-default." Conflating unbound `scale` with bound `scale=0.5` would merge an OPEN blueprint with a CLOSED instance — collapsing the sweep-store key's meaning and colliding with the shipped distinguishing test. So identity-canonicalization's treatment of param openness is a real semantic decision (dedup granularity + run identity), not a derivation from invariant 11. Low urgency: reproduction (byte-exact store, byte-canonical key) is unaffected; this only bites cross-authoring-path dedup / identity (C11/C12). Appropriate to open as its own design cycle when cross-path dedup is wanted.
Brummel added this to the Project environment — the project-as-crate authoring loop milestone 2026-07-02 14:17:30 +02:00
Author
Owner

Design reconciliation (specify, cycle 0104)

The fork this issue captured is resolved for spec production; an adversarial review of the initial derivation surfaced a load-bearing coupling that reshaped it.

  • Fork: what the topology-canonical form strips → composite debug-name (axis 1, settled by invariant 11), primitive instance names (axis 2), and role/output re-export names (axis 4) are stripped; role/output ORDER is kept (order is the wiring); param openness and bound values are identity-bearing (axis 3) — per-slot boundness is textually encoded in the byte form (bound slots carry pos+value, open slots are absent), so an open blueprint never shares an id with any bound instance, and a sweep family's hash is taken over the OPEN reloaded doc (member values inject as positional cells at bootstrap, never into the JSON — verified at crates/aura-cli/src/main.rs:3025). Basis: derived — the sweep-store-key argument in the 2026-07-01 recon comment, the shipped distinguishing test (a bound I64:4 vs 5 must differ), and C23's positional-identity reading.
  • Fork: where the canonical form lives → a NEW sibling id (identity hash), NOT a redefinition of topology_hash. Basis: derived under adversarial review, which REFUTED the initial "hash-only swap" shape: topology_hash is triple-purposed — introspect identity, the reproduction store key (put_blueprint, main.rs:3227), and reproduce's fetch anchor (get_blueprint, main.rs:2256) — and reproduce recovers the bootstrap point BY NAME against the fetched blueprint's param_space (point_from_params, main.rs:2286), so primitive names ARE load-bearing on that path. A name-blind hash as store key would let cross-path name-twins clobber the byte-exact store (fs::write is last-writer-wins) and derail a legitimately stored family's reproduction. Consequence: byte-canonical topology_hash keeps all three roles untouched (zero golden churn, reproduction provably unaffected); the identity hash is additive.
  • Fork: which consumers take the identity hash now → introspection only (the cross-authoring-path comparison this issue asks for); no manifest field and no store keyed by it this cycle. Basis: derived — no dedup consumer exists yet; the deferred-until-a-consumer discipline recorded in the C15 realization applies unchanged.

Status: design settled — ready for spec production.

## Design reconciliation (specify, cycle 0104) The fork this issue captured is resolved for spec production; an adversarial review of the initial derivation surfaced a load-bearing coupling that reshaped it. - **Fork: what the topology-canonical form strips** → composite debug-name (axis 1, settled by invariant 11), primitive instance names (axis 2), and role/output re-export names (axis 4) are stripped; role/output ORDER is kept (order is the wiring); **param openness and bound values are identity-bearing** (axis 3) — per-slot boundness is textually encoded in the byte form (bound slots carry pos+value, open slots are absent), so an open blueprint never shares an id with any bound instance, and a sweep family's hash is taken over the OPEN reloaded doc (member values inject as positional cells at bootstrap, never into the JSON — verified at crates/aura-cli/src/main.rs:3025). Basis: derived — the sweep-store-key argument in [the 2026-07-01 recon comment](issues/171#issuecomment-2), the shipped distinguishing test (a bound I64:4 vs 5 must differ), and C23's positional-identity reading. - **Fork: where the canonical form lives** → a NEW sibling id (identity hash), NOT a redefinition of topology_hash. Basis: derived under adversarial review, which REFUTED the initial "hash-only swap" shape: topology_hash is triple-purposed — introspect identity, the reproduction store key (put_blueprint, main.rs:3227), and reproduce's fetch anchor (get_blueprint, main.rs:2256) — and reproduce recovers the bootstrap point BY NAME against the fetched blueprint's param_space (point_from_params, main.rs:2286), so primitive names ARE load-bearing on that path. A name-blind hash as store key would let cross-path name-twins clobber the byte-exact store (fs::write is last-writer-wins) and derail a legitimately stored family's reproduction. Consequence: byte-canonical topology_hash keeps all three roles untouched (zero golden churn, reproduction provably unaffected); the identity hash is additive. - **Fork: which consumers take the identity hash now** → introspection only (the cross-authoring-path comparison this issue asks for); no manifest field and no store keyed by it this cycle. Basis: derived — no dedup consumer exists yet; the deferred-until-a-consumer discipline recorded in the C15 realization applies unchanged. Status: design settled — ready for spec production.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Brummel/Aura#171