P08-03: 配置の通常操作受入
This commit is contained in:
@@ -246,3 +246,36 @@ and implementation.json. These are mocked Python controls, not native or OS
|
||||
acceptance. Root native, OS, final full regression, independent review and
|
||||
integration/current verification remain pending, including Windows device
|
||||
acceptance. Existing driver2400, calibration and earlier repair work is retained.
|
||||
|
||||
## R07 actual-input observer repair (current submission)
|
||||
|
||||
Authority: ROOT_OS_R07_SELECTED_CELL_READBACK.json and its supplied raw writer
|
||||
actions/observations and screenshot. These historical failures remain unchanged:
|
||||
writer exit1, reader not started, and request64 is not current acceptance proof.
|
||||
The five condition records and acceptance denominator remain unchanged.
|
||||
|
||||
The passive read-only observer captures left-press viewport coordinates in
|
||||
_input before GUI side effects, inverse local coordinates, the actual converted
|
||||
world cell, bounds, cell dimensions, map origin, pan/zoom and both affine
|
||||
transforms. It also records the prior selection/occupant as separate dispatch
|
||||
fields. The action records the post-input selected entity and cell occupant
|
||||
separately. Later mouse-getter reads remain diagnostic only. The checker binds
|
||||
the action to the actual pressed event, independently recomputes its cell and
|
||||
transforms, and checks camera selection against complete-model occupancy.
|
||||
Requested center pixels remain distinct from actual event pixels; a press
|
||||
elsewhere within the same cell is valid.
|
||||
|
||||
All old tests remain; new whole-checker semantic controls cover missing dispatch,
|
||||
wrong actual cell, mismatched event coordinates, transform drift, wrong selected
|
||||
entity/occupancy and bypassed selection. A positive control demonstrates that
|
||||
a later diagnostic getter cannot change acceptance. Final syntax, test counts
|
||||
and source/input hashes are recorded in repair-r07-local-validation.json and
|
||||
repair-r07-local-source-manifest.json, referenced by implementation.json.
|
||||
|
||||
No product changes, Godot/native/OS/full regression, git writes, Runner actions
|
||||
or integration were performed by this worker. Driver receipt values remain
|
||||
2400 seconds and all timeout cleanup guards remain. Root must collect actual
|
||||
native and OS evidence on the final candidate, perform verify-only and final
|
||||
full regression, then obtain a separate independent review of all five facets.
|
||||
Any proposed historical reuse requires exact source/input applicability and
|
||||
independent review; no changed-source PASS is inherited here.
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"unit": "P08-03",
|
||||
"candidate": "0c5e0022f2d809e4735804f67582b9c96a1dc025",
|
||||
"native4_phases": "pass",
|
||||
"local75": "pass final SHA matched",
|
||||
"actual_OS_requests": 64,
|
||||
"OS_writer_pid": 35236,
|
||||
"OS_timeout_seconds": 2400,
|
||||
"actual_failures": [
|
||||
"observed OS selected cell",
|
||||
"camera world identity"
|
||||
],
|
||||
"raw_recorded_selected_cell": [],
|
||||
"actual_input_viewport": [
|
||||
625.5,
|
||||
580.5
|
||||
],
|
||||
"preclick_viewport_transform": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
79.0,
|
||||
65.0
|
||||
],
|
||||
"local_actual_input": [
|
||||
546.5,
|
||||
515.5
|
||||
],
|
||||
"map_position": [
|
||||
475.0,
|
||||
360.0
|
||||
],
|
||||
"cell_dimensions": [
|
||||
16.0,
|
||||
24.0
|
||||
],
|
||||
"bounds": {
|
||||
"max_x": 12,
|
||||
"max_y": 6,
|
||||
"min_x": -12,
|
||||
"min_y": 0
|
||||
},
|
||||
"derived_actual_input_cell": [
|
||||
-8,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"formula_source": "scenes/main/world_preview.gd:391-401",
|
||||
"UI_after_click": "request0063 screen-after.png shows selected reception at coordinate(-8,0)",
|
||||
"observer_source": "tests/fixtures/p08-03/passive_placement.gd:156-161 uses view.canvas_to_cell(view.get_local_mouse_position()) after awaiting Root and frames",
|
||||
"diagnosis_limit": "raw getter value was not recorded; exact reason it returns null remains unproven. Input-event-derived cell and actual selected-entity/UI identity must both be verified, not replaced by requested cell.",
|
||||
"ordinary_save": "request0064 actual normal save completed and exported formal",
|
||||
"OS_reader": "not started, writer exit1",
|
||||
"owned_ended": true,
|
||||
"guard_errors": [],
|
||||
"cleanup_errors": [],
|
||||
"formal_acceptance": false,
|
||||
"requested_fix": "Capture actual left-press world coordinate from read-only input event before UI side effects; retain matching selected entity ID check and whole-model guards. Add negative wrong-input-cell/entity tests. Never alter raw failed proof or waive native exit/error criteria. Opus should assess narrowly applicable evidence reuse to avoid needless duplicate replay."
|
||||
}
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 171 KiB |
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -18,23 +18,23 @@
|
||||
},
|
||||
"final_local_files_sha256": {
|
||||
"docs/plans/original-completion-2026-10-02/execution/completion_marker_contracts.json": "f59f01f65c795057117fda753706a6200928d4954af901fc965cf1a7c09232b5",
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/CONDITION_MATRIX.md": "a1b1c5f29863696ac05f6aa2061b7f6a0cf9b227400f9b53bed4708e4c9d6ba3",
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/CONDITION_MATRIX.md": "10068b65fedf08bcec7edb3bf1797b7af3776a7ced1ffb9a8a64bf98bd49a757",
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_COLLECTION.md": "0bf5a4b0f6f1fc21e4e0dd314f74030cdae9073c7531f77fb8b6177dfb0b2823",
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/passive_os_placement_recorder.gd": "21855e3b32263eb98127202467b770cb1334fa70c6b357825a122db385ffa8d6",
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/passive_os_placement_recorder.gd.uid": "d7f97560c8bbdfdaed173b2b48261e65b92847bd0493dd1cc2ebb309d0622314",
|
||||
"scenes/main/main.gd": "e015e18fee8c114ddce39e46a04be139e694a4e3569855065576c0c909b94fc6",
|
||||
"scenes/main/world_preview.gd": "2f0e417ae2ff1745062bdbdcec74b3b058cfcffbbd9001ca3e6a62cd23efb47d",
|
||||
"tests/fixtures/p08-03/checker_fixture.py": "508e8bc0f9ff3dc578a3b34fdfb3354fe31e5e604e2d7731cd25511c4261e93a",
|
||||
"tests/fixtures/p08-03/passive_placement.gd": "ca2a8b83d6657a858823645ad8d29aed100637e6a620e9d1c451780118409e81",
|
||||
"tests/fixtures/p08-03/checker_fixture.py": "e83a94dfcfd490a99bd44ed71bd6c378de94413750cfce68dea938bfbbbf011f",
|
||||
"tests/fixtures/p08-03/passive_placement.gd": "4bf9e4cf0424c2965d28c378e07f00fdb8388f4e3f51975a92128bd0e921242d",
|
||||
"tests/fixtures/p08-03/passive_placement.gd.uid": "f75e3f2d9555c30cee3a1de882f32225fc624565a78a7ef3f5c604325c578ce3",
|
||||
"tests/fixtures/p08-03/placement.gd": "1ebaaa80784905d661954e255bfb8a96a3a963a57b9a38b19e7f91b90fb628b3",
|
||||
"tests/fixtures/p08-03/placement.gd.uid": "5caca08c1e627e0ca07e2ab7b13bd82b24a22f77513fe87e30b79023568e1987",
|
||||
"tests/ui/test_p08_normal_placement_ops.gd": "734f4f85d0b0fc45dc31fc23502425b911635667dabc0341ba0d88b0107f4405",
|
||||
"tests/ui/test_p08_normal_placement_ops.gd.uid": "7d168ac50ca82ae1979da31c467f9a5020ca5ec7616b5bf99294413d85b3b299",
|
||||
"tools/check_p08_normal_placement_evidence.py": "dfa37ed586240972e57cbb6a962beb856ba289abd58dbfaf83706d3a227a0116",
|
||||
"tools/check_p08_normal_placement_evidence.py": "e3fea19be9952cd99be4df003734c43e6f81580b79984addb3622d81c1a2cc4e",
|
||||
"tools/regression/suite-policy.json": "e875e6b05bbac8dfb98c2af007221436fa558ddb8806b6214689830295b32e8d",
|
||||
"tools/run_p08_normal_placement.py": "5cb1f58664a0bec71c8b5e853749d1c3656e0a2a027201e33fbaa6b0c0291fc6",
|
||||
"tools/test_p08_normal_placement_evidence.py": "8b98647990e45588e117b6e9ecb87e5a1fcf8d83ddbd6c27da8b0a2edd74da86",
|
||||
"tools/test_p08_normal_placement_evidence.py": "f75ce5a2aeee080864d252dc4285e32f107ce0b48b7f62dd598665a13ffa5637",
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/NATIVE_R02_READER_OPERATION_DELTA.json": "60463e735d4f473bdad7b0925c37b9ca71ee34d6f0eb74ea6c07b508b5153e41",
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/native-r02-reader-stderr.log": "8079b3dc2ba7b9a9c37fcd9a5a8df0971354027c60def4b95fb15c7310832040",
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/OS_R04_INITIAL_LOAD_ORACLE_FINDING.json": "6a796bd63f5a16bd0bf0fcf07cec3f4414a5e6704409a79522a2677eae53b417",
|
||||
@@ -44,10 +44,10 @@
|
||||
"tests/fixtures/p08-03/r05-reader-checkpoint-provenance.json": "daf034375facf36e9cc8da4ec0001d7c7b017c315a9155680b4b490e582ec5b9"
|
||||
},
|
||||
"executed_source_manifest": {
|
||||
"path": "local-source-manifest.json",
|
||||
"sha256": "a8f442062ef4d4c791e07ad00271c7123c80997bf76897f12c78c303d90173a8",
|
||||
"path": "repair-r07-local-source-manifest.json",
|
||||
"sha256": "bd23d9f9ce58be85ebd3749112b281d7d71cf4eb9733d4aecf2e57a15f4e8ca7",
|
||||
"source_file_count": 1328,
|
||||
"contains": "Complete final local execution source and immutable/fixture/evidence input map; no material-size omission"
|
||||
"contains": "Complete final local source and fixture/evidence input map; no material-size omission"
|
||||
},
|
||||
"final_input_sha256": {
|
||||
"AGENTS.md": "8d8dc40211390275f858f322743309a0663c4f2d859780a8b8a08cea1450e8b3",
|
||||
@@ -57,8 +57,8 @@
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/passive_os_placement_recorder.gd.uid": "d7f97560c8bbdfdaed173b2b48261e65b92847bd0493dd1cc2ebb309d0622314",
|
||||
"docs/spec/acceptance/P08-03.json": "37a6a581b5a9bbe03c07a3a266b03df1f740ff488da33ecd9bd60f5b32f1efe1",
|
||||
"docs/traceability/original/BASELINE.txt": "75e4bdbdeb379c9269553f0c776bdb9cc96e01f2d6aa5ca650d132c63a8aeb1a",
|
||||
"tests/fixtures/p08-03/checker_fixture.py": "508e8bc0f9ff3dc578a3b34fdfb3354fe31e5e604e2d7731cd25511c4261e93a",
|
||||
"tests/fixtures/p08-03/passive_placement.gd": "ca2a8b83d6657a858823645ad8d29aed100637e6a620e9d1c451780118409e81",
|
||||
"tests/fixtures/p08-03/checker_fixture.py": "e83a94dfcfd490a99bd44ed71bd6c378de94413750cfce68dea938bfbbbf011f",
|
||||
"tests/fixtures/p08-03/passive_placement.gd": "4bf9e4cf0424c2965d28c378e07f00fdb8388f4e3f51975a92128bd0e921242d",
|
||||
"tests/fixtures/p08-03/passive_placement.gd.uid": "f75e3f2d9555c30cee3a1de882f32225fc624565a78a7ef3f5c604325c578ce3",
|
||||
"tests/fixtures/p08-03/placement.gd": "1ebaaa80784905d661954e255bfb8a96a3a963a57b9a38b19e7f91b90fb628b3",
|
||||
"tests/fixtures/p08-03/placement.gd.uid": "5caca08c1e627e0ca07e2ab7b13bd82b24a22f77513fe87e30b79023568e1987",
|
||||
@@ -67,15 +67,20 @@
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_CHECKER_R05_INFRASTRUCTURE_ORACLE.json": "9fd11c89e53d4fa569f809c06be1d0592741c58691fae3e515cd1c12da86758c",
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_OS_R05_GUIDE_INTERCEPTION.json": "b8a9041b59b87c7b01ebbf36f320a5227734034c6b2d45d26fd22ce654d2c612",
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_R05_READER_CHECKPOINT.json.gz": "27fd3b41834106ccd6a0980dfa2bc91cc19806dd3f552ba04246efb400763233",
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_R05_READER_CHECKPOINT_PROVENANCE.json": "daf034375facf36e9cc8da4ec0001d7c7b017c315a9155680b4b490e582ec5b9"
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_R05_READER_CHECKPOINT_PROVENANCE.json": "daf034375facf36e9cc8da4ec0001d7c7b017c315a9155680b4b490e582ec5b9",
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_NATIVE_R06_OS_TIMEOUT.json": "7e3068026e0f779cd05273b8e4d64e78f7cb923c9241172c6bad7b347682445e",
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_OS_R07_SELECTED_CELL_READBACK.json": "2c33d781a5b3cce497337e9d10006a0ad5ee020ffa489374ec3133553abcbf69",
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_OS_R07_SELECTED_RECEPTION.png": "46790a7a14cd0452c0f3aa167ec8510675648ccbdd3e30567ea0ca5095b1e9c4",
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_OS_R07_writer-actions.json.gz": "f0e71967a29c64a122627de5f0ca90797a384c9b800c461cd38055d9b6eb95ad",
|
||||
"docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_OS_R07_writer-request-0063_observed.json.gz": "59170312cb02a25b172c6ab7ccb7cfb495e17989ae20b2adb83c49d011678e58"
|
||||
},
|
||||
"actual_local_validation": {
|
||||
"path": "local-validation.json",
|
||||
"sha256": "6393caaffaae4e289871e96b241ea3aeb94fbba385b39fcd71d6bf14e2d1c12a",
|
||||
"path": "repair-r07-local-validation.json",
|
||||
"sha256": "38ee3e0bfa6416da2adbad194c96b57c2e3da7b23475f38dd9c17f46e2b1870f",
|
||||
"python_syntax": "pass: four final Python sources parsed in memory",
|
||||
"checker_control_tests": 75,
|
||||
"checker_control_tests": 83,
|
||||
"checker_exit_code": 0,
|
||||
"log_sha256": "74a86ece9204fadf39c763ec089b46b94ecfb9a04206c4aaeaeadda423879bda",
|
||||
"log_sha256": "60c535311af9c32b5f8ce706d88f99ef963217d000c1d12f8bcdd62b2a2ad0c3",
|
||||
"native_OS_full_regression": "not executed"
|
||||
},
|
||||
"conditions": {
|
||||
@@ -250,6 +255,30 @@
|
||||
"checker_exit_code": 0,
|
||||
"pending": "Root native, OS, full regression, independent review and integration"
|
||||
}
|
||||
},
|
||||
"r07": {
|
||||
"prior_submitted_commit": "0c5e0022f2d809e4735804f67582b9c96a1dc025",
|
||||
"authority": "ROOT_OS_R07_SELECTED_CELL_READBACK.json",
|
||||
"changes": [
|
||||
"Actual left-press dispatch coordinates, cell and pan/zoom affine transforms retained before GUI effects",
|
||||
"Post-input entity selection and occupancy recorded separately; later getter diagnostic only",
|
||||
"Checker binds pressed event and independently converts actual pixels; rejects wrong entity and complete-model occupancy"
|
||||
],
|
||||
"all_previous_75_tests_retained": true,
|
||||
"additional_tests": 8,
|
||||
"production_changes": false,
|
||||
"driver_changes": false,
|
||||
"OS_child_timeout_seconds": 2400,
|
||||
"raw_failed64_unchanged": true,
|
||||
"strict_native_exit_and_condition_records_unchanged": true,
|
||||
"context_gaps": [],
|
||||
"pending": [
|
||||
"Root actual native",
|
||||
"Root actual OS",
|
||||
"Root final full regression",
|
||||
"Separate independent review",
|
||||
"Integration/current verification"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
...................................................................................
|
||||
----------------------------------------------------------------------
|
||||
Ran 83 tests in 73.589s
|
||||
|
||||
OK
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
...................................................................................
|
||||
----------------------------------------------------------------------
|
||||
Ran 83 tests in 75.150s
|
||||
|
||||
OK
|
||||
+6670
File diff suppressed because it is too large
Load Diff
+29
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"task_id": "P08-03",
|
||||
"syntax": {
|
||||
"method": "ast.parse in memory; no pyc",
|
||||
"files": {
|
||||
"tools/run_p08_normal_placement.py": "5cb1f58664a0bec71c8b5e853749d1c3656e0a2a027201e33fbaa6b0c0291fc6",
|
||||
"tools/check_p08_normal_placement_evidence.py": "e3fea19be9952cd99be4df003734c43e6f81580b79984addb3622d81c1a2cc4e",
|
||||
"tools/test_p08_normal_placement_evidence.py": "f75ce5a2aeee080864d252dc4285e32f107ce0b48b7f62dd598665a13ffa5637",
|
||||
"tests/fixtures/p08-03/checker_fixture.py": "e83a94dfcfd490a99bd44ed71bd6c378de94413750cfce68dea938bfbbbf011f"
|
||||
},
|
||||
"passed": true
|
||||
},
|
||||
"checker_tests": {
|
||||
"argv": [
|
||||
"python3",
|
||||
"tools/test_p08_normal_placement_evidence.py"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"log": "repair-r07-final-local-checker-tests.log",
|
||||
"log_sha256": "60c535311af9c32b5f8ce706d88f99ef963217d000c1d12f8bcdd62b2a2ad0c3",
|
||||
"test_count": 83,
|
||||
"failures": 0
|
||||
},
|
||||
"native_engines_started": 0,
|
||||
"OS_operations": 0,
|
||||
"full_regression_executed": false,
|
||||
"acceptance": "pending",
|
||||
"local_source_manifest_sha256": "bd23d9f9ce58be85ebd3749112b281d7d71cf4eb9733d4aecf2e57a15f4e8ca7"
|
||||
}
|
||||
+10
-1
@@ -186,6 +186,11 @@ def make(base, source, checker):
|
||||
def os_meta(row, pid):
|
||||
row['input_method'] = method
|
||||
if method != 'native_os': return
|
||||
p = row['position']
|
||||
p['dispatch'] = {k:copy.deepcopy(v) for k,v in p.items() if k != 'selected_cell'}
|
||||
occupant = next((o['entity_id'] for o in runtime['world']['occupancies'] if p['world_cell'] in o['cells']), '')
|
||||
p['selection'] = {'required':row['id'] == 'pan_zoom', 'entity_id':occupant, 'occupant_id':occupant}
|
||||
p['diagnostics'] = {'getter_cell':[]}
|
||||
requests = [{'action':'cell', 'world_cell':row['position']['world_cell'],
|
||||
'viewport':row['position']['viewport'], 'screen':row['position']['screen'],
|
||||
'viewport_to_screen':[1,0,0,1,0,0], 'guide_visible':False, 'guide_rect':[0,0,0,0]}]
|
||||
@@ -200,7 +205,11 @@ def make(base, source, checker):
|
||||
prefix = directory+'/'+row['id']+'-'+str(sequence)+'/'
|
||||
request.update(target_pid=pid, sequence=sequence)
|
||||
put(out/(prefix+'request.json'), request)
|
||||
put(out/(prefix+'observed.json'), {'pid':pid,'sequence':sequence,'events':[{'synthetic':True}]})
|
||||
events = [{'synthetic':True}]
|
||||
if request['action'] == 'cell':
|
||||
events = [{'synthetic':True, 'button':1, 'pressed':True,
|
||||
'position':p['dispatch']['viewport'], 'dispatch':copy.deepcopy(p['dispatch'])}]
|
||||
put(out/(prefix+'observed.json'), {'pid':pid,'sequence':sequence,'events':events})
|
||||
receipt = directory+'/'+row['id']+'-os.json'
|
||||
put(out/receipt, {'method':'native_os','target_pid':pid,'timestamp_ns':10000,
|
||||
'collector_pid':999,'tool_name':'mock CUA','tool_call_id':'synthetic-only',
|
||||
|
||||
+39
-8
@@ -5,9 +5,11 @@ extends "res://tests/fixtures/p08-03/placement.gd"
|
||||
var receipts: Array = []
|
||||
var all_requests: Array = []
|
||||
var input_observer: Node
|
||||
var last_dispatch: Dictionary = {}
|
||||
|
||||
class InputObserver extends Node:
|
||||
var events: Array = []
|
||||
var endpoint: Node
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton or event is InputEventKey:
|
||||
var row := {"ticks_usec": Time.get_ticks_usec(), "text": event.as_text(), "pressed": event.is_pressed()}
|
||||
@@ -15,6 +17,8 @@ class InputObserver extends Node:
|
||||
row["button"] = event.button_index
|
||||
row["position"] = [event.position.x, event.position.y]
|
||||
row["ctrl"] = event.ctrl_pressed
|
||||
if endpoint != null and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
|
||||
row["dispatch"] = endpoint.dispatch_position(event.position)
|
||||
events.append(row)
|
||||
|
||||
func request(action: Dictionary) -> void:
|
||||
@@ -45,6 +49,9 @@ func request(action: Dictionary) -> void:
|
||||
tree.quit(1)
|
||||
return
|
||||
await frames()
|
||||
for event: Dictionary in input_observer.events.slice(first_event):
|
||||
if event.has("dispatch"):
|
||||
last_dispatch = event["dispatch"]
|
||||
json_file(stem + "/observed.json", {"pid": OS.get_process_id(), "sequence": serial,
|
||||
"events": input_observer.events.slice(first_event), "status": main.build_status_label.text})
|
||||
receipts.append("os-pair/" + stem + "/ack.json")
|
||||
@@ -86,6 +93,7 @@ func setup() -> void:
|
||||
tree.root.add_child(main)
|
||||
tree.root.title = "The Tree P08-03 " + role + " PID " + str(OS.get_process_id())
|
||||
input_observer = InputObserver.new()
|
||||
input_observer.endpoint = self
|
||||
tree.root.add_child(input_observer)
|
||||
await frames()
|
||||
json_file(role + "-profile.json", {"pid": OS.get_process_id(), "user_data_dir": OS.get_user_data_dir(), "engine": Engine.get_version_info(), "display": DisplayServer.get_name()})
|
||||
@@ -147,19 +155,23 @@ func click(cell: Vector3i) -> Dictionary:
|
||||
var mapping := affine(view.get_screen_transform() * view.get_global_transform_with_canvas().affine_inverse())
|
||||
var guide_rect: Rect2 = main.opening_guide.get_global_rect()
|
||||
observed_cell = null
|
||||
last_dispatch = {}
|
||||
await request({"action": "cell", "world_cell": vector(cell), "viewport": [viewport.x, viewport.y],
|
||||
"screen": [screen.x, screen.y], "viewport_to_screen": mapping,
|
||||
"guide_rect": [guide_rect.position.x, guide_rect.position.y, guide_rect.size.x, guide_rect.size.y],
|
||||
"guide_visible": main.opening_guide.visible,
|
||||
"instructions": "Click screen coordinates derived from viewport_to_screen (includes canvas stretch and window translation). Retain actual CUA coordinates and screenshots. If clipped or intercepted, recollect; never substitute fixed pixels."})
|
||||
var selected: Variant = observed_cell
|
||||
if not selected is Vector3i:
|
||||
# With no active build tool, the normal preview selects a facility.
|
||||
var occupant: StringName = runtime.world_service.get_occupant(cell)
|
||||
expect(not occupant.is_empty() and view.selected_entity_id == occupant, "actual OS facility selection")
|
||||
selected = view.canvas_to_cell(view.get_local_mouse_position())
|
||||
expect(selected is Vector3i and selected == cell, "observed OS selected cell")
|
||||
return {"world_cell": vector(cell), "selected_cell": vector(selected) if selected is Vector3i else [],
|
||||
var selected: Array = last_dispatch.get("world_cell", [])
|
||||
expect(selected == vector(cell), "observed OS selected cell")
|
||||
var occupant: StringName = runtime.world_service.get_occupant(cell)
|
||||
var selection_required: bool = view.build_definition == null and not view.excavation_mode and not view.demolition_mode and not occupant.is_empty()
|
||||
if selection_required:
|
||||
expect(view.selected_entity_id == occupant, "actual OS facility selection")
|
||||
var getter: Variant = view.canvas_to_cell(view.get_local_mouse_position())
|
||||
return {"world_cell": vector(cell), "selected_cell": selected,
|
||||
"dispatch": last_dispatch.duplicate(true),
|
||||
"selection": {"required": selection_required, "entity_id": String(view.selected_entity_id), "occupant_id": String(occupant)},
|
||||
"diagnostics": {"getter_cell": vector(getter) if getter is Vector3i else []},
|
||||
"local": [local.x, local.y], "viewport": [viewport.x, viewport.y],
|
||||
"viewport_transform": affine(view.get_global_transform_with_canvas()),
|
||||
"screen": [screen.x, screen.y], "screen_transform": affine(view.get_screen_transform()),
|
||||
@@ -168,6 +180,25 @@ func click(cell: Vector3i) -> Dictionary:
|
||||
"bounds": runtime.world_service.get_bounds(), "zoom": view.zoom_level,
|
||||
"pan": [view.pan_offset.x, view.pan_offset.y]}
|
||||
|
||||
func dispatch_position(position: Vector2) -> Dictionary:
|
||||
var view: WorldPreview = main.world_preview
|
||||
var local := view.get_global_transform_with_canvas().affine_inverse() * position
|
||||
var cell: Variant = view.canvas_to_cell(local)
|
||||
return {"world_cell": vector(cell) if cell is Vector3i else [],
|
||||
"entity_id_before": String(view.selected_entity_id),
|
||||
"occupant_id_before": String(runtime.world_service.get_occupant(cell)) if cell is Vector3i else "",
|
||||
"local": [local.x, local.y], "viewport": [position.x, position.y],
|
||||
"viewport_transform": affine(view.get_global_transform_with_canvas()),
|
||||
"screen_transform": affine(view.get_screen_transform()),
|
||||
"screen": vector2(view.get_screen_transform() * local),
|
||||
"bounds": runtime.world_service.get_bounds(),
|
||||
"map_position": vector2(view.get_map_rect().position),
|
||||
"cell_dimensions": vector2(view.get_cell_rect(Vector3i.ZERO).size),
|
||||
"zoom": view.zoom_level, "pan": vector2(view.pan_offset)}
|
||||
|
||||
func vector2(value: Vector2) -> Array:
|
||||
return [value.x, value.y]
|
||||
|
||||
func wheel(event: InputEventMouseButton, move_mode: bool) -> void:
|
||||
var action := point(main.world_preview)
|
||||
action.merge({"action": "wheel", "direction": "up" if event.button_index == MOUSE_BUTTON_WHEEL_UP else "down",
|
||||
|
||||
@@ -267,6 +267,31 @@ def coordinates(p):
|
||||
expected = [matrix[0]*x+matrix[2]*y+matrix[4], matrix[1]*x+matrix[3]*y+matrix[5]]
|
||||
require(len(p[target]) == 2 and all(abs(a-b) < 0.001 for a,b in zip(expected,p[target])), 'transformed '+target+' mismatch')
|
||||
|
||||
def dispatch_gate(p, events):
|
||||
d = p['dispatch']
|
||||
presses = [e for e in events if e.get('button') == 1 and e.get('pressed') is True]
|
||||
require(len(presses) == 1 and presses[0].get('dispatch') == d
|
||||
and presses[0]['position'] == d['viewport'], 'OS dispatch event mismatch')
|
||||
for key in ('bounds', 'map_position', 'cell_dimensions', 'zoom', 'pan',
|
||||
'viewport_transform', 'screen_transform'):
|
||||
require(d[key] == p[key], 'OS dispatch transform drift')
|
||||
x, y = d['local']
|
||||
require(math.isfinite(x) and math.isfinite(y), 'OS nonfinite dispatch')
|
||||
origin, size, bounds = d['map_position'], d['cell_dimensions'], d['bounds']
|
||||
derived = [bounds['min_x'] + math.floor((x-origin[0])/size[0]),
|
||||
bounds['max_y'] - math.floor((y-origin[1])/size[1]), p['world_cell'][2]]
|
||||
require(d['world_cell'] == derived == p['world_cell'], 'OS actual input cell drift')
|
||||
for target in ('viewport', 'screen'):
|
||||
matrix = d[target+'_transform']
|
||||
expected = [matrix[0]*x+matrix[2]*y+matrix[4], matrix[1]*x+matrix[3]*y+matrix[5]]
|
||||
require(len(d[target]) == 2 and all(math.isfinite(v) for v in d[target])
|
||||
and all(abs(a-b) < 0.001 for a,b in zip(expected,d[target])), 'OS dispatch coordinate drift')
|
||||
selection = p['selection']
|
||||
require(isinstance(selection['required'], bool), 'OS selection requirement')
|
||||
if selection['required']:
|
||||
require(selection['occupant_id'] and selection['entity_id'] == selection['occupant_id'],
|
||||
'OS selected entity mismatch')
|
||||
|
||||
def preserve(a, b, results):
|
||||
differences = typed_differences(a, b)
|
||||
if not results:
|
||||
@@ -644,6 +669,13 @@ def check(project_root, evidence_dir):
|
||||
for key in ('screen_before', 'screen_after'):
|
||||
png(artifact(ack[key]).read_bytes())
|
||||
if request['action'] == 'cell' and 'position' in row:
|
||||
dispatch_gate(row['position'], observed['events'])
|
||||
if row['id'] == 'pan_zoom':
|
||||
selection = row['position']['selection']
|
||||
require(selection['required'], 'OS missing entity selection')
|
||||
occupants = decode(document('os-pair/'+row['after']+'.json'))['systems']['runtime']['world']['occupancies']
|
||||
matches = [o['entity_id'] for o in occupants if row['position']['world_cell'] in o['cells']]
|
||||
require(matches == [selection['occupant_id']], 'OS selected occupancy mismatch')
|
||||
require(request['world_cell'] == row['position']['world_cell']
|
||||
and request['viewport'] == row['position']['viewport']
|
||||
and request['screen'] == row['position']['screen'], 'OS transformed cell drift')
|
||||
|
||||
@@ -329,6 +329,41 @@ class EvidenceTests(unittest.TestCase):
|
||||
self.edit('os-pair/unsupported-1/observed.json', lambda d: d.update(events=[]))
|
||||
self.reject('missing OS events')
|
||||
|
||||
def test_os_dispatch_missing(self):
|
||||
self.edit('os-pair/writer-actions.json', lambda d: d['actions'][0]['position'].pop('dispatch'))
|
||||
self.reject('dispatch')
|
||||
|
||||
def test_os_wrong_actual_input_cell(self):
|
||||
# Keep event and action mutually consistent; independent conversion must reject.
|
||||
self.edit('os-pair/writer-actions.json', lambda d: d['actions'][0]['position']['dispatch']['world_cell'].__setitem__(0, 99))
|
||||
self.edit('os-pair/unsupported-1/observed.json', lambda d: d['events'][0]['dispatch']['world_cell'].__setitem__(0, 99))
|
||||
self.reject('OS actual input cell drift')
|
||||
|
||||
def test_os_event_position_disagrees(self):
|
||||
self.edit('os-pair/unsupported-1/observed.json', lambda d: d['events'][0].update(position=[0,0]))
|
||||
self.reject('OS dispatch event mismatch')
|
||||
|
||||
def test_os_wrong_selected_entity(self):
|
||||
self.edit('os-pair/writer-actions.json', lambda d: d['actions'][-1]['position']['selection'].update(entity_id='wrong'))
|
||||
self.reject('OS selected entity mismatch')
|
||||
|
||||
def test_os_wrong_selected_occupancy(self):
|
||||
self.edit('os-pair/writer-actions.json', lambda d: d['actions'][-1]['position']['selection'].update(entity_id='wrong', occupant_id='wrong'))
|
||||
self.reject('OS selected occupancy mismatch')
|
||||
|
||||
def test_os_selection_requirement_cannot_be_removed(self):
|
||||
self.edit('os-pair/writer-actions.json', lambda d: d['actions'][-1]['position']['selection'].update(required=False))
|
||||
self.reject('OS missing entity selection')
|
||||
|
||||
def test_os_dispatch_transform_drift(self):
|
||||
self.edit('os-pair/writer-actions.json', lambda d: d['actions'][0]['position']['dispatch'].update(pan=[100,0]))
|
||||
self.edit('os-pair/unsupported-1/observed.json', lambda d: d['events'][0]['dispatch'].update(pan=[100,0]))
|
||||
self.reject('OS dispatch transform drift')
|
||||
|
||||
def test_os_later_getter_is_diagnostic(self):
|
||||
self.edit('os-pair/writer-actions.json', lambda d: d['actions'][-1]['position']['diagnostics'].update(getter_cell=[999,999,0]))
|
||||
self.assertEqual([], C.check(self.root, self.out))
|
||||
|
||||
def test_os_ack_for_another_request(self):
|
||||
self.edit('os-pair/unsupported-1/ack.json', lambda d: d.update(request_sha256='0'*64))
|
||||
self.reject('OS ack request mismatch')
|
||||
|
||||
Reference in New Issue
Block a user