# Ticket #000070 — AnchorN + π*_w_object: deterministic world-object canonicalization

**Status:** open · Phase 1 deliverable scoped · dav1d GO-with-rewrite
landed 2026-06-01 (full review at
`docs/dav1d-reviews/000070-spatial-anchor-pi-w-object--2026-06-01.txt`).
**Opened:** 2026-05-31

## Goal

Implement the first executable v7-W world-object canonicalization
kernel. The kernel takes:

```
hard_hash_32                  one committed 32-byte SHA-256
spatial_anchor_seed           v7-W manifest-published, dedicated
WorldDimensionGrammar         frozen grammar declaring N + N mappers
world_manifest                privacy class, axiom pack, version pins
```

and produces:

```
canonical_world_object_bytes  deterministic byte string
world_object_hash             SHA-256(canonical bytes)
object_record                 grammar_hash + axiom_pack_hash +
                              manifest_hash + seed_hash + per-dim values
```

A single committed hash deterministically expands into N named regions
via the HMAC-SHA-512 KDF already shipped in
`arborist/substrate/anchor_prg.py` (#000035). Each region drives one
quantized dimension under a registered mapper. **N is grammar-decided,
not hard-coded.**

## Axis split (cross-ref #000071)

```
#000070  intra-world state instantiation
         AnchorN + WorldDimensionGrammar + AxiomPack + Manifest →
         canonical world-state records

#000071  inter-world treaty grammar
         two chains decide whether commitments cross the gap →
         agreement | translation | embassy | quarantine | no_bridge
```

Joseph6 (six-dimension Joseph @TrudoJo framework) is the **first
registered default grammar**, not THE substrate ontology. See §5.

## Hard constraints

- **A1–A3 axioms.** Canonical encoding · public quantization for
  proof-path state · collision-resistant hash. No new axiom.
- **Quantized integer state only.** Continuous floats do not enter
  the proof path (v7-W manifest hard rule, paper §1.2). Mappers
  emit integers; any continuous-domain math (e.g. Euler rotation)
  quantizes deterministically before commitment.
- **Hard-hash family stays SHA-256 commit + HMAC-SHA-512 expansion.**
  Soft locality indices (Morton / H3 / S2 / Hilbert / geohash) live
  **outside** the proof path per CLAUDE.md soft-hash rule.
- **Domain separation from #000035.** A NEW seed
  `spatial_anchor_seed` is published in the v7-W manifest beside
  `anchor_prg_seed`; #000035 §2.4 "single-purpose — never reuse
  `anchor_prg_seed`" is honored without a `PHI_PRG_VERSION` bump.
- **No new `audit_mode` token.** π*_w_object emits commitments,
  not warrants. The four-rung ladder (POINTER-LINKED →
  ANCHOR-WARRANTED → EVIDENCE-WARRANTED → ENTAILMENT-VERIFIED) is
  not extended.
- **No SQL at Phase 1.** A new `pi_star/spatial-anchor-object@v1`
  registry entry folds into `canonicalization_version` (one of the
  v9.8 8-dim cache_key dimensions). Persistence to a
  `world_state_cells` table is deferred to Phase 3.
- **Privacy fail-closed.** Missing `privacy.class` in the manifest
  is a HARD reject — no PUBLIC fallback.
- **Grammars must be FROZEN before proof-path use.** Axioms MAY
  *select* a grammar (LLM may *propose* candidates), only
  deterministic validators may *accept* them. Accepted grammars
  are canonicalized, hashed, KAT-tested, versioned. `grammar_hash`
  binds into every downstream commitment.
- **Topic-named, not version-prefixed** (CLAUDE.md naming rule).
  Lands under `arborist/world/` and `arborist/pi_star/`, not
  `arborist/v7w/` or `arborist/spatial/`.

---

## 1. AnchorN — the substrate primitive

`AnchorN` is the generic key-derivation primitive. Given a hard hash
and a frozen grammar declaring N regions, it expands the hash into N
named 32-byte regions via HMAC-SHA-512 counter mode.

```python
@dataclass(frozen=True)
class AnchorN:
    hard_hash: bytes              # 32 bytes (SHA-256 of source)
    seed: bytes                   # spatial_anchor_seed from manifest
    grammar_hash: bytes           # binds region identities to a grammar
    regions: tuple[bytes, ...]    # N × 32 bytes


def split_anchor_n(
    hard_hash: bytes,
    seed: bytes,
    grammar_hash: bytes,
    n: int,
) -> AnchorN:
    """Generic N-region split. Counter-mode HMAC-SHA-512 over the
    triple (hard_hash || seed || grammar_hash || counter_be8) yields
    64-byte blocks; first 32 bytes of each block is the region.

    Same construction as arborist.substrate.anchor_prg._expand;
    the difference is grammar_hash binds into the expansion so the
    same hard_hash under two different grammars yields disjoint
    regions. Without that binding, swapping grammars at proof-path
    time would silently reuse anchor bytes for new semantics.
    """
```

Lives at `arborist/substrate/spatial_anchor.py`. Substrate-level (not
under `arborist/world/`) because AnchorN is the generic primitive any
future world kernel may consume — v7-W is one consumer, but other
consumers may want N-region splits without the v7-W semantics.

---

## 2. WorldDimensionGrammar — frozen, hash-pinned, registry-addressable

A grammar declares N and the N region-to-mapper bindings. It is a
canonical-bytes-serializable record; its SHA-256 is `grammar_hash`.

```python
@dataclass(frozen=True)
class WorldDimensionGrammar:
    name: str                     # e.g. "joseph6"
    version: str                  # e.g. "v1"
    n: int                        # number of regions
    region_mappers: tuple[str, ...]   # registered mapper names
    region_names: tuple[str, ...]     # human-readable per-region label
    axiom_pack_ref: str               # "axiom-pack@v1" registry slot


def canonical_grammar_bytes(g: WorldDimensionGrammar) -> bytes:
    """Stable serialization. Field order pinned, integers little-endian."""


def grammar_hash(g: WorldDimensionGrammar) -> bytes:
    return sha256(canonical_grammar_bytes(g))


def validate_grammar(g: WorldDimensionGrammar) -> None:
    """Raise GrammarInvalid if:
      - n != len(region_mappers) != len(region_names)
      - any mapper name isn't in the registry
      - axiom_pack_ref isn't in the registry
      - reserved-name collision (e.g. names starting with '_')
    """
```

Lives at `arborist/world/grammar.py` (new file).

---

## 3. Quantization mappers

Five registered mappers ship in Phase 1 (Joseph6's full set). Each
takes a 32-byte region and returns a quantized integer record.

| mapper | input | output | notes |
|---|---|---|---|
| `map_octree_position()` | 32 bytes | `(level: uint8, cell: uint256)` | **uint256, not uint64** — preserves entropy at any octree depth |
| `map_scale_level()` | 32 bytes | `uint16` | quantized log-scale, fixed scale-ladder per grammar |
| `map_rotation_euler_ypr()` | 32 bytes | `(yaw: uint16, pitch: uint16, roll: uint16)` | **NOT "SO(3)"** — quantized Euler triple, no continuous SO(3) coverage claim |
| `map_symbol_table_index()` | 32 bytes | `uint64` | modulo over a fixed-size symbol palette |
| `map_passthrough_hex()` | 32 bytes | `hex_str` | 64-char hex; preserves the full region byte string for downstream interpretation |

All mappers are **deterministic** and **registered by name@version**.
Adding a mapper = bump
`canonicalization_version: spatial-anchor-object@v1 → v2`.

Lives at `arborist/world/pi_star/object.py`.

### 3.1 Critical correction (dav1d §16)

The original Anchor6 spec called `map_octree_position()` to return
`uint64`. **That loses entropy at octree depth > 8.** Use `uint256`:
preserves the full 32-byte region. The bench (§7) measured chi²
uniformity at L=2,3,4 only; production deployments will go deeper,
and uint64 truncation would silently bias cell selection at the bits
that get dropped.

### 3.2 Critical correction (dav1d §17)

The original Anchor6 spec described H₃ as "quantized SO(3) rotation."
**Do not overclaim.** The mapper is a quantized Euler ypr triple.
Quantized Euler is not SO(3) coverage — adjacent rotation operations
are not metrically adjacent in this quantization. Name the mapper
`map_rotation_euler_ypr()` so consumers don't import SO(3) semantics.

---

## 4. π*_w_object — the canonicalizer

```python
def derive_world_object_record(
    hard_hash: bytes,
    seed: bytes,
    grammar: WorldDimensionGrammar,
    manifest: WorldManifest,
) -> WorldObjectRecord:
    """End-to-end:
      1. validate_grammar(grammar)            — fail-closed
      2. validate_manifest(manifest)          — privacy.class HARD-required
      3. anchor = split_anchor_n(...)         — N regions
      4. per region: invoke registered mapper
      5. assemble record with all four hashes
      6. SHA-256(canonical(record)) = world_object_hash
    """
```

Output record carries the FOUR identity hashes plus per-dimension
values:

```python
@dataclass(frozen=True)
class WorldObjectRecord:
    grammar_hash: bytes           # which grammar produced this
    axiom_pack_hash: bytes        # which axiom pack the grammar pins
    manifest_hash: bytes          # which deployment manifest
    seed_hash: bytes              # which spatial_anchor_seed
    dimensions: tuple[Any, ...]   # length N; one per region_mapper
    world_object_hash: bytes      # SHA-256 of the canonical bytes
```

All four hashes are **required for replayability across grammar
versions**. A consumer presented with a `world_object_hash` and the
four identity hashes can re-derive the record byte-for-byte by
loading the named grammar + axiom pack + manifest + seed.

Lives at `arborist/world/pi_star/object.py`.

A separate registry-facing adapter at
`arborist/pi_star/spatial_anchor_object.py` exposes the canonicalizer
as a registered π*:

```python
PI_STAR_NAME = "spatial-anchor-object"
PI_STAR_VERSION = "v1"


def canonicalize(raw: bytes) -> bytes:
    """Registry contract. Parses raw bytes into the (hard_hash,
    seed_ref, grammar_ref, manifest_ref) tuple, dispatches to
    derive_world_object_record, returns the canonical bytes."""
```

---

## 5. Joseph6 — the first registered grammar (example)

Joseph @TrudoJo's six-dimension framework lands as the first
canonical grammar. It is **an example of what a WorldDimensionGrammar
looks like**, not THE substrate.

```python
JOSEPH6 = WorldDimensionGrammar(
    name="joseph6",
    version="v1",
    n=6,
    region_names=(
        "position",        # H₁
        "scale",           # H₂
        "rotation",        # H₃
        "material",        # H₄
        "links",           # H₅ — reserved for π*_w_relation sibling
        "behavior",        # H₆
    ),
    region_mappers=(
        "map_octree_position",
        "map_scale_level",
        "map_rotation_euler_ypr",
        "map_symbol_table_index",       # material palette
        "map_passthrough_hex",          # raw links reserved
        "map_symbol_table_index",       # behavior code
    ),
    axiom_pack_ref="axiom-pack-joseph6@v1",
)
```

Joseph6's `grammar_hash` is computed at module-load time; KATs (§7)
pin it byte-for-byte. Future grammars (Joseph12, Cartesian5,
spatial+temporal-7, etc.) register through the same mechanism —
none is privileged over Joseph6 except by being chosen at
deployment-manifest level.

**Why ship Joseph6 in Phase 1:**

- Proves AnchorN can host a non-trivial grammar end-to-end.
- Gives the substrate-paper a concrete worked example readers can
  follow without inventing one.
- Lets the bench (§7) measure a real grammar's avalanche / uniformity
  / domain-separation properties rather than a synthetic.

---

## 6. Phase 1 deliverables

**What ships:**

1. **Substrate primitive:**
   - `arborist/substrate/spatial_anchor.py` — `AnchorN`,
     `split_anchor_n()`, `SPATIAL_ANCHOR_VERSION`,
     `PLACEHOLDER_SPATIAL_SEED`
2. **Grammar layer:**
   - `arborist/world/grammar.py` — `WorldDimensionGrammar`,
     `canonical_grammar_bytes()`, `grammar_hash()`,
     `validate_grammar()`
3. **Mappers + record:**
   - `arborist/world/pi_star/object.py` — 5 mappers + `WorldObjectRecord`
     + `derive_world_object_record()`
4. **Registry adapter:**
   - `arborist/pi_star/spatial_anchor_object.py` — registered π*
5. **Joseph6 grammar:**
   - `arborist/world/grammars/joseph6.py` (or registry-registered
     constant) — the `JOSEPH6` grammar definition + axiom-pack-joseph6@v1
6. **KATs:**
   - `bench/fixtures/spatial-anchor-object/known-answer-tests.jsonl`
     — at minimum: zero-hash, max-hash, low-entropy hash, three
     mid-entropy hashes; expected outputs for Joseph6 grammar
7. **Tests:**
   - `tests/test_spatial_anchor.py` — AnchorN unit + integration
   - `tests/test_world_dimension_grammar.py` — grammar validation
   - `tests/test_pi_star_spatial_anchor_object.py` — canonicalizer
     round-trip + KAT replay
8. **Paper amendment:**
   - `docs/_source/merkle-agi-v7w-spatial-temporal.rst` — new section
     citing Joseph (@TrudoJo) as the framework author; AnchorN +
     grammar layer as the architecture
9. **Status:**
   - `arborist/world/__init__.py` STATUS bumps from
     `namespace_reserved` → `world_object_kernel_in_progress`

**What does NOT ship in Phase 1:**

- ❌ Relation / event / place / agent_trace kernels (siblings)
- ❌ SQL `world_state_cells` table (Phase 3)
- ❌ New `audit_mode` token (ever — hard)
- ❌ `verifier_policy_hash` change (commitments ≠ warrants)
- ❌ Continuous tensors anywhere in proof path
- ❌ Runtime LLM-decided dimensionality (axioms select, validators
     accept, grammars freeze)
- ❌ Bench at Phase 1 over and above the pre-review empirical bench
     (§7) — KATs replace open-ended bench

---

## 7. Pre-review empirical evidence (already shipped)

`bench/spatial_anchor_validation.py` (commit `55b651f`, 561 lines)
validated the HMAC-SHA-512 expansion properties before this ticket's
implementation phase. **Survives the AnchorN reframe** — N just
changes the loop count; the per-region properties measured are
unchanged.

Reproduce:

```
make bench-spatial-anchor [SPATIAL_N=10000]
```

| § | Bench | Result | What it proves |
|---|---|---|---|
| 1 | Avalanche (single-bit hash flip → Hamming distance over output) | mean 767.85 / 768 bits (z = -0.49) | counter-mode HMAC-SHA-512 is PRF-good; fixed-offset slicing inherits the property |
| 2 | Octree chi² uniformity (L=2,3,4) | \|z\| < 1 every level | `map_octree_position()` modulo `8^L` is uniform; no LE-conversion bias |
| 3 | Birthday-bound collision ratio (L=4,6) | 0.989 / 1.038 | structural skew absent; collisions follow PRF expectation |
| 4 | Cross-region Pearson r (15 pairs of H₁..H₆) | r ∈ [-0.018, +0.012], max \|r\|/stderr = 1.80 | counter-mode block independence holds empirically |
| 5 | Domain separation (Arm A distinct seeds vs Arm B shared seed) | Arm A 767.91 bits independent · Arm B **0.00 bits exact collision** | dedicated `spatial_anchor_seed` discipline is load-bearing; sharing seeds yields byte-identical anchor maps |

Result file `bench/spatial_anchor_validation_results.md` pinned to
the RNG seed `0xa8c90e551fd34427` — re-runs hash-stable.

The bench answers 5 of the original 10 design questions (Q1 seed
source, Q2 segmentation method, Q3 octree mapper, Q8 endianness,
Q9 KAT adversarial vectors). The remaining 5 are design decisions
the dav1d review already resolved or paper-editorial calls (see §10).

---

## 8. Phase 2 (deferred — siblings under #000013)

When Phase 2 opens:

- **Second grammar registration** — proves the registry mechanism
  works for grammars other than Joseph6. Candidate: Cartesian5
  (position-only, 5-region for 3D + 2D-affine variants).
- **π*_w_relation** — relation kernel sibling. Different grammar
  (relations don't have a single anchor), different π* registry slot.
- **π*_w_event / π*_w_place / π*_w_agent_trace** — three more
  sibling kernels. Each is its own ticket.

The substrate primitive `AnchorN` is unchanged across siblings —
only the grammar + mappers vary.

---

## 9. Phase 3 (deferred — persistence)

- SQL `world_state_cells` table (or whatever shape mesh-replay needs)
- v9.8 `cache_key` may gain a new dimension if Phase 3 reveals it
  must (defer to bench evidence)
- Cold-pack export for v7-W records (sibling of #000061 cold-pack)
- `audit_events` writer for π*_w_* commitments (no new event_type
  needed; existing `pi_star_emit` handles this)

---

## 10. Open questions

Only the items the dav1d review left genuinely open. The empirical
bench (§7) closed Q1/Q2/Q3/Q8/Q9; dav1d's review §0.7 answered
Q4/Q5/Q6/Q7.

1. **Q10 — substrate-paper amendment wording.** The paper at
   `docs/_source/merkle-agi-v7w-spatial-temporal.rst` needs a new
   section introducing AnchorN + grammar layer (not fixed Anchor6).
   The technical content is in this ticket; the paper's voice,
   diagram style, and reading order stay for fox to set.

2. **Grammar registry persistence shape.** Are grammars Python
   constants in `arborist/world/grammars/*.py`, manifest-declared
   YAML, or hash-pinned blobs in a SQLite table? Phase 1 starts
   Pythonic; Phase 2 may need otherwise as more grammars land.

3. **Axiom pack registry vs declared inline.** `axiom_pack_ref`
   is a registry slot; the axiom pack itself (the physics / math
   constraints the grammar pins) needs its own canonicalization
   and KAT discipline. May warrant a sibling ticket if axiom packs
   grow beyond simple shape.

---

## 11. Cross-references

- **#000013** — v7-W substrate reservation. This ticket's Phase 1
  flips `arborist/world/__init__.py` STATUS from
  `namespace_reserved` → `world_object_kernel_in_progress`.
- **#000035** — HMAC-SHA-512 KDF (`anchor_prg.py`). AnchorN reuses
  `_expand` byte-for-byte. Domain separation via dedicated
  `spatial_anchor_seed` keeps #000035's KAT freeze intact.
- **#000071** — World-bridge grammar. Bridge ChainRoots may carry
  `world_dimension_grammar_hash` from this ticket's grammar layer.
- **#000015** — π* cross-domain composition. Eventually composes
  spatial π* with linguistic π* via the composition theorem.
- **#000061** — cold-pack object store. Future v7-W record persistence
  may piggyback on the cold-pack distribution tier.

---

## 12. Five-step alignment

1. **Make requirements less dumb.** Joseph's framework named the
   six-dimension axis; dav1d's review named "freeze grammars before
   proof-path use." Both are load-bearing requirements with people
   attached.
2. **Delete the part / process.** AnchorN replaces Anchor6 — same
   primitive, generalized. Joseph6 stays as the worked example.
   Five mappers, not "however many feels right." `map_rotation_so3`
   is deleted; renamed `map_rotation_euler_ypr` so consumers don't
   import the SO(3) overclaim.
3. **Simplify and optimize.** uint256 for H₁ position (no truncation
   ambiguity); grammar identity binds into AnchorN expansion (no
   silent reuse across grammars); manifest-published `spatial_anchor_seed`
   (no need to bump `PHI_PRG_VERSION`).
4. **Accelerate cycle time.** KATs replace open-ended bench at
   Phase 1. Joseph6's grammar_hash is a fixed byte string; deviations
   show up as KAT failures, not bench drift.
5. **Automate.** Last. The π* registry mechanism + KAT discipline
   automates what the paper specifies; no automation before the
   grammar registry mechanism freezes.

---

## 13. Review history

- **2026-05-31** — original ticket filed (fixed Anchor6 framing,
  H₁..H₆ ontology, six-dimension Joseph kernel). Full original
  proposal preserved in git history at commit `55b651f` / `e2c8e2c`
  (pre-rewrite tip).
- **2026-05-31** — pre-review empirical bench shipped in `55b651f`
  validating HMAC-SHA-512 expansion properties (§7); survives the
  rewrite unchanged.
- **2026-06-01** — dav1d de-novo review: GO-with-rewrite. Generic
  AnchorN replaces fixed Anchor6; Joseph6 is the first registered
  grammar, not THE substrate. uint256 H₁ correction; no SO(3)
  overclaim; canonical record carries four identity hashes;
  privacy fail-closed; no SQL / new audit_mode / verifier_policy_hash
  change at Phase 1. Full review (1904 lines) archived at
  `docs/dav1d-reviews/000070-spatial-anchor-pi-w-object--2026-06-01.txt`.
  This ticket text is the rewrite dav1d's review requires.
