Skip to content

Commit 809fdde

Browse files
authored
Implement TPU primitive with commit functionality
1 parent 9bf344f commit 809fdde

1 file changed

Lines changed: 394 additions & 0 deletions

File tree

  • thought_simulator/requirements_20/system_playground/primitives/tpu
Lines changed: 394 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,395 @@
1+
"""
2+
TPU Primitive (Version 1.0)
3+
TP-UPDATE — sole deterministic commit authority for Path-A.
14
5+
Aligned with:
6+
- 20.46 (TPU Requirements, Option C Rewrite)
7+
- tpu_py_struc_pgm.md (Version 1.0)
8+
- 20.105 / 20.105.010–030 (writer authority + provenance)
9+
- 20.30 (safe-boundary discipline)
10+
- 20.95 (canonical ordering)
11+
- 20.12 (replay invariants)
12+
- progressive_lineup_testing.md
13+
"""
14+
15+
from __future__ import annotations
16+
17+
import copy
18+
import hashlib
19+
import json
20+
from typing import Any, Dict, List, Optional, Tuple
21+
22+
23+
PRIMITIVE_NAME = "tpu"
24+
25+
26+
def get_primitive_name() -> str:
27+
return PRIMITIVE_NAME
28+
29+
30+
class TPU:
31+
"""
32+
TPU is the deterministic commit engine of Path-A.
33+
34+
Public API
35+
----------
36+
tpu = TPU(tp_input, tp_update_request)
37+
tp_n1, audit = tpu.commit() # primary return shape
38+
# also available after commit:
39+
# tpu.tp, tpu.audit_record, tpu.error
40+
41+
Responsibilities (SHALL)
42+
------------------------
43+
- validate tp_update_request blocks
44+
- enforce writer-authority boundaries
45+
- enforce canonical ordering
46+
- enforce safe-boundary rules
47+
- apply updates atomically
48+
- enforce 1-TP-cycle lag semantics (commit appears only in N+1)
49+
- write commit provenance
50+
- produce tpu_audit_record and (on failure) tpu_error
51+
52+
Forbidden
53+
---------
54+
- generate meaning / interpret semantics
55+
- modify structural geometry, semantic_core, intake, current-turn context
56+
- perform routing or identity refinement
57+
- partial commits
58+
"""
59+
60+
# ----------------------------------------------------------------
61+
# Authoritative writer namespaces (minimal 20.105 model)
62+
# ----------------------------------------------------------------
63+
# Fields that meaning-layer blocks are NEVER allowed to carry.
64+
FORBIDDEN_IN_MEANING_BLOCKS = {
65+
"structural_geometry",
66+
"structural",
67+
"geometry",
68+
"semantic_core",
69+
"intake",
70+
"routing",
71+
"routing_metadata",
72+
}
73+
74+
# Blocks that belong to meaning / process layers
75+
MEANING_BLOCKS = ("idob_update", "mcb_update", "rbu_update", "cob", "cop", "cil", "isc")
76+
77+
def __init__(self, tp_input: Dict[str, Any], tp_update_request: Dict[str, Any]):
78+
# Working copy — we never mutate the caller's objects
79+
self.tp: Dict[str, Any] = copy.deepcopy(tp_input or {})
80+
self.req: Dict[str, Any] = copy.deepcopy(tp_update_request or {})
81+
self.tp_in: Dict[str, Any] = copy.deepcopy(tp_input or {}) # frozen snapshot for audit
82+
83+
self.audit_record: Optional[Dict[str, Any]] = None
84+
self.error: Optional[Dict[str, Any]] = None
85+
86+
# Deterministic commit counter derived from request seed / prior lineage
87+
self._commit_seq = self._derive_commit_sequence()
88+
89+
# ================================================================
90+
# Public entry point — exact ordering from tpu_py_struc_pgm.md §4
91+
# ================================================================
92+
def commit(self) -> Tuple[Dict[str, Any], Dict[str, Any]]:
93+
"""
94+
Execute the authoritative commit pipeline.
95+
96+
Returns
97+
-------
98+
(tp_n1, audit_record)
99+
100+
On validation failure the original TP is retained, an error
101+
object is attached, and the audit records the rejection.
102+
"""
103+
# 1–2 already performed in __init__ (read TP(N) + request)
104+
105+
# 3. Validate writer authority
106+
ok, reason = self._validate_writer_authority(self.req)
107+
if not ok:
108+
return self._reject("WRITER_AUTHORITY_VIOLATION", reason)
109+
110+
# 4. Validate update blocks (shape / boundedness)
111+
ok, reason = self._validate_update_blocks(self.req)
112+
if not ok:
113+
return self._reject("UPDATE_BLOCK_INVALID", reason)
114+
115+
# 5. Validate canonical ordering markers
116+
ok, reason = self._validate_canonical_ordering(self.req)
117+
if not ok:
118+
return self._reject("CANONICAL_ORDERING_VIOLATION", reason)
119+
120+
# 6. Validate safe-boundary conditions
121+
ok, reason = self._validate_safe_boundary(self.req)
122+
if not ok:
123+
return self._reject("SAFE_BOUNDARY_VIOLATION", reason)
124+
125+
# 7. Apply updates atomically
126+
updated_tp = self._apply_updates(self.tp, self.req)
127+
128+
# 8. Write commit provenance
129+
self._write_provenance(updated_tp)
130+
131+
# 9–10. Produce audit (and no error on success)
132+
audit = self._build_audit_record(
133+
status="committed",
134+
writer_authority="pass",
135+
canonical_ordering="pass",
136+
safe_boundary="pass",
137+
atomicity="pass",
138+
tp_n=self.tp_in,
139+
tp_n1=updated_tp,
140+
reason=None,
141+
)
142+
self.audit_record = audit
143+
self.error = None
144+
self.tp = updated_tp
145+
146+
# 11. Emit TP(N+1)
147+
return updated_tp, audit
148+
149+
# ================================================================
150+
# Validation helpers
151+
# ================================================================
152+
153+
def _validate_writer_authority(self, req: Dict[str, Any]) -> Tuple[bool, str]:
154+
"""
155+
HLR-20.46-003
156+
Each block may contain only fields permitted for that writer.
157+
Meaning-layer blocks must not carry structural / process geometry.
158+
"""
159+
for block_name in self.MEANING_BLOCKS:
160+
block = req.get(block_name) or {}
161+
if not isinstance(block, dict):
162+
continue
163+
for forbidden in self.FORBIDDEN_IN_MEANING_BLOCKS:
164+
if forbidden in block:
165+
return (
166+
False,
167+
f"{block_name} attempted write outside authority domain ({forbidden})",
168+
)
169+
return True, ""
170+
171+
def _validate_update_blocks(self, req: Dict[str, Any]) -> Tuple[bool, str]:
172+
"""
173+
Light structural validation of the known update blocks.
174+
Clarifying-field boundedness (10 / 100 / 4) is enforced when present.
175+
"""
176+
# Clarifying-field limits (HLR-20.46-025)
177+
# Look in mcb_update / cob / continuity-style payloads
178+
for block_name in ("mcb_update", "cob", "idob_update"):
179+
block = req.get(block_name) or {}
180+
cf = None
181+
if isinstance(block, dict):
182+
cf = block.get("clarifying_fields")
183+
if cf is None and "next_context" in block:
184+
cf = (block.get("next_context") or {}).get("clarifying_fields")
185+
if cf is not None:
186+
if not isinstance(cf, list):
187+
return False, f"{block_name}.clarifying_fields must be a list"
188+
if len(cf) > 10:
189+
return False, f"{block_name}.clarifying_fields exceeds max 10 (got {len(cf)})"
190+
return True, ""
191+
192+
def _validate_canonical_ordering(self, req: Dict[str, Any]) -> Tuple[bool, str]:
193+
"""
194+
HLR-20.46-004 / 20.95
195+
We accept an explicit canonical_ordering_hash when supplied;
196+
absence is tolerated for early testbenches.
197+
"""
198+
meta = req.get("metadata") or {}
199+
# Presence of a hash is recorded but not cryptographically verified here
200+
# (full 20.95 verification can be layered later).
201+
_ = meta.get("canonical_ordering_hash")
202+
return True, ""
203+
204+
def _validate_safe_boundary(self, req: Dict[str, Any]) -> Tuple[bool, str]:
205+
"""
206+
HLR-20.46-005 / 20.30
207+
Commit is allowed only when the safe-boundary marker is true
208+
(or the marker is absent — treated as safe for progressive testing).
209+
"""
210+
meta = req.get("metadata") or {}
211+
marker = meta.get("safe_boundary_marker")
212+
if marker is None:
213+
return True, "" # progressive / early tests may omit the marker
214+
if marker is True or marker == "true" or marker == 1:
215+
return True, ""
216+
return False, f"safe_boundary_marker is not true (got {marker!r})"
217+
218+
# ================================================================
219+
# Atomic apply
220+
# ================================================================
221+
222+
def _apply_updates(
223+
self, tp: Dict[str, Any], req: Dict[str, Any]
224+
) -> Dict[str, Any]:
225+
"""
226+
Apply the authorized portions of the update request atomically.
227+
Only fields that TPU is allowed to write are touched.
228+
"""
229+
out = copy.deepcopy(tp)
230+
meta = out.setdefault("metadata", {})
231+
232+
# ---- next_context (from MCB) ----
233+
mcb = req.get("mcb_update") or {}
234+
if isinstance(mcb, dict) and "next_context" in mcb:
235+
incoming = mcb["next_context"]
236+
if isinstance(incoming, dict):
237+
# Write-only into next_context; never touch current-turn context
238+
meta["next_context"] = self._canonical_copy(incoming)
239+
240+
# ---- continuity / clarifying (light pass-through when present) ----
241+
for src_key, dst_key in (
242+
("continuity_metadata", "continuity_metadata"),
243+
("msl_metadata", "msl_metadata"),
244+
("cil_metadata", "cil_metadata"),
245+
("semantic_residue_metadata", "semantic_residue_metadata"),
246+
):
247+
# Prefer values already sitting on the TP (from CE / upstream);
248+
# only overwrite when the request explicitly carries them.
249+
if src_key in req and isinstance(req[src_key], dict):
250+
meta[dst_key] = self._canonical_copy(req[src_key])
251+
252+
# ---- CE context envelope is already on the TP; mark it committed ----
253+
# (TPU does not re-normalize; it only records the commit)
254+
ctx = meta.get("context")
255+
if isinstance(ctx, dict):
256+
prov = ctx.setdefault("context_provenance", {})
257+
# Extend lineage; origin stays CE, last_update becomes TPU
258+
prov["last_update"] = "TPU"
259+
lineage = list(prov.get("commit_lineage") or [])
260+
# lineage is completed in _write_provenance
261+
262+
return out
263+
264+
# ================================================================
265+
# Provenance
266+
# ================================================================
267+
268+
def _write_provenance(self, tp: Dict[str, Any]) -> None:
269+
"""
270+
Write TPU-authored provenance_metadata (HLR + structural program).
271+
"""
272+
meta = tp.setdefault("metadata", {})
273+
commit_id = self._make_commit_id()
274+
275+
# Prior lineage (from CE or earlier TPU commits)
276+
prior = []
277+
old_prov = meta.get("provenance_metadata") or {}
278+
if isinstance(old_prov, dict):
279+
prior = list(old_prov.get("commit_lineage") or [])
280+
# Also harvest from context_provenance if present
281+
ctx_prov = (meta.get("context") or {}).get("context_provenance") or {}
282+
if isinstance(ctx_prov, dict):
283+
for item in ctx_prov.get("commit_lineage") or []:
284+
if item not in prior:
285+
prior.append(item)
286+
287+
new_lineage = prior + [commit_id]
288+
289+
meta["provenance_metadata"] = {
290+
"commit_id": commit_id,
291+
"commit_sequence": self._commit_seq,
292+
"primitive_origin": "TPU",
293+
"commit_timestamp": None, # deliberately omitted for pure replay determinism
294+
"commit_lineage": new_lineage,
295+
}
296+
297+
# Keep context_provenance lineage in sync
298+
if isinstance(meta.get("context"), dict):
299+
cp = meta["context"].setdefault("context_provenance", {})
300+
cp["last_update"] = "TPU"
301+
cp["commit_lineage"] = list(new_lineage)
302+
303+
# ================================================================
304+
# Audit / Error
305+
# ================================================================
306+
307+
def _build_audit_record(
308+
self,
309+
*,
310+
status: str,
311+
writer_authority: str,
312+
canonical_ordering: str,
313+
safe_boundary: str,
314+
atomicity: str,
315+
tp_n: Dict[str, Any],
316+
tp_n1: Dict[str, Any],
317+
reason: Optional[str],
318+
) -> Dict[str, Any]:
319+
return {
320+
"status": status,
321+
"writer_authority": writer_authority,
322+
"canonical_ordering": canonical_ordering,
323+
"safe_boundary": safe_boundary,
324+
"atomicity": atomicity,
325+
"reason": reason,
326+
"tp_n_hash": self._stable_hash(tp_n),
327+
"tp_n1_hash": self._stable_hash(tp_n1),
328+
"commit_sequence": self._commit_seq,
329+
"primitive_origin": "TPU",
330+
}
331+
332+
def _reject(self, code: str, rationale: str) -> Tuple[Dict[str, Any], Dict[str, Any]]:
333+
"""
334+
Deterministic fallback: retain TP(N), emit error + audit.
335+
"""
336+
retained = copy.deepcopy(self.tp_in)
337+
audit = self._build_audit_record(
338+
status="rejected",
339+
writer_authority="fail" if "AUTHORITY" in code else "pass",
340+
canonical_ordering="pass",
341+
safe_boundary="pass",
342+
atomicity="pass", # nothing was applied
343+
tp_n=self.tp_in,
344+
tp_n1=retained,
345+
reason=rationale,
346+
)
347+
error = {
348+
"code": code,
349+
"rationale": rationale,
350+
"fallback_behavior": "retain_TP_N",
351+
"audit_record": audit,
352+
}
353+
self.tp = retained
354+
self.audit_record = audit
355+
self.error = error
356+
return retained, audit
357+
358+
# ================================================================
359+
# Deterministic helpers
360+
# ================================================================
361+
362+
def _derive_commit_sequence(self) -> int:
363+
"""Derive a stable sequence number from prior provenance or request seed."""
364+
meta = (self.tp.get("metadata") or {})
365+
prov = meta.get("provenance_metadata") or {}
366+
seq = prov.get("commit_sequence")
367+
if isinstance(seq, int):
368+
return seq + 1
369+
# Fall back to a hash of the request seed
370+
seed = ((self.req.get("metadata") or {}).get("seed")) or "tpu_default"
371+
h = int(hashlib.sha256(str(seed).encode("utf-8")).hexdigest()[:8], 16)
372+
return (h % 100000) + 1
373+
374+
def _make_commit_id(self) -> str:
375+
seed = ((self.req.get("metadata") or {}).get("seed")) or "tpu_default"
376+
raw = f"{seed}:{self._commit_seq}"
377+
digest = hashlib.sha256(raw.encode("utf-8")).hexdigest()[:12]
378+
return f"tpu_{digest}"
379+
380+
@staticmethod
381+
def _stable_hash(obj: Any) -> str:
382+
try:
383+
payload = json.dumps(obj, sort_keys=True, default=str)
384+
except Exception:
385+
payload = str(obj)
386+
return hashlib.sha256(payload.encode("utf-8")).hexdigest()[:16]
387+
388+
@staticmethod
389+
def _canonical_copy(obj: Any) -> Any:
390+
"""Deep copy with deterministic key ordering for dicts."""
391+
if isinstance(obj, dict):
392+
return {k: TPU._canonical_copy(obj[k]) for k in sorted(obj.keys())}
393+
if isinstance(obj, list):
394+
return [TPU._canonical_copy(x) for x in obj]
395+
return copy.deepcopy(obj)

0 commit comments

Comments
 (0)