From 0b7727a66c76d1ce2a345571718feb3117dd8802 Mon Sep 17 00:00:00 2001 From: Agent Control Runner Date: Thu, 15 Oct 2026 10:03:36 +0900 Subject: [PATCH] =?UTF-8?q?P23-03:=20=E7=94=9F=E6=B4=BB=E3=81=AE=E8=A6=B3?= =?UTF-8?q?=E5=AF=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../p23-03/OFFLINE_REPAIR_55c29f1.md | 72 +++++++++++++++++++ tests/fixtures/p23-03/queued_elevator.json | 27 +++++++ tools/check_p23_life_observation_evidence.py | 22 +++++- tools/test_p23_life_observation_evidence.py | 64 +++++++++++++++-- 4 files changed, 177 insertions(+), 8 deletions(-) create mode 100644 docs/plans/original-completion-2026-10-02/execution/p23-03/OFFLINE_REPAIR_55c29f1.md create mode 100644 tests/fixtures/p23-03/queued_elevator.json diff --git a/docs/plans/original-completion-2026-10-02/execution/p23-03/OFFLINE_REPAIR_55c29f1.md b/docs/plans/original-completion-2026-10-02/execution/p23-03/OFFLINE_REPAIR_55c29f1.md new file mode 100644 index 0000000..dd8a45b --- /dev/null +++ b/docs/plans/original-completion-2026-10-02/execution/p23-03/OFFLINE_REPAIR_55c29f1.md @@ -0,0 +1,72 @@ +# Offline checker repair on 55c29f1 + +Source HEAD: 55c29f119234dd6f993c604c4edc6f377fe3951c. Registration base remains +860136f7099f5e3aee99da5798a627a77f2d8eea. No git writes or engine execution. + +Read access: MIRROR_INDEX.json and all 2,622 named files were successfully read +under `/Users/korizatou114/Projects/TheTree-managed-worktrees/p23-03-code/.runtime/P23-03/offline-evidence-55c29f1-r06`. +No mirror bytes were changed. The initial shell heredoc could not create its +temporary file; the read check was executed using Python `-c` instead. + +## First defects and production cause + +Both layouts first failed at frame-030, checker line 328, with KeyError: +`shaft_id`. State objects: + +- A: `layout-A/objects/ac3ed64a6e6162918e4d44c45b2f78d09e50d473b607f3469350a06074a7efb3.json` +- B: `layout-B/objects/97ea43afe3d1b21602ebe2a602e7b7d796ff05f03983cd4d1b3edc9157e39a08.json` + +The exception came from `shaft['shaft_id']`, not `t['shaft_id']`. Every trip in +these failing frames has shaft_id. Saved shaft rows have it only in config. +There is no evidence of shaft-less QUEUED trips to support accepting that shape. + +`autoload/simulation_runtime.gd:541` writes elevators.create_snapshot(). +`scripts/transport/elevator_service.gd:362-367` copies the saved cars without +flattening config; get_report at 345-359 merges config into each report row before +counting queued trips. request at 169-171 assigns shaft_id on creation, and the +restore validator at 412-454 requires the exact trip keys and shaft/connector +agreement. The connector spelling is defined at 617-618; trips are ordered at +707-710. `scenes/main/world_preview.gd:973-975` consumes get_report and sorts +trips by enqueue time/actor. The checker now counts using the merged row identity. + +## Reviewable changes + +- `tools/check_p23_life_observation_evidence.py`: replace + `t['shaft_id'] == shaft['shaft_id']` with `t['shaft_id'] == row['shaft_id']`; + validate exact trip keys, companion identity, shaft/stops/connector attribution + and QUEUED/BOARDED state before recomputing. Whole report equality remains strict. +- `tests/fixtures/p23-03/queued_elevator.json`: retain one actual frame-030 + queued trip and its shaft, without a cabin seat. Distilled unit data only. +- `tools/test_p23_life_observation_evidence.py`: queued shape positive and + shaft attribution, waiting_count, missing/extra key negatives; update two + pre-existing abbreviated synthetic fixtures to the actual shaft/trip shape. +- This repair report. The complete implementation diff is available through + `git diff -- tools/check_p23_life_observation_evidence.py tools/test_p23_life_observation_evidence.py`; + the new fixture and this file are untracked submission files. + +## Local results + +`inspect_stream` on each exact absolute mirror directory: + +``` +layout-A inspect_stream PASS 17 frames +layout-B inspect_stream PASS 17 frames +``` + +For both, labels are frame-000, frame-030, frame-060, frame-090, frame-120, +frame-150, frame-180, frame-210, frame-240, frame-270, frame-300, frame-330, +frame-360, frame-390, frame-420, frame-450, frame-480. Each returns fields: +guests, staff, tasks, elevators, room_service_fields, queues, traffic, goods, +cabin_geometry. Every frame also passes the existing two-motion-history-frame +check, unchanged. No later schema/semantic mismatch was observed. + +Python syntax AST checks pass. Local checker tests: 32 tests, OK. +The initial test run exposed two abbreviated synthetic fixtures; subsequent +queue expectations were corrected for actual lift landing exclusion. Tests +were preserved. git diff --check passes; changed files are within allowed_paths. +completion_marker_contracts retains its one required P23 strict template entry; +no marker changes. Seven conditions, six categories and 30/88 remain unchanged. + +Root native results (319 tests, A/B PASS), matched metric values (48 metrics, +7153/2585), production data and services were not modified. Root OS evidence +remains pending as expected at worker submission; this repair grants no acceptance. diff --git a/tests/fixtures/p23-03/queued_elevator.json b/tests/fixtures/p23-03/queued_elevator.json new file mode 100644 index 0000000..28d1d48 --- /dev/null +++ b/tests/fixtures/p23-03/queued_elevator.json @@ -0,0 +1,27 @@ +{ + "provenance": "Distilled layout-A frame-030 state ac3ed64a6e6162918e4d44c45b2f78d09e50d473b607f3469350a06074a7efb3; retain first real queued trip, no cabin seat. Unit fixture only.", + "saved": { + "version": 1, + "current_minute": 691, + "request_sequence": 8, + "shafts": [{ + "config": { + "shaft_id": "transport.shaft.0.0.0.2", + "module_ids": ["facility_00000003"], + "stops": [[0, 0, 0], [0, 1, 0]], + "width": 2, "capacity": 4, "travel_minutes_per_floor": 10, "access_id": "" + }, + "operational": true, "operator_open": true, "powered": true, + "current_floor": 1, "target_floor": 1, "boarding_floor": 0, + "phase": "PICKUP", "remaining_minutes": 9, "phase_total_minutes": 10, + "phase_started_at": 690, "phase_origin_floor": 1, "phase_passenger_count": 0, + "boarded": [] + }], + "trips": [{ + "actor_id": "guest:p23-000", "request_id": "transport.trip.000000000002", + "shaft_id": "transport.shaft.0.0.0.2", + "connector_id": "transport.shaft.0.0.0.2.link.0.1", + "from": [0, 0, 0], "to": [0, 1, 0], "enqueued_at": 674, "state": "QUEUED" + }] + } +} diff --git a/tools/check_p23_life_observation_evidence.py b/tools/check_p23_life_observation_evidence.py index 9f80b39..c582983 100644 --- a/tools/check_p23_life_observation_evidence.py +++ b/tools/check_p23_life_observation_evidence.py @@ -314,6 +314,25 @@ def compare_actor_rows(snapshot, report, root=ROOT): def elevator_report(snapshot): saved = snapshot['systems']['runtime']['elevators'] + # ElevatorService.restore_snapshot validates this exact trip shape (412-438). + # QUEUED means no cabin seat yet, not an absent shaft assignment. + configs = {shaft['config']['shaft_id']: shaft['config'] for shaft in saved['shafts']} + for trip in saved['trips']: + keys = {'actor_id', 'request_id', 'shaft_id', 'connector_id', 'from', 'to', + 'enqueued_at', 'state'} + if 'companion_id' in trip: + keys.add('companion_id') + require(isinstance(trip['companion_id'], str) and trip['companion_id'], + 'invalid elevator companion identity') + require(set(trip) == keys, 'exact elevator trip keys') + require(trip['shaft_id'] in configs, 'elevator trip shaft attribution') + config = configs[trip['shaft_id']] + require(trip['from'] != trip['to'] and trip['from'] in config['stops'] + and trip['to'] in config['stops'], 'elevator trip stops') + low, high = sorted((trip['from'][1], trip['to'][1])) + require(trip['connector_id'] == f"{trip['shaft_id']}.link.{low}.{high}", + 'elevator trip connector attribution') + require(trip['state'] in ('QUEUED', 'BOARDED'), 'elevator trip state') rows = [] for shaft in saved['shafts']: row = copy.deepcopy(shaft) @@ -325,7 +344,8 @@ def elevator_report(snapshot): floor = shaft['phase_origin_floor'] + (destination - shaft['phase_origin_floor']) * progress row.update(visual_floor=floor, passenger_count=len(shaft['boarded']), passenger_ids=copy.deepcopy(shaft['boarded']), - waiting_count=sum(t['shaft_id'] == shaft['shaft_id'] and t['state'] == 'QUEUED' + # get_report() merges config before counting (345-359). + waiting_count=sum(t['shaft_id'] == row['shaft_id'] and t['state'] == 'QUEUED' for t in saved['trips'])) rows.append(row) # WorldPreview sorts its report's trip copy for queue placement. diff --git a/tools/test_p23_life_observation_evidence.py b/tools/test_p23_life_observation_evidence.py index 536fa73..609eab9 100644 --- a/tools/test_p23_life_observation_evidence.py +++ b/tools/test_p23_life_observation_evidence.py @@ -18,6 +18,54 @@ sys.dont_write_bytecode = True import check_p23_life_observation_evidence as check +class QueuedElevatorTests(unittest.TestCase): + def setUp(self): + fixture = json.loads((check.ROOT / 'tests/fixtures/p23-03/queued_elevator.json').read_text()) + self.state = {'systems': {'runtime': {'elevators': fixture['saved']}}} + + def test_queued_trip_without_cabin_seat_uses_config_shaft(self): + saved = self.state['systems']['runtime']['elevators'] + self.assertNotIn('shaft_id', saved['shafts'][0]) + self.assertEqual(saved['shafts'][0]['boarded'], []) + report = check.elevator_report(self.state) + self.assertEqual(report['shafts'][0]['waiting_count'], 1) + self.assertEqual(report['shafts'][0]['passenger_count'], 0) + self.assertAlmostEqual(report['shafts'][0]['visual_floor'], .9) + self.assertEqual(report['trips'], saved['trips']) + + def test_tampered_shaft_attribution_fails(self): + self.state['systems']['runtime']['elevators']['trips'][0]['shaft_id'] = 'wrong.shaft' + with self.assertRaisesRegex(ValueError, 'shaft attribution'): + check.elevator_report(self.state) + + def test_wrong_waiting_count_fails_whole_report_comparison(self): + expected = check.elevator_report(self.state) + tampered = copy.deepcopy(expected) + tampered['shafts'][0]['waiting_count'] = 0 + directory = mock.MagicMock() + manifest = dict(label='queued-fixture', pid=123, state='state', + state_after='state', report='report') + directory.__truediv__.return_value.open.return_value = io.StringIO(json.dumps(manifest)+'\n') + # Exercise the actual stream comparison; isolate unrelated actor fields. + with mock.patch.object(check, 'read_object', side_effect=[self.state, self.state, + {'elevators': tampered}]), \ + mock.patch.object(check, 'compare_actor_rows', return_value=[]): + with self.assertRaisesRegex(ValueError, 'typed elevator report mismatch'): + check.inspect_stream(directory) + + def test_extra_and_missing_trip_keys_fail(self): + for mutation in ('extra', 'missing'): + with self.subTest(mutation=mutation): + state = copy.deepcopy(self.state) + trip = state['systems']['runtime']['elevators']['trips'][0] + if mutation == 'extra': + trip['unexpected'] = True + else: + del trip['shaft_id'] + with self.assertRaisesRegex(ValueError, 'exact elevator trip keys'): + check.elevator_report(state) + + class SnapshotCellTests(unittest.TestCase): def test_layout_arrival_compares_serialized_cell_arrays(self): source = (check.ROOT / 'tests/fixtures/p23-03/layout_comparison.gd').read_text() @@ -431,12 +479,14 @@ class SpatialTests(unittest.TestCase): check.compare_traffic(self.snapshot, self.report) def test_missing_queue_overflow_rejected(self): - self.runtime['elevators']['trips'] = [dict(actor_id='guest:g'+str(i), enqueued_at=i, - state='QUEUED', **{'from': [0, 0, 0]}) for i in range(3)] - self.report.update(queue_positions={'guest/g0': [84, 82], 'guest/g1': [100, 82]}, - queue_hidden={'guest/g2': True}, landing_badges=[dict(at=[76, 54], - text='待ち3人(他1人)', cell=[0, 0, 0], - members=['guest/g0', 'guest/g1', 'guest/g2'], visible_count=2, overflow=1)]) + saved = json.loads((check.ROOT / 'tests/fixtures/p23-03/queued_elevator.json').read_text())['saved'] + self.runtime['elevators'] = saved + saved['trips'] = [dict(copy.deepcopy(saved['trips'][0]), actor_id='guest:g'+str(i), + request_id='trip'+str(i), enqueued_at=i) for i in range(3)] + self.report.update(queue_positions={'guest/g0': [100, 82]}, + queue_hidden={'guest/g1': True, 'guest/g2': True}, landing_badges=[dict(at=[-4, 54], + text='待ち3人(他2人)', cell=[0, 0, 0], + members=['guest/g0', 'guest/g1', 'guest/g2'], visible_count=1, overflow=2)]) check.compare_queues(self.snapshot, self.report) self.report['landing_badges'][0]['overflow'] = 0 with self.assertRaises(ValueError): @@ -470,7 +520,7 @@ class SpatialTests(unittest.TestCase): check.compare_room_services(self.snapshot, changed) def test_cabin_geometry_and_phase(self): - shaft = dict(shaft_id='lift', config=dict(stops=[[0, 0, 0]], width=2, capacity=4), + shaft = dict(config=dict(shaft_id='lift', stops=[[0, 0, 0]], width=2, capacity=4), current_floor=0, phase='IDLE', operational=True, boarded=[]) self.runtime['elevators']['shafts'] = [shaft] self.report.update(paused=True, animation_time=0, elevator_tracks={'lift': dict(floor=0, at=0)},