P08-03: 配置の通常操作受入

This commit is contained in:
Agent Control Runner
2026-10-15 00:32:39 +09:00
parent f5f85ae08c
commit d369d1c399
5 changed files with 175 additions and 12 deletions
@@ -582,3 +582,13 @@ not claimed
R09 history and pins remain unrewritten. Low findings (empty cell, audio, idempotency, English Runtime messages) remain deferred. R09 history and pins remain unrewritten. Low findings (empty cell, audio, idempotency, English Runtime messages) remain deferred.
Repair18 local validation: python3 -B tools/test_p08_normal_placement_evidence.py ran 131 tests in 88.476s, exit 1: one existing R09 raw-proof read PermissionError; no weakening. py_compile initially failed on default cache permissions, then passed with PYTHONPYCACHEPREFIX inside allowed fixture scratch. JSON load and git diff --check passed. Scope adapter passed including boundary equality. Exact commands and source SHA256 are in implementation.json repair18. Root/Runner raw-proof access remains required. Repair18 local validation: python3 -B tools/test_p08_normal_placement_evidence.py ran 131 tests in 88.476s, exit 1: one existing R09 raw-proof read PermissionError; no weakening. py_compile initially failed on default cache permissions, then passed with PYTHONPYCACHEPREFIX inside allowed fixture scratch. JSON load and git diff --check passed. Scope adapter passed including boundary equality. Exact commands and source SHA256 are in implementation.json repair18. Root/Runner raw-proof access remains required.
## Repair20 — submitted candidate
Frozen input f5f85ae08c1c6c9c1ec511f13a7215170d7d8d49; registered validate failure f2d59df6a3c34c0e93b15b1a05cfa533. Historical pins selected the R09 route for fresh proof; the checker now selects from the explicit index commit, retaining R09 applicability and fresh HEAD/source/input checks. Both routes bind screenshot paths to the request; fresh proof uses strict PNG/JPEG magic dispatch and equal positive dimensions. Historical R09 pins, dimensions and runtime_preview.patch remain unchanged.
Root full176 was interrupted after 13 PASS, at phase013, guards 0: an interruption, not a product defect and not acceptance. No OS collection was started. New HEAD: to be bound by Runner. Conditions (1)-(5) remain pending Root native/OS.
Pending: Root exact-final local/native/checker applicability; one complete final full regression on the derived roster; fresh contiguous all-5 normal OS plus native collection; independent Opus review of all five conditions; integration/current final verification/ledger; Windows same-distribution/device, with no Mac substitution.
Local verification: final Python rerun 153 tests / 127.709s, 152 passed, one existing R09 original raw-proof PermissionError (exit 1); all 11 repair20 cases passed. py_compile failed on default external cache permission (exit 1). JSON load, diff whitespace and scope self-check passed. No test skipped or weakened; Root must rerun with required evidence/cache access.
@@ -7,7 +7,10 @@
"status": "submitted", "status": "submitted",
"acceptance": "not claimed; all five conditions pending", "acceptance": "not claimed; all five conditions pending",
"diagnosis": "R15 reveal published no geometry and verified neither visibility nor scroll direction; deferred quit allowed subsequent requests and secondary failures after a terminal ACK error.", "diagnosis": "R15 reveal published no geometry and verified neither visibility nor scroll direction; deferred quit allowed subsequent requests and secondary failures after a terminal ACK error.",
"owner_rulings": ["Root31 ACK is invalid", "R15 earns no credit"], "owner_rulings": [
"Root31 ACK is invalid",
"R15 earns no credit"
],
"source_map": { "source_map": {
"tests/fixtures/p08-03/passive_placement.gd": { "tests/fixtures/p08-03/passive_placement.gd": {
"at_M": "cdbd9a09b535d00a79aec7e2b7219f4daa4cf235449602b3cd02c6f12d9df88c", "at_M": "cdbd9a09b535d00a79aec7e2b7219f4daa4cf235449602b3cd02c6f12d9df88c",
@@ -2037,5 +2040,51 @@
} }
], ],
"context_gap": "R09 original raw-proof read access denied in this environment; Root/Runner must execute with required raw-proof access." "context_gap": "R09 original raw-proof read access denied in this environment; Root/Runner must execute with required raw-proof access."
},
"repair20": {
"input": "f5f85ae08c1c6c9c1ec511f13a7215170d7d8d49",
"registered_validate_failure": "f2d59df6a3c34c0e93b15b1a05cfa533",
"routing": "Historical pins incorrectly selected reuse for fresh HEAD. Route now follows explicit index.commit == R09_COMMIT; fresh uses current source and HEAD, never R09 functions or dimensions.",
"screenshots": "Both routes bind request paths; fresh dispatches strict PNG/JPEG by magic and checks equal positive dimensions.",
"full176": "Root interrupted full176: 13 PASS, phase013 interrupted, guards 0. Interruption, not a product defect or acceptance.",
"OS_collection": "No OS collection was started.",
"HEAD": "to be bound by Runner",
"conditions": "(1)-(5) pending Root native/OS; no acceptance claimed",
"pending": [
"Root exact-final local, native and checker applicability",
"one final complete full regression on derived roster",
"fresh contiguous all-5 normal OS plus native collection",
"independent Opus review of all 5 conditions",
"integration, current final verification and ledger",
"Windows same-distribution/device remains open; no Mac substitution"
],
"local_source_sha256": {
"tools/check_p08_normal_placement_evidence.py": "53acc2e29fc36f7c7e3b2a5f6d3f97ba935b9052e4b6e095b071ec240f4ff68a",
"tools/test_p08_normal_placement_evidence.py": "8a29e3b996e2f1d15c69283faeae54555654c3cf89df99d8ce7f9eed38cd5edc",
"tests/fixtures/p08-03/checker_fixture.py": "80b9ad74b04b8d235b3ff152ca2cba720085ec420c7dc7cbf07274d534f8fdfb"
},
"validation": [
{
"command": "python3 -B tools/test_p08_normal_placement_evidence.py",
"initial_result": "153 tests in 128.538s; one obsolete screenshot target failure corrected; one existing R09 raw-proof PermissionError. Final rerun pending.",
"result": "Final rerun exit 1: 153 tests in 127.709s; 152 passed, one existing R09 raw-proof PermissionError; no skips or weakened tests. All 11 repair20 cases passed."
},
{
"command": "python3 -m py_compile tools/check_p08_normal_placement_evidence.py tools/test_p08_normal_placement_evidence.py tests/fixtures/p08-03/checker_fixture.py",
"result": "exit 1 on both attempts: PermissionError writing default external Python cache; no bypass"
},
{
"command": "python3 -c 'import json; json.load(open(\"docs/plans/original-completion-2026-10-02/execution/p08-03/implementation.json\")); print(\"json.load: PASS\")'",
"result": "exit 0"
},
{
"command": "git diff --check",
"result": "exit 0"
},
{
"command": "scope self-check: git diff --name-only f5f85ae08c1c6c9c1ec511f13a7215170d7d8d49 plus git ls-files --others --exclude-standard; fnmatch against unchanged contract allowed_paths",
"result": "exit 0; final self-check found exactly five changed paths, no untracked files, all within unchanged allowed_paths"
}
]
} }
} }
+5 -2
View File
@@ -235,13 +235,16 @@ def make(base, source, checker):
'events':[{'synthetic':True}]}) 'events':[{'synthetic':True}]})
put(out/(prefix+'ack.json'), {'target_pid':pid,'sequence':sequence, put(out/(prefix+'ack.json'), {'target_pid':pid,'sequence':sequence,
'request_sha256':sha(out/(prefix+'request.json')), 'input_receipt':receipt, 'request_sha256':sha(out/(prefix+'request.json')), 'input_receipt':receipt,
'screen_before':directory+'/screen.png','screen_after':directory+'/screen.png'}) 'screen_before':prefix+'screen-before.png','screen_after':prefix+'screen-after.png'})
row['requests'].append(prefix+'ack.json') row['requests'].append(prefix+'ack.json')
def chunk(kind, body): def chunk(kind, body):
return struct.pack('>I',len(body))+kind+body+struct.pack('>I',zlib.crc32(kind+body)) return struct.pack('>I',len(body))+kind+body+struct.pack('>I',zlib.crc32(kind+body))
(folder/'screen.png').write_bytes(b'\x89PNG\r\n\x1a\n'+ screenshot = (b'\x89PNG\r\n\x1a\n'+
chunk(b'IHDR',struct.pack('>IIBBBBB',1,1,8,2,0,0,0))+ chunk(b'IHDR',struct.pack('>IIBBBBB',1,1,8,2,0,0,0))+
chunk(b'IDAT',zlib.compress(b'\0\0\0\0'))+chunk(b'IEND',b'')) chunk(b'IDAT',zlib.compress(b'\0\0\0\0'))+chunk(b'IEND',b''))
for ack_name in row['requests']:
for side in ('before', 'after'):
(out/Path(ack_name).parent/('screen-'+side+'.png')).write_bytes(screenshot)
actions = [] actions = []
for label in checker.WRITER: for label in checker.WRITER:
+23 -8
View File
@@ -116,6 +116,9 @@ def expected_operation(formal, result, event_type):
recent['items'] = recent['items'][-256:] recent['items'] = recent['items'][-256:]
return expected return expected
def r09_reuse(index):
return index.get('commit') == R09_COMMIT
def png(data): def png(data):
require(data.startswith(b'\x89PNG\r\n\x1a\n'), 'missing OS screenshot') require(data.startswith(b'\x89PNG\r\n\x1a\n'), 'missing OS screenshot')
offset = 8; chunks = []; payload = b'' offset = 8; chunks = []; payload = b''
@@ -135,6 +138,7 @@ def png(data):
require(bool(zlib.decompress(payload)), 'empty OS screenshot') require(bool(zlib.decompress(payload)), 'empty OS screenshot')
except zlib.error as exc: except zlib.error as exc:
raise ValueError('invalid OS screenshot pixels') from exc raise ValueError('invalid OS screenshot pixels') from exc
return struct.unpack('>II', data[16:24])
def input_map(root): def input_map(root):
names = [SPEC, CONTRACT, 'AGENTS.md', RECORDER, RECORDER+'.uid', names = [SPEC, CONTRACT, 'AGENTS.md', RECORDER, RECORDER+'.uid',
@@ -633,21 +637,25 @@ def check(project_root, evidence_dir):
require(index['schema'] == 'p08-normal-placement-v1' and index['acceptance'] == 'pending', 'schema/acceptance') require(index['schema'] == 'p08-normal-placement-v1' and index['acceptance'] == 'pending', 'schema/acceptance')
require(index['collection_error'] is None, 'collection failed') require(index['collection_error'] is None, 'collection failed')
require(index['conditions'] == CONDITIONS, 'missing condition') require(index['conditions'] == CONDITIONS, 'missing condition')
actual = {n: v['sha256'] for n, v in runner.source_refs(root).items()}
original = evidence original = evidence
pins_path = root/R09_DOCS/'ROOT_NATIVE_R09_COLLECTION_PINS.json' pins_path = root/R09_DOCS/'ROOT_NATIVE_R09_COLLECTION_PINS.json'
if pins_path.exists(): reuse = r09_reuse(index)
dimensions_path = root/R09_DOCS/'ROOT_R09_READONLY_IMAGE_DIMENSIONS.json'
if reuse:
require(pins_path.is_file() and dimensions_path.is_file(), 'R09 reuse candidate')
r09_applicability(root, index) r09_applicability(root, index)
r09_runner_binding(evidence, index, load(pins_path)) r09_runner_binding(evidence, index, load(pins_path))
original = r09_collection(evidence, index, load(pins_path)) original = r09_collection(evidence, index, load(pins_path))
r09_images(evidence, index, load(root/R09_DOCS/'ROOT_R09_READONLY_IMAGE_DIMENSIONS.json'), load(pins_path)) r09_images(evidence, index, load(dimensions_path), load(pins_path))
actual = index['source_map'] actual = index['source_map']
else:
actual = {n: v['sha256'] for n, v in runner.source_refs(root).items()}
require(index['source_map'] == actual, 'stale source') require(index['source_map'] == actual, 'stale source')
require(index['inputs'] == input_map(root), 'stale input') require(index['inputs'] == input_map(root), 'stale input')
require(index['spec_sha256'] == SPEC_SHA == hashlib.sha256((root/SPEC).read_text().strip().encode()).hexdigest(), 'immutable spec') require(index['spec_sha256'] == SPEC_SHA == hashlib.sha256((root/SPEC).read_text().strip().encode()).hexdigest(), 'immutable spec')
require(index['marker_contract_sha256'] == sha(root/CONTRACT), 'stale marker') require(index['marker_contract_sha256'] == sha(root/CONTRACT), 'stale marker')
require(re.fullmatch('[0-9a-f]{40}', index['commit']) is not None, 'commit') require(re.fullmatch('[0-9a-f]{40}', index['commit']) is not None, 'commit')
if not pins_path.exists(): if not reuse:
require(index['commit'] == subprocess.check_output(['git','-C',str(root),'rev-parse','HEAD'],text=True).strip(), 'stale commit') require(index['commit'] == subprocess.check_output(['git','-C',str(root),'rev-parse','HEAD'],text=True).strip(), 'stale commit')
def artifact(name): def artifact(name):
require(isinstance(name, str) and name and not Path(name).is_absolute() and '..' not in Path(name).parts, 'unsafe/missing artifact') require(isinstance(name, str) and name and not Path(name).is_absolute() and '..' not in Path(name).parts, 'unsafe/missing artifact')
@@ -969,13 +977,20 @@ def check(project_root, evidence_dir):
and raw['tool_name'] and raw['tool_call_id'], 'fake OS provenance') and raw['tool_name'] and raw['tool_call_id'], 'fake OS provenance')
require(raw['events'] and observed['events'], 'missing OS events') require(raw['events'] and observed['events'], 'missing OS events')
require(raw['collector_pid'] > 0 and raw['collector_pid'] not in os_pids, 'fake OS collector') require(raw['collector_pid'] > 0 and raw['collector_pid'] not in os_pids, 'fake OS collector')
screen_dimensions = []
for key in ('screen_before', 'screen_after'): for key in ('screen_before', 'screen_after'):
require(ack[key] == folder+key.replace('_', '-')+'.png', 'R09 screenshot request binding')
data = artifact(ack[key]).read_bytes() data = artifact(ack[key]).read_bytes()
if pins_path.exists(): if reuse:
require(ack[key] == folder+key.replace('_', '-')+'.png', 'R09 screenshot request binding') dimensions = jpeg_dimensions(data)
jpeg_dimensions(data) elif data.startswith(b'\x89PNG\r\n\x1a\n'):
dimensions = png(data)
elif data.startswith(b'\xff\xd8'):
dimensions = jpeg_dimensions(data)
else: else:
png(data) raise ValueError('missing OS screenshot')
screen_dimensions.append(dimensions)
require(screen_dimensions[0] == screen_dimensions[1] and all(v > 0 for v in screen_dimensions[0]), 'OS screenshot dimension mismatch')
if request['action'] == 'cell' and 'position' in row: if request['action'] == 'cell' and 'position' in row:
dispatch_gate(row['position'], observed['events']) dispatch_gate(row['position'], observed['events'])
if row['id'] == 'pan_zoom': if row['id'] == 'pan_zoom':
+87 -1
View File
@@ -598,7 +598,7 @@ class EvidenceTests(unittest.TestCase):
self.reject('operation kind mismatch') self.reject('operation kind mismatch')
def test_png_signature_is_not_a_screenshot(self): def test_png_signature_is_not_a_screenshot(self):
(self.out/'os-pair/screen.png').write_bytes(b'\x89PNG\r\n\x1a\nFAKE') next((self.out/'os-pair').rglob('screen-before.png')).write_bytes(b'\x89PNG\r\n\x1a\nFAKE')
F.repin(self.out) F.repin(self.out)
self.reject('invalid OS screenshot') self.reject('invalid OS screenshot')
@@ -722,6 +722,92 @@ def refresh_images(out, index, measurement):
measurement['original_index_sha256'] = hashlib.sha256((out/'index.json').read_bytes()).hexdigest() measurement['original_index_sha256'] = hashlib.sha256((out/'index.json').read_bytes()).hexdigest()
class FreshRoutingTests(unittest.TestCase):
setUp = EvidenceTests.setUp
edit = EvidenceTests.edit
reject = EvidenceTests.reject
# Reuse the whole synthetic proof, not a boolean checker substitute.
def historical(self):
F.put(self.root/C.R09_DOCS/'ROOT_NATIVE_R09_COLLECTION_PINS.json', {'synthetic': True})
def fresh_check(self):
commit = C.load(self.out/'index.json')['commit']
original_load = C.load
def guarded_load(path):
self.assertNotEqual(Path(path).name, 'ROOT_R09_READONLY_IMAGE_DIMENSIONS.json')
return original_load(path)
with contextlib.ExitStack() as stack:
stack.enter_context(patch.object(C.subprocess, 'check_output', return_value=commit+'\n'))
stack.enter_context(patch.object(C, 'load', side_effect=guarded_load))
for name in ('r09_applicability', 'r09_runner_binding', 'r09_collection', 'r09_images'):
stack.enter_context(patch.object(C, name, side_effect=AssertionError('fresh used '+name)))
return C.check(self.root, self.out)
def test_historical_pins_fresh_positive(self):
self.historical()
self.assertEqual([], self.fresh_check())
def test_historical_pins_stale_commit(self):
self.historical()
self.edit('index.json', lambda d: d.update(commit='0'*40))
with patch.object(C.subprocess, 'check_output', return_value='1'*40+'\n'):
self.reject('stale commit')
def test_historical_pins_stale_source(self):
self.historical()
self.edit('index.json', lambda d: d['source_map'].update({'project.godot':'0'*64}))
self.assertIn('stale source', ' '.join(self.fresh_check()))
def test_historical_pins_stale_input(self):
self.historical()
self.edit('index.json', lambda d: d['inputs'].update({'AGENTS.md':'0'*64}))
self.assertIn('stale input', ' '.join(self.fresh_check()))
def test_reuse_missing_pins(self):
self.edit('index.json', lambda d: d.update(commit=C.R09_COMMIT))
self.reject('R09 reuse candidate')
def test_reuse_wrong_applicability(self):
self.historical()
F.put(self.root/C.R09_DOCS/'ROOT_R09_READONLY_IMAGE_DIMENSIONS.json', {'synthetic':True})
self.edit('index.json', lambda d: d.update(commit=C.R09_COMMIT))
with patch.object(C.subprocess, 'check_output', return_value=C.R09_COMMIT+'\n'), patch.object(
C.subprocess, 'run', return_value=SimpleNamespace(returncode=1)):
self.reject('R09_COMMIT not an ancestor')
def screenshots(self, data):
for path in self.out.rglob('screen-*.png'):
path.write_bytes(data)
F.repin(self.out)
def test_fresh_jpeg_positive(self):
self.historical()
self.screenshots(negative_jpeg())
self.assertEqual([], self.fresh_check())
def test_fresh_truncated_jpeg(self):
self.screenshots(negative_jpeg()[:-2])
self.assertIn('invalid JPEG SOI/EOI', ' '.join(self.fresh_check()))
def test_fresh_garbage(self):
self.screenshots(b'garbage')
self.assertIn('missing OS screenshot', ' '.join(self.fresh_check()))
def test_fresh_dimension_mismatch(self):
self.screenshots(negative_jpeg())
next(self.out.rglob('screen-after.png')).write_bytes(negative_jpeg(width=640))
F.repin(self.out)
self.assertIn('OS screenshot dimension mismatch', ' '.join(self.fresh_check()))
def test_fresh_unbound_path(self):
ack = next(self.out.rglob('ack.json'))
data = C.load(ack)
data['screen_before'] = data['screen_after']
F.put(ack, data)
F.repin(self.out)
self.assertIn('R09 screenshot request binding', ' '.join(self.fresh_check()))
class R09ImageTests(unittest.TestCase): class R09ImageTests(unittest.TestCase):
def setUp(self): def setUp(self):
self.temp = tempfile.TemporaryDirectory(dir=ROOT/'tests/fixtures/p08-03/.scratch') self.temp = tempfile.TemporaryDirectory(dir=ROOT/'tests/fixtures/p08-03/.scratch')