P23-03: 生活の観察

This commit is contained in:
Agent Control Runner
2026-10-16 14:41:54 +09:00
parent 447f77a13b
commit 7f1920b329
2 changed files with 262 additions and 19 deletions
+94 -18
View File
@@ -1839,7 +1839,58 @@ def git_bytes(root, *arguments):
return process.stdout
def verify_composition(root, roles, source_map, inputs, read_git=None):
_VERIFIED_VIEWS = {}
def blob_oid(content):
return hashlib.sha1(b'blob %d\0' % len(content) + content).hexdigest()
def git_blob_stream(root, oids, read_batch=None):
"""Pace requests to one owned child; adapters return (binary stream, status)."""
requested = list(oids)
require(all(isinstance(oid, str) and re.fullmatch('[0-9a-f]{40}', oid)
for oid in requested), 'invalid blob oid')
process = stream = None
try:
if read_batch is None:
process = subprocess.Popen(['git', '-C', str(root), 'cat-file', '--batch'],
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
stream = process.stdout
else:
stream, status = read_batch(requested)
count = 0
for oid in requested:
if process is not None:
process.stdin.write((oid + '\n').encode())
process.stdin.flush()
header = stream.readline(128)
match = re.fullmatch(rb'([0-9a-f]{40}) blob (0|[1-9][0-9]*)\n', header)
require(match is not None and match[1].decode() == oid, 'invalid/out-of-order blob header')
size = int(match[2])
content = stream.read(size)
require(len(content) == size, 'short blob read')
require(stream.read(1) == b'\n', 'missing blob LF')
require(blob_oid(content) == oid, 'blob content SHA-1 mismatch')
count += 1
yield oid, content
if process is not None:
process.stdin.close()
require(stream.read(1) == b'', 'extra trailing blob output')
require((process.wait() if process is not None else status) == 0, 'cat-file failed')
require(count == len(requested), 'blob output count mismatch')
finally:
if process is not None:
if process.poll() is None:
process.kill()
process.wait()
process.stdin.close()
process.stdout.close()
elif stream is not None:
stream.close()
def verify_composition(root, roles, source_map, inputs, read_git=None, view=None, read_batch=None):
require(isinstance(roles, dict), 'missing externally bound S/M/T/Q')
names = ('submitted_sha', 'main_context_sha', 'tested_tree', 'tested_commit')
require(all(isinstance(roles.get(k), str) and re.fullmatch('[0-9a-f]{40}', roles[k]) for k in names),
@@ -1865,6 +1916,7 @@ def verify_composition(root, roles, source_map, inputs, read_git=None):
continue
metadata, path = record.split(b'\t', 1)
mode, kind, oid = metadata.decode().split()
require(path.decode() not in result, 'duplicate T path')
result[path.decode()] = (mode, kind, oid)
return result
base, submitted, main, tested = map(tree, (BASE_COMMIT, s, m, t))
@@ -1882,30 +1934,43 @@ def verify_composition(root, roles, source_map, inputs, read_git=None):
return strict_json(git('show', revision + ':' + CONTRACT))
verify_marker_join(document(BASE_COMMIT), document(s), document(m), document(t))
runner = module('p23_composition_source_filter', ROOT/'tools/run_all_tests.py')
with tested_checkout(root, roles, git) as view:
require(source_map == {p: row['sha256'] for p, row in runner.source_refs(view).items()},
if view is not None:
registered = _VERIFIED_VIEWS.get(Path(view).resolve())
require(registered is not None, 'unregistered verified view')
require(registered[0] == t, 'verified view tree mismatch')
require(registered[1] == {p: e for p, e in tested.items() if e[1] == 'blob'},
'verified view manifest mismatch')
with (contextlib.nullcontext(Path(view)) if view is not None else
tested_checkout(root, roles, git, read_batch)) as source_view:
require(source_map == {p: row['sha256'] for p, row in runner.source_refs(source_view).items()},
'incomplete tested-tree source/settings/QA map')
# Source refs include all QA suites and settings, not just S-changed files.
required = set(source_map) | {p.split('#', 1)[0] for p in inputs} | {PLAN, CONTRACT,
'project.godot', 'tests/business/test_required_kitchen.gd', 'tools/regression/suite-policy.json'}
bindings = roles.get('blob_bindings')
require(isinstance(bindings, dict) and set(bindings) == required, 'incomplete/extraneous T blob bindings')
for path in sorted(required):
paths = sorted(required)
for path in paths:
entry = tested.get(path)
require(entry is not None and entry[1] == 'blob' and bindings[path] == entry[2], 'T blob binding: ' + path)
content = git('show', t + ':' + path)
if head != s or path in changed_s-changed_m:
require((root/path).read_bytes() == content, 'checkout bytes differ from T: ' + path)
digest = hashlib.sha256(content).hexdigest()
if path in source_map:
require(source_map[path] == digest, 'evidence source is not T: ' + path)
if path in inputs:
require(inputs[path] == digest, 'evidence input is not T: ' + path)
contents = (((tested[p][2], (Path(view)/p).read_bytes()) for p in paths) if view is not None
else git_blob_stream(root, [tested[p][2] for p in paths], read_batch))
with contextlib.closing(contents):
for number, (oid, content) in enumerate(contents):
path = paths[number]
require(blob_oid(content) == tested[path][2] == oid, 'binding blob SHA-1 mismatch: ' + path)
if head != s or path in changed_s-changed_m:
require((root/path).read_bytes() == content, 'checkout bytes differ from T: ' + path)
digest = hashlib.sha256(content).hexdigest()
if path in source_map:
require(source_map[path] == digest, 'evidence source is not T: ' + path)
if path in inputs:
require(inputs[path] == digest, 'evidence input is not T: ' + path)
return {k: roles[k] for k in names}
@contextlib.contextmanager
def tested_checkout(root, roles, read_git=None):
def tested_checkout(root, roles, read_git=None, read_batch=None):
"""Disposable Git-blob view shared by the existing filters and readers."""
require(isinstance(roles, dict), 'missing externally bound S/M/T/Q')
tree = roles.get('tested_tree', '')
@@ -1913,6 +1978,7 @@ def tested_checkout(root, roles, read_git=None):
git = read_git or (lambda *args: git_bytes(root, *args))
with tempfile.TemporaryDirectory(prefix='p23-tested-tree-') as temporary:
view = Path(temporary)
manifest = {}
for record in git('ls-tree', '-r', '-z', tree).split(b'\0'):
if not record:
continue
@@ -1921,10 +1987,20 @@ def tested_checkout(root, roles, read_git=None):
path = Path(raw_path.decode())
require(not path.is_absolute() and '..' not in path.parts, 'unsafe T path')
require(kind == 'blob' and mode in ('100644', '100755'), 'T source aliases refused')
target = view/path
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(git('show', tree + ':' + path.as_posix()))
yield view
require(path.as_posix() not in manifest, 'duplicate T path')
manifest[path.as_posix()] = (mode, kind, oid)
paths = list(manifest)
contents = git_blob_stream(root, [manifest[p][2] for p in paths], read_batch)
with contextlib.closing(contents):
for number, (_, content) in enumerate(contents):
target = view/paths[number]
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(content)
_VERIFIED_VIEWS[view.resolve()] = (tree, manifest)
try:
yield view
finally:
del _VERIFIED_VIEWS[view.resolve()]
def root_context(directory, index, explicit=None):
@@ -2164,7 +2240,7 @@ def check_tested(checkout, directory, root_roles, root):
'historical v1 evidence has diagnostic applicability only')
runner = module('p23_source_applicability', root/'tools/run_all_tests.py')
require(index.get('source_map') == {n: v['sha256'] for n, v in runner.source_refs(root).items()}, 'exact frozen source map')
roles = verify_composition(checkout, root_roles, index['source_map'], input_map(root))
roles = verify_composition(checkout, root_roles, index['source_map'], input_map(root), view=root)
if git_bytes(checkout, 'rev-parse', 'HEAD').decode().strip() != roles['submitted_sha']:
require(index['source_map'] == {n: v['sha256'] for n, v in runner.source_refs(checkout).items()},
'exact checkout source map')