tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

generation_io.py (3653B)


      1 """Declared generation framing, never inferred from execution or tree shape.
      2 
      3 Missing metadata denotes isolated historical v1. New records carry both versions:
      4 a transition can execute v1 while producing v2, using a separately selected output
      5 adapter. These are caller assertions bound to recorded artifact bytes, not a proof
      6 of arbitrary worker semantics or an authority sandbox.
      7 """
      8 import hashlib
      9 import argparse
     10 
     11 FIELDS = ('execution_io_version', 'produced_io_version', 'produced_adapter_sha256')
     12 
     13 
     14 def version(value):
     15     if type(value) is not int or value not in (1, 2):
     16         raise ValueError('IO version must be integer 1 or 2')
     17     return value
     18 
     19 
     20 def cli_version(text):
     21     if text not in ('1', '2'):
     22         raise argparse.ArgumentTypeError('IO version must be 1 or 2')
     23     return int(text)
     24 
     25 
     26 def versions(record):
     27     if not isinstance(record, dict):
     28         raise ValueError('invalid generation IO metadata')
     29     present = [key in record for key in FIELDS]
     30     if any(present) and not all(present):
     31         raise ValueError('incomplete generation IO metadata')
     32     if not any(present):
     33         return 1, 1
     34     pin = record['produced_adapter_sha256']
     35     if not isinstance(pin, str) or len(pin) != 64 or any(c not in '0123456789abcdef' for c in pin):
     36         raise ValueError('invalid produced adapter digest')
     37     return version(record['execution_io_version']), version(record['produced_io_version'])
     38 
     39 
     40 def produced_adapter(directory, record):
     41     versions(record)
     42     name = 'produced-io-adapter.arboricx' if 'produced_io_version' in record else 'io-adapter.arboricx'
     43     payload = (directory / name).read_bytes()
     44     expected = record.get('produced_adapter_sha256', record.get('adapter_sha256'))
     45     if hashlib.sha256(payload).hexdigest() != expected:
     46         raise ValueError('produced adapter identity mismatch')
     47     return payload
     48 
     49 
     50 def execution_flags(io_version, host='haskell'):
     51     value = version(io_version)
     52     if host not in ('haskell', 'haskell-ffi'):
     53         raise ValueError('selected execution host does not support IO')
     54     if host == 'haskell-ffi' and value != 2:
     55         raise ValueError('Haskell embedding requires IO version 2')
     56     return ['--unsafe-io'] if value == 1 else []
     57 
     58 
     59 def check_pair(first, second):
     60     _, output = versions(first)
     61     execution, reproduced = versions(second)
     62     if output != execution or output != reproduced:
     63         raise ValueError('generation IO version chain mismatch')
     64     a = first.get('produced_adapter_sha256', first['adapter_sha256'])
     65     b = second.get('produced_adapter_sha256', second['adapter_sha256'])
     66     if a != second['adapter_sha256'] or a != b:
     67         raise ValueError('generation adapter chain mismatch')
     68 
     69 
     70 def check_invocation(record, command, host='haskell'):
     71     execution, _ = versions(record)
     72     execution_flags(execution, host)
     73     if not isinstance(command, list) or not all(isinstance(arg, str) for arg in command):
     74         raise ValueError('invalid generation invocation')
     75     if host != 'haskell':
     76         return
     77     grants = {'--unsafe-io', '--allow-read', '--allow-write', '--allow-read-all', '--allow-write-all'}
     78     if execution == 2 and any(arg.split('=', 1)[0] in grants for arg in command):
     79         raise ValueError('invocation IO authority mismatch')
     80     if execution == 1 and '--unsafe-io' not in command:
     81         raise ValueError('invocation IO authority mismatch')
     82 
     83 
     84 def compiler_flags(io_version):
     85     # Retained v1 runtimes predate the selector. Their documented default is v1;
     86     # do not probe failure and retry. HostIO always requires the explicit new flag.
     87     return [] if version(io_version) == 1 else ['--toolchain-io-version', '2']