P23-03: 生活の観察
This commit is contained in:
@@ -5,6 +5,8 @@ Whole typed inputs are preserved. The existing typed codec is reused verbatim.
|
||||
Unimplemented visual/OS checks are explicit blockers, never successful checks.
|
||||
"""
|
||||
import argparse
|
||||
import contextlib
|
||||
import tempfile
|
||||
import copy
|
||||
import hashlib
|
||||
import importlib.util
|
||||
@@ -1848,7 +1850,11 @@ def verify_composition(root, roles, source_map, inputs, read_git=None):
|
||||
require(git('cat-file', '-t', commit).strip() == b'commit', 'role is not a commit')
|
||||
require(git('cat-file', '-t', t).strip() == b'tree', 'T is not a tree')
|
||||
require(git('rev-parse', q+'^{tree}').decode().strip() == t, 'Q does not contain T')
|
||||
require(git('rev-parse', 'HEAD').decode().strip() == q, 'checkout is not Q')
|
||||
head = git('rev-parse', 'HEAD').decode().strip()
|
||||
if head not in (s, q):
|
||||
require(git('rev-parse', 'HEAD^{tree}').decode().strip() == t, 'C tree differs from T')
|
||||
require(git('rev-list', '--parents', '-n', '1', 'HEAD').decode().split() == [head, m, s],
|
||||
'C parents must be exactly [M,S]')
|
||||
require(not git('status', '--porcelain'), 'tested checkout is dirty')
|
||||
merged = git('merge-tree', '--write-tree', s, m).decode().splitlines()
|
||||
require(merged and merged[0] == t, 'T differs from deterministic merge-tree')
|
||||
@@ -1875,11 +1881,10 @@ def verify_composition(root, roles, source_map, inputs, read_git=None):
|
||||
def document(revision):
|
||||
return strict_json(git('show', revision + ':' + CONTRACT))
|
||||
verify_marker_join(document(BASE_COMMIT), document(s), document(m), document(t))
|
||||
source_names = {p for p in tested if p.split('/', 1)[0] in
|
||||
('autoload', 'scripts', 'scenes', 'data', 'tests', 'assets', 'tools')
|
||||
and Path(p).name != '.DS_Store' and '__pycache__' not in Path(p).parts}
|
||||
source_names.update(('project.godot', 'export_presets.cfg', 'Makefile', 'AGENTS.md'))
|
||||
require(set(source_map) == source_names, 'incomplete tested-tree source/settings/QA map')
|
||||
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()},
|
||||
'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'}
|
||||
@@ -1889,7 +1894,8 @@ def verify_composition(root, roles, source_map, inputs, read_git=None):
|
||||
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)
|
||||
require((root/path).read_bytes() == content, 'checkout bytes differ from 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)
|
||||
@@ -1898,6 +1904,41 @@ def verify_composition(root, roles, source_map, inputs, read_git=None):
|
||||
return {k: roles[k] for k in names}
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def tested_checkout(root, roles, read_git=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', '')
|
||||
require(isinstance(tree, str) and re.fullmatch('[0-9a-f]{40}', tree), 'invalid T')
|
||||
git = read_git or (lambda *args: git_bytes(root, *args))
|
||||
with tempfile.TemporaryDirectory(prefix='p23-tested-tree-') as temporary:
|
||||
view = Path(temporary)
|
||||
for record in git('ls-tree', '-r', '-z', tree).split(b'\0'):
|
||||
if not record:
|
||||
continue
|
||||
metadata, raw_path = record.split(b'\t', 1)
|
||||
mode, kind, oid = metadata.decode().split()
|
||||
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
|
||||
|
||||
|
||||
def root_context(directory, index, explicit=None):
|
||||
path = directory/'root-composition.json'
|
||||
require(path.is_file() and not path.is_symlink()
|
||||
and path.resolve().is_relative_to(directory.resolve()), 'missing/unsafe Root composition context')
|
||||
data = path.read_bytes()
|
||||
require(index.get('root_composition_sha256') == hashlib.sha256(data).hexdigest(),
|
||||
'Root composition context SHA mismatch')
|
||||
roles = strict_json(data)
|
||||
require(explicit is None or structural_equal(explicit, roles), 'explicit Root context disagrees')
|
||||
return roles
|
||||
|
||||
|
||||
def normative_plan_text(root):
|
||||
"""Requirement text is an input; explicitly labelled progress is metadata."""
|
||||
text = (root / PLAN).read_text()
|
||||
@@ -2097,6 +2138,19 @@ def inspect_native_phases(directory, index, root=ROOT):
|
||||
|
||||
|
||||
def check(root, directory, root_roles=None):
|
||||
try:
|
||||
index_path = directory/'index.json'
|
||||
if not index_path.is_file():
|
||||
return ['missing evidence index: ' + str(index_path)]
|
||||
index = strict_json(index_path.read_bytes())
|
||||
roles = root_context(directory, index, root_roles)
|
||||
with tested_checkout(root, roles) as view:
|
||||
return check_tested(root, directory, roles, view)
|
||||
except (OSError, ValueError, KeyError, TypeError, AttributeError) as exc:
|
||||
return ['invalid evidence index: ' + str(exc)]
|
||||
|
||||
|
||||
def check_tested(checkout, directory, root_roles, root):
|
||||
"""Never infer completion of unimplemented gates from an index assertion."""
|
||||
deficits = []
|
||||
executed = {c: {category: set() for category in CATEGORIES} for c in CONDITIONS}
|
||||
@@ -2110,7 +2164,10 @@ def check(root, directory, root_roles=None):
|
||||
'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(root, root_roles, index['source_map'], input_map(root))
|
||||
roles = verify_composition(checkout, root_roles, index['source_map'], input_map(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')
|
||||
require(index.get('composition') == roles and index.get('commit') == roles['tested_commit'],
|
||||
'evidence role binding differs from Root S/M/T/Q; historical v1 is not T evidence')
|
||||
if index.get('input_map') != input_map(root):
|
||||
@@ -2127,7 +2184,10 @@ def check(root, directory, root_roles=None):
|
||||
require(path.resolve().is_relative_to(directory.resolve()) and not path.is_symlink(),
|
||||
'artifact escapes evidence directory')
|
||||
require(path.is_file() and sha(path) == digest, 'artifact SHA mismatch: ' + name)
|
||||
inspect_native_phases(directory, index, root)
|
||||
try:
|
||||
inspect_native_phases(directory, index, root)
|
||||
except (OSError, ValueError, KeyError, TypeError, AttributeError) as exc:
|
||||
deficits.append('native evidence deficit: ' + str(exc))
|
||||
for layout in ('A', 'B'):
|
||||
layout_semantic, layout_pixels = set(), set()
|
||||
stream = directory / ('layout-' + layout)
|
||||
@@ -2199,8 +2259,7 @@ def main(argv=None):
|
||||
parser.add_argument('--offline-mirror', type=Path)
|
||||
parser.add_argument('--layout-a', type=Path)
|
||||
parser.add_argument('--layout-b', type=Path)
|
||||
parser.add_argument('--root-composition', type=Path,
|
||||
default=os.environ.get('P23_ROOT_COMPOSITION'))
|
||||
parser.add_argument('--root-composition', type=Path)
|
||||
args = parser.parse_args(argv)
|
||||
check_setup_budget_source((args.project_root / 'tests/fixtures/p23-03/observation.gd').read_text())
|
||||
if args.offline_mirror:
|
||||
@@ -2208,13 +2267,17 @@ def main(argv=None):
|
||||
'offline preparation must not be combined with acceptance checks')
|
||||
print(json.dumps(inspect_offline_mirror(args.offline_mirror), ensure_ascii=False))
|
||||
return 2
|
||||
if args.verify_only:
|
||||
if args.verify_only or (os.environ.get('BUILD_OUTPUT_DIR') and not any(
|
||||
(args.diagnostics, args.layout_a, args.layout_b))):
|
||||
build = os.environ.get('BUILD_OUTPUT_DIR')
|
||||
if not build:
|
||||
print('BUILD_OUTPUT_DIR is required', file=sys.stderr)
|
||||
return 2
|
||||
root_roles = strict_json(args.root_composition.read_bytes()) if args.root_composition else None
|
||||
deficits = check(args.project_root, Path(build) / 'p23-life-observation-evidence', root_roles)
|
||||
try:
|
||||
root_roles = strict_json(args.root_composition.read_bytes()) if args.root_composition else None
|
||||
deficits = check(args.project_root, Path(build) / 'p23-life-observation-evidence', root_roles)
|
||||
except (OSError, ValueError) as exc:
|
||||
deficits = ['invalid Root composition context: ' + str(exc)]
|
||||
print(json.dumps(dict(task_id='P23-03', integrity='fail' if deficits else 'pass',
|
||||
deficits=deficits, acceptance='pending'), ensure_ascii=False))
|
||||
return 1 if deficits else 0
|
||||
|
||||
Reference in New Issue
Block a user