"""Proposed task-execution-grant CCR immutable projection; no native admission. The only excluded paths are typed append-only review.comments and evidence.events. Status, updated, review requirements, effectful bytes and source revisions remain immutable. A digest is provenance, never served approval or execution authority. """ from __future__ import annotations import copy import hashlib import json import re from datetime import datetime class Refused(Exception): pass ROOT_KEYS={'id','kind','schema_version','request_type','title','status','created','updated', 'requester','review','target','execution','openbao','evidence'} APPEND_ONLY_PATHS=(('review','comments'),('evidence','events')) HASH=re.compile(r'sha256:[0-9a-f]{64}') def need(value): if not value:raise Refused('invalid task execution grant') def encoded(value): def json_value(item): if type(item) is dict: need(all(type(key) is str for key in item)) for child in item.values():json_value(child) elif type(item) is list: for child in item:json_value(child) else:need(item is None or type(item) in {str,int,float,bool}) try: json_value(value) return json.dumps(value,sort_keys=True,separators=(',',':'),ensure_ascii=True,allow_nan=False).encode() except (TypeError,ValueError,RecursionError):raise Refused('invalid task execution grant') from None def closed(value,keys): need(type(value) is dict and set(value)==set(keys));return value def text(value): need(type(value) is str and 0=len(old) and new[:len(old)]==old) def bind(record,*,grant_id,expected_execution,expected_openbao,expected_digest): """Compare an owner source record with independently reconstructed effects. Does not trust status/comments as approval, read source files, contact engines, register a delegated applier or issue any backend request. """ value=projection(record) need(record['id']==grant_id and type(expected_digest) is str and HASH.fullmatch(expected_digest)) need(encoded(record['execution'])==encoded(expected_execution)) need(encoded(record['openbao'])==encoded(expected_openbao)) need(immutable_digest(record)==expected_digest) return value