2b.1a: placement_digest gate on CpSharedL2PageAllocator (B1 proof obligation)

Add CpSharedL2PageAllocator.placement_digest() -- a deterministic, order-independent SHA-256 of the full replicated allocator state (free list + per-object ranges + committed set). This is the B1 proof-obligation gate from the 2b design (sec 2.4, Theorem 1): the write-through (2b.1) cross-rank assert hashes this each tick and EQ-checks it across CP ranks, turning placement-determinism into a fail-loud runtime invariant -- any reserve/release fired on a subset of ranks or with a per-rank arg changes the digest.

6 unit tests (pure-Python, no torch): determinism, changes-on-reserve, identical-op-sequence => identical-digest, divergent-op => different-digest, reserve+release restores digest (free-list coalescing), placement-order sensitivity. Full suite 81/81 green on g0033 syh-dev-new.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-20 12:32:25 +00:00
parent 17cbfa31c9
commit 7850aab1a2
2 changed files with 109 additions and 0 deletions

View File

@@ -10,6 +10,7 @@ orchestration are intentionally left to later phases.
from __future__ import annotations
import ctypes
import hashlib
import inspect
import mmap
import operator
@@ -950,6 +951,37 @@ class CpSharedL2PageAllocator:
for _, length in free_by_slab
)
def placement_digest(self) -> str:
"""Deterministic content hash of the full replicated allocator state.
The B1 proof-obligation gate (see docs_internal/cp_hicache_2b_pooled_l2_b1_design.md
sec 2.4, Theorem 1): two allocators that applied the IDENTICAL op sequence
over identical initial state produce identical digests on every CP rank, so
the runtime cross-rank assert can prove placement determinism each tick. Any
per-rank divergence -- a reserve/release fired on a subset of ranks, or with
a per-rank arg -- changes the digest. Order-independent within each container
(sorted) so dict/set iteration order cannot perturb it. Hashes the durable
placement state: the free list, the per-object ranges, and the committed set.
"""
h = hashlib.sha256()
for payload in sorted(self._free_by_payload):
h.update(b"P" + payload.encode())
for slab_id in sorted(self._free_by_payload[payload]):
h.update(b"|s" + str(slab_id).encode() + b":")
for base, length in sorted(self._free_by_payload[payload][slab_id]):
h.update(f"{base},{length};".encode())
for object_key in sorted(self._ranges_by_object):
ranges = self._ranges_by_object[object_key]
for payload in sorted(ranges):
r = ranges[payload]
h.update(
f"#{object_key}/{payload}={r.slab_id},{r.base_page},"
f"{r.num_pages};".encode()
)
for object_key in sorted(self._committed_objects):
h.update(b"@" + object_key.encode() + b";")
return h.hexdigest()
def _allocate_contiguous(self, payload_kind: str, num_pages: int) -> tuple[int, int]:
for slab in self._slabs_by_payload[payload_kind]:
free_ranges = self._free_by_payload[payload_kind][slab.slab_id]