diff --git a/docs/plans/original-completion-2026-10-02/execution/p08-03/CONDITION_MATRIX.md b/docs/plans/original-completion-2026-10-02/execution/p08-03/CONDITION_MATRIX.md index c3c0a9e..7bc9fe2 100644 --- a/docs/plans/original-completion-2026-10-02/execution/p08-03/CONDITION_MATRIX.md +++ b/docs/plans/original-completion-2026-10-02/execution/p08-03/CONDITION_MATRIX.md @@ -31,6 +31,32 @@ and a budget report explicitly tagged diagnostic. No production costs, services, balance values or definitions are changed. New art is unnecessary; the demolition button reuses Main's existing styling and audio. +## r01 fixture repair (current candidate) + +The only failure authorities used are `NATIVE_R01_FAILURE_AND_SCOPE.json` +and `native-r01-writer-stderr.log`. The r01 writer failed 13 expectations; +its full SaveService return Dictionaries were not captured and remain absent. +No past Dictionary was reconstructed and no historical raw evidence was edited. + +The handler now changes a toggle button's pressed state, emitting the real +production `toggled` connection before `pressed`. Repeated `choose(id)` leaves +an already selected ID active. Production wiring is unchanged. +Initial and normal saves use private `user://saves/p08_initial` and +`user://saves/manual_ui`; separate handler readers SHA-copy the exported input +into their own private profile before normal-menu load. The initial diagnostic +package seeds the normal slot, so the actual normal save creates its backup. +Exports retain all four members, the backup, and verbatim manifest slot +metadata with SHA/byte lengths and actual paths. An absent initial backup is +explicitly recorded. Every observed Runtime save/load result and direct +SaveService diagnostic return is recorded as typed JSON and SHA-pinned raw +Variant, with PID, requested/absolute paths and user-data directory. + +The 35 local checker tests remain synthetic controls. Their negative cases +also cover missing backup members, slot metadata SHA, failed actual returns, +and saves outside private profiles. The mock collector supplies a controlled +disk-space input; the production disk-admission gate is unchanged. +Current native/OS/full regression/review/integration remain pending. + ## Evidence boundary `placement.gd` contains the shared finite scenario and handler endpoints. diff --git a/docs/plans/original-completion-2026-10-02/execution/p08-03/NATIVE_R01_FAILURE_AND_SCOPE.json b/docs/plans/original-completion-2026-10-02/execution/p08-03/NATIVE_R01_FAILURE_AND_SCOPE.json new file mode 100644 index 0000000..719b354 --- /dev/null +++ b/docs/plans/original-completion-2026-10-02/execution/p08-03/NATIVE_R01_FAILURE_AND_SCOPE.json @@ -0,0 +1,63 @@ +{ + "unit": "P08-03", + "submitted_commit": "7dd450c64c897fc8e9e6bbd9fa242f28f42cb1af", + "actual_local_final": { + "source_input_hash_mismatches": 0, + "synthetic_checker_tests": 35, + "passed": true, + "native_acceptance": false + }, + "native_output": "artifacts/original-completion-2026-10-02/p08-03-native-os-r01", + "phases": [ + { + "phase": "import", + "pid": 45015, + "passed": true + }, + { + "phase": "parse", + "pid": 45110, + "passed": true + }, + { + "phase": "writer", + "pid": 45149, + "passed": false, + "assert_failures": 13 + } + ], + "failure": "strict writer diagnostics; fixture methods do not match actual toggle/save API", + "observed": { + "build_report": "can_build=false with cells/error_codes/reasons empty", + "status": "initial UI help, no build results", + "formal_member_hashes": "empty because no formal save was created" + }, + "source_diagnosis": [ + { + "fixture": "button() only emits pressed", + "production": "build buttons connect toggled to _on_build_button_toggled", + "scope": "repair fixture; no evidence to change production button wiring" + }, + { + "fixture": "formal and initial-formal paths outside private user://", + "production": "SaveService only accepts paths under current user://", + "scope": "use legitimate private profile saves and SHA-copy formal members; preserve SaveService protection", + "certainty": "UNSAFE_PATH is source/path inference; full returned save Dictionary must be recorded next" + } + ], + "cleanup_errors": [], + "all_owned_children_ended": true, + "OS_input_performed": false, + "full_regression_performed": false, + "registered_copy": { + "files": 2047, + "whole_file_hashes_match": true + }, + "registered_validation": { + "passed": false, + "artifact": "/Users/korizatou114/Library/Application Support/agent-control/the-tree-p08-03-code/runs/ac8ca67c94324f719a327d98b5e833c3-validation.json" + }, + "acceptance_credit": 0, + "next": "Actual Opus request-repair judgment for same worktree, narrow fixture fixes", + "updated_utc": "2026-10-13T18:33:26.998526+00:00" +} diff --git a/docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_COLLECTION.md b/docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_COLLECTION.md index 9a71a0a..b26e675 100644 --- a/docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_COLLECTION.md +++ b/docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_COLLECTION.md @@ -31,6 +31,16 @@ All four formal members are copied and SHA-checked. The staged project gets only private-profile settings and is restored in finally. Only captured Popen children are cleaned by `owned_outer.finish_captured`. +The repaired handler also saves only inside its private `user://` profile, +then exports `initial-formal` and `formal` to the evidence directory. +`formal.bak`, `*-slot-metadata.json` (verbatim manifest), and `*-export.json` +retain backup/metadata bytes, source paths and SHA pins. Initial backup absence +is recorded explicitly. Each role's `*-save-results.json` references actual +typed result JSON and SHA-pinned raw Variant files, including failed returns. +Inspect the full returned dictionaries and paths on the next native run. +The r01 dictionaries were never captured; do not reconstruct them or append +new results to the historical r01 raw evidence. + Actual argv is `[engine, --path, stage, --script, passive_os.gd]`, with no headless flag or headless proof gate. `started.json`, `process-started.json`, `ended.json`, raw engine log, actual profile/engine report, source/stage/default diff --git a/docs/plans/original-completion-2026-10-02/execution/p08-03/implementation.json b/docs/plans/original-completion-2026-10-02/execution/p08-03/implementation.json index bca8ad3..5ec2b47 100644 --- a/docs/plans/original-completion-2026-10-02/execution/p08-03/implementation.json +++ b/docs/plans/original-completion-2026-10-02/execution/p08-03/implementation.json @@ -5,7 +5,7 @@ "P08-03" ], "base_commit": "12430ba89970bda349781e51157e72e48a4ac06f", - "actual_worktree_HEAD": "12430ba89970bda349781e51157e72e48a4ac06f", + "actual_worktree_HEAD": "7dd450c64c897fc8e9e6bbd9fa242f28f42cb1af", "worker_committed": false, "final_commit": null, "final_commit_note": "Root must freeze and record the submitted candidate; worker git writes are prohibited.", @@ -18,27 +18,27 @@ }, "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": "656400d0f9028ff7cf272b8e02be9c329f25adb6ff2039708ca04d0251be611c", - "docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_COLLECTION.md": "d2a0da74c3b469350e9130fbd8a024be49b4c8cc9ed09154c3966bf7642c38cc", + "docs/plans/original-completion-2026-10-02/execution/p08-03/CONDITION_MATRIX.md": "3cbdc2bd8eccb8cf1f060fb237072dc5b935e8cd147c74884c8c1abb2c810c81", + "docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_COLLECTION.md": "ed1d3978bba78a9ffe68808ec1f1f1990ae180630b5eace9587b8dd1719b43ef", "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": "56f80f0e8a44a707c2c59e385c2fa5bd3949f1dd95d4cbf40f480870aca08d45", - "tests/fixtures/p08-03/passive_placement.gd": "f070c93000d0eceef8cc9e518fefa2ba9c7608883ab5bf9198dff224b60d4da1", + "tests/fixtures/p08-03/checker_fixture.py": "c6dd2471f408a790f174e5ab73bbdbd899838f51a0dcde91bd0655da63238c46", + "tests/fixtures/p08-03/passive_placement.gd": "c17a1874fa585ec1ea892b51860f0997176df084b08751ea41e27ea5bc1189e8", "tests/fixtures/p08-03/passive_placement.gd.uid": "f75e3f2d9555c30cee3a1de882f32225fc624565a78a7ef3f5c604325c578ce3", - "tests/fixtures/p08-03/placement.gd": "95d46b329d5b08954915a4f80b06b7774eeaf4976ef9c687e8c61433bf0d14eb", + "tests/fixtures/p08-03/placement.gd": "84296f340e26daff3e7cff50ae6e1d5ef8f717a39d9dfa9ff1b590059fa00a81", "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": "d79df59d4434afd5219aca9aeb4581119859d08a71c9df73ae25f01e70ab59f5", + "tools/check_p08_normal_placement_evidence.py": "9805c595b95172926f515e5600aaa14e19c83f0e9a2f88406b17656076b0e82c", "tools/regression/suite-policy.json": "e875e6b05bbac8dfb98c2af007221436fa558ddb8806b6214689830295b32e8d", "tools/run_p08_normal_placement.py": "191831b4f684bdd7e99b3be6038c15a6830f544d17c941869b1c2a8fbe4a2aa3", - "tools/test_p08_normal_placement_evidence.py": "5a4a859817b7764a07d215c4811d2912c7f502de7219d57b11c3e3158dc177eb" + "tools/test_p08_normal_placement_evidence.py": "2c493349de1d72d1eae383356f8bf3904096c810550f9de373816a25d5ae54c8" }, "executed_source_manifest": { "path": "local-source-manifest.json", - "sha256": "63cd7079a4e9fb4966d829b5edcccdaedd82154c3afa2ea715622cb3d0b437b5", + "sha256": "f3b5d4edb152c685bd10efa4ccbdb87fda5e3bdbb26a60c66bc12c1780a41003", "source_file_count": 1326, "contains": "Complete local execution source map and immutable/fixture input map; no material-size omission" }, @@ -50,19 +50,21 @@ "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": "56f80f0e8a44a707c2c59e385c2fa5bd3949f1dd95d4cbf40f480870aca08d45", - "tests/fixtures/p08-03/passive_placement.gd": "f070c93000d0eceef8cc9e518fefa2ba9c7608883ab5bf9198dff224b60d4da1", + "tests/fixtures/p08-03/checker_fixture.py": "c6dd2471f408a790f174e5ab73bbdbd899838f51a0dcde91bd0655da63238c46", + "tests/fixtures/p08-03/passive_placement.gd": "c17a1874fa585ec1ea892b51860f0997176df084b08751ea41e27ea5bc1189e8", "tests/fixtures/p08-03/passive_placement.gd.uid": "f75e3f2d9555c30cee3a1de882f32225fc624565a78a7ef3f5c604325c578ce3", - "tests/fixtures/p08-03/placement.gd": "95d46b329d5b08954915a4f80b06b7774eeaf4976ef9c687e8c61433bf0d14eb", - "tests/fixtures/p08-03/placement.gd.uid": "5caca08c1e627e0ca07e2ab7b13bd82b24a22f77513fe87e30b79023568e1987" + "tests/fixtures/p08-03/placement.gd": "84296f340e26daff3e7cff50ae6e1d5ef8f717a39d9dfa9ff1b590059fa00a81", + "tests/fixtures/p08-03/placement.gd.uid": "5caca08c1e627e0ca07e2ab7b13bd82b24a22f77513fe87e30b79023568e1987", + "docs/plans/original-completion-2026-10-02/execution/p08-03/NATIVE_R01_FAILURE_AND_SCOPE.json": "afc8dada6451e7e61d6d57abb0d7d040f5abcdb93432c8431c1c920dc4984515", + "docs/plans/original-completion-2026-10-02/execution/p08-03/native-r01-writer-stderr.log": "07fe6cccad7305b2ec88cb91fa79fa22ec9269d7c99d62a1f69971f7c5d22686" }, "actual_local_validation": { "path": "local-validation.json", - "sha256": "09701dcb7ef6a6da695f1c9b1060107e64666a8f8f0256c3caf1da9cf2d8f487", - "python_syntax": "pass: four changed/new Python sources compiled in memory", + "sha256": "531616ae3151b7a506978e43b754e1d6a372508515510da65c0fccffbbfff1c0", + "python_syntax": "pass: four final Python sources compiled in memory", "checker_mock_tests": 35, "checker_mock_exit_code": 0, - "log_sha256": "c542a3079ac31374dd3fce5bc96ccb44ca8ccfc765f78fe9f18413c259c5f67b", + "log_sha256": "0779ddd3305f3a2c6c719240e036bb1d7f0d10f97ce21854a0f946feefcace93", "git_diff_check": "pass" }, "conditions": { @@ -138,5 +140,20 @@ }, "whole_P08_complete": false, "P08_01_all30_claimed": false, - "formal_acceptance": false + "formal_acceptance": false, + "repair": { + "prior_submitted_commit": "7dd450c64c897fc8e9e6bbd9fa242f28f42cb1af", + "failure_authorities": [ + "NATIVE_R01_FAILURE_AND_SCOPE.json", + "native-r01-writer-stderr.log" + ], + "changes": [ + "fixture toggled signal and idempotent choose", + "private user:// formal and initial saves; SHA exports of members/backup/slot metadata", + "actual returned Dictionaries and paths captured prospectively" + ], + "production_changes": false, + "historical_r01_dicts": "absent; not reconstructed or appended", + "worker_git_writes": false + } } diff --git a/docs/plans/original-completion-2026-10-02/execution/p08-03/local-source-manifest.json b/docs/plans/original-completion-2026-10-02/execution/p08-03/local-source-manifest.json index e03fb73..b6b2412 100644 --- a/docs/plans/original-completion-2026-10-02/execution/p08-03/local-source-manifest.json +++ b/docs/plans/original-completion-2026-10-02/execution/p08-03/local-source-manifest.json @@ -3678,20 +3678,20 @@ "sha256": "8f150654767de0c66329d7d9e84d306c4fa5a65c918e04fb520b626b39f68334" }, "tests/fixtures/p08-03/checker_fixture.py": { - "bytes": 20189, - "sha256": "56f80f0e8a44a707c2c59e385c2fa5bd3949f1dd95d4cbf40f480870aca08d45" + "bytes": 22311, + "sha256": "c6dd2471f408a790f174e5ab73bbdbd899838f51a0dcde91bd0655da63238c46" }, "tests/fixtures/p08-03/passive_placement.gd": { - "bytes": 9121, - "sha256": "f070c93000d0eceef8cc9e518fefa2ba9c7608883ab5bf9198dff224b60d4da1" + "bytes": 8799, + "sha256": "c17a1874fa585ec1ea892b51860f0997176df084b08751ea41e27ea5bc1189e8" }, "tests/fixtures/p08-03/passive_placement.gd.uid": { "bytes": 20, "sha256": "f75e3f2d9555c30cee3a1de882f32225fc624565a78a7ef3f5c604325c578ce3" }, "tests/fixtures/p08-03/placement.gd": { - "bytes": 13792, - "sha256": "95d46b329d5b08954915a4f80b06b7774eeaf4976ef9c687e8c61433bf0d14eb" + "bytes": 17684, + "sha256": "84296f340e26daff3e7cff50ae6e1d5ef8f717a39d9dfa9ff1b590059fa00a81" }, "tests/fixtures/p08-03/placement.gd.uid": { "bytes": 20, @@ -5218,8 +5218,8 @@ "sha256": "928353233bf85436a5aaf8c4f77f3f82472d94b8ad2646cb1b030baf92b3b5dc" }, "tools/check_p08_normal_placement_evidence.py": { - "bytes": 31120, - "sha256": "d79df59d4434afd5219aca9aeb4581119859d08a71c9df73ae25f01e70ab59f5" + "bytes": 34820, + "sha256": "9805c595b95172926f515e5600aaa14e19c83f0e9a2f88406b17656076b0e82c" }, "tools/check_p24_selection_evidence.py": { "bytes": 14622, @@ -5298,8 +5298,8 @@ "sha256": "fd9ef7ec63f53dfd5e04e261606c9303d01a2e6590e22a35fef2e35df1429280" }, "tools/test_p08_normal_placement_evidence.py": { - "bytes": 12374, - "sha256": "5a4a859817b7764a07d215c4811d2912c7f502de7219d57b11c3e3158dc177eb" + "bytes": 13526, + "sha256": "2c493349de1d72d1eae383356f8bf3904096c810550f9de373816a25d5ae54c8" }, "tools/verify_exported_opening.py": { "bytes": 1519, @@ -5314,10 +5314,12 @@ "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": "56f80f0e8a44a707c2c59e385c2fa5bd3949f1dd95d4cbf40f480870aca08d45", - "tests/fixtures/p08-03/passive_placement.gd": "f070c93000d0eceef8cc9e518fefa2ba9c7608883ab5bf9198dff224b60d4da1", + "tests/fixtures/p08-03/checker_fixture.py": "c6dd2471f408a790f174e5ab73bbdbd899838f51a0dcde91bd0655da63238c46", + "tests/fixtures/p08-03/passive_placement.gd": "c17a1874fa585ec1ea892b51860f0997176df084b08751ea41e27ea5bc1189e8", "tests/fixtures/p08-03/passive_placement.gd.uid": "f75e3f2d9555c30cee3a1de882f32225fc624565a78a7ef3f5c604325c578ce3", - "tests/fixtures/p08-03/placement.gd": "95d46b329d5b08954915a4f80b06b7774eeaf4976ef9c687e8c61433bf0d14eb", - "tests/fixtures/p08-03/placement.gd.uid": "5caca08c1e627e0ca07e2ab7b13bd82b24a22f77513fe87e30b79023568e1987" + "tests/fixtures/p08-03/placement.gd": "84296f340e26daff3e7cff50ae6e1d5ef8f717a39d9dfa9ff1b590059fa00a81", + "tests/fixtures/p08-03/placement.gd.uid": "5caca08c1e627e0ca07e2ab7b13bd82b24a22f77513fe87e30b79023568e1987", + "docs/plans/original-completion-2026-10-02/execution/p08-03/NATIVE_R01_FAILURE_AND_SCOPE.json": "afc8dada6451e7e61d6d57abb0d7d040f5abcdb93432c8431c1c920dc4984515", + "docs/plans/original-completion-2026-10-02/execution/p08-03/native-r01-writer-stderr.log": "07fe6cccad7305b2ec88cb91fa79fa22ec9269d7c99d62a1f69971f7c5d22686" } } diff --git a/docs/plans/original-completion-2026-10-02/execution/p08-03/local-validation.json b/docs/plans/original-completion-2026-10-02/execution/p08-03/local-validation.json index f1e82d8..98600c0 100644 --- a/docs/plans/original-completion-2026-10-02/execution/p08-03/local-validation.json +++ b/docs/plans/original-completion-2026-10-02/execution/p08-03/local-validation.json @@ -1,14 +1,14 @@ { "task_id": "P08-03", - "started_at": "2026-10-13T18:21:21.344634+00:00", - "ended_at": "2026-10-13T18:21:42.345749+00:00", + "started_at": "2026-10-13T18:51:54.296622+00:00", + "ended_at": "2026-10-13T18:52:13.521432+00:00", "syntax": { "method": "compile(source, filename, exec); no pyc", "files": { "tools/run_p08_normal_placement.py": "191831b4f684bdd7e99b3be6038c15a6830f544d17c941869b1c2a8fbe4a2aa3", - "tools/check_p08_normal_placement_evidence.py": "d79df59d4434afd5219aca9aeb4581119859d08a71c9df73ae25f01e70ab59f5", - "tools/test_p08_normal_placement_evidence.py": "5a4a859817b7764a07d215c4811d2912c7f502de7219d57b11c3e3158dc177eb", - "tests/fixtures/p08-03/checker_fixture.py": "56f80f0e8a44a707c2c59e385c2fa5bd3949f1dd95d4cbf40f480870aca08d45" + "tools/check_p08_normal_placement_evidence.py": "9805c595b95172926f515e5600aaa14e19c83f0e9a2f88406b17656076b0e82c", + "tools/test_p08_normal_placement_evidence.py": "2c493349de1d72d1eae383356f8bf3904096c810550f9de373816a25d5ae54c8", + "tests/fixtures/p08-03/checker_fixture.py": "c6dd2471f408a790f174e5ab73bbdbd899838f51a0dcde91bd0655da63238c46" }, "passed": true }, @@ -19,13 +19,13 @@ "tools/test_p08_normal_placement_evidence.py" ], "exit_code": 0, - "log": "local-checker-tests.log", - "log_sha256": "c542a3079ac31374dd3fce5bc96ccb44ca8ccfc765f78fe9f18413c259c5f67b", + "log": "repair-local-checker-tests.log", + "log_sha256": "0779ddd3305f3a2c6c719240e036bb1d7f0d10f97ce21854a0f946feefcace93", "synthetic_only": true, "test_count": 35 }, "local_source_manifest": "local-source-manifest.json", - "local_source_manifest_sha256": "63cd7079a4e9fb4966d829b5edcccdaedd82154c3afa2ea715622cb3d0b437b5", + "local_source_manifest_sha256": "f3b5d4edb152c685bd10efa4ccbdb87fda5e3bdbb26a60c66bc12c1780a41003", "source_and_inputs_unchanged_during_tests": true, "native_engines_started": 0, "OS_operations": 0, diff --git a/docs/plans/original-completion-2026-10-02/execution/p08-03/native-r01-writer-stderr.log b/docs/plans/original-completion-2026-10-02/execution/p08-03/native-r01-writer-stderr.log new file mode 100644 index 0000000..165f1f4 --- /dev/null +++ b/docs/plans/original-completion-2026-10-02/execution/p08-03/native-r01-writer-stderr.log @@ -0,0 +1,89 @@ +ERROR: P08 placement: diagnostic initial formal save + at: push_error (core/variant/variant_utility.cpp:1023) + GDScript backtrace (most recent call first): + [0] expect (res://tests/fixtures/p08-03/placement.gd:33) + [1] save_initial_fixture (res://tests/fixtures/p08-03/placement.gd:306) + [2] writer (res://tests/fixtures/p08-03/placement.gd:208) + [3] run (res://tests/fixtures/p08-03/placement.gd:261) + [4] frames (res://tests/fixtures/p08-03/placement.gd:65) +ERROR: P08 placement: unsupported preview refusal + at: push_error (core/variant/variant_utility.cpp:1023) + GDScript backtrace (most recent call first): + [0] expect (res://tests/fixtures/p08-03/placement.gd:33) + [1] build (res://tests/fixtures/p08-03/placement.gd:115) + [2] click (res://tests/fixtures/p08-03/placement.gd:94) + [3] frames (res://tests/fixtures/p08-03/placement.gd:65) +ERROR: P08 placement: out_of_bounds preview refusal + at: push_error (core/variant/variant_utility.cpp:1023) + GDScript backtrace (most recent call first): + [0] expect (res://tests/fixtures/p08-03/placement.gd:33) + [1] build (res://tests/fixtures/p08-03/placement.gd:115) + [2] click (res://tests/fixtures/p08-03/placement.gd:94) + [3] frames (res://tests/fixtures/p08-03/placement.gd:65) +ERROR: P08 placement: supported_connected accepted + at: push_error (core/variant/variant_utility.cpp:1023) + GDScript backtrace (most recent call first): + [0] expect (res://tests/fixtures/p08-03/placement.gd:33) + [1] build (res://tests/fixtures/p08-03/placement.gd:111) + [2] click (res://tests/fixtures/p08-03/placement.gd:94) + [3] frames (res://tests/fixtures/p08-03/placement.gd:65) +ERROR: P08 placement: overlap preview refusal + at: push_error (core/variant/variant_utility.cpp:1023) + GDScript backtrace (most recent call first): + [0] expect (res://tests/fixtures/p08-03/placement.gd:33) + [1] build (res://tests/fixtures/p08-03/placement.gd:115) + [2] click (res://tests/fixtures/p08-03/placement.gd:94) + [3] frames (res://tests/fixtures/p08-03/placement.gd:65) +ERROR: P08 placement: missing_connection domain refusal + at: push_error (core/variant/variant_utility.cpp:1023) + GDScript backtrace (most recent call first): + [0] expect (res://tests/fixtures/p08-03/placement.gd:33) + [1] build (res://tests/fixtures/p08-03/placement.gd:113) + [2] click (res://tests/fixtures/p08-03/placement.gd:94) + [3] frames (res://tests/fixtures/p08-03/placement.gd:65) +ERROR: P08 placement: lower_stair accepted + at: push_error (core/variant/variant_utility.cpp:1023) + GDScript backtrace (most recent call first): + [0] expect (res://tests/fixtures/p08-03/placement.gd:33) + [1] build (res://tests/fixtures/p08-03/placement.gd:111) + [2] click (res://tests/fixtures/p08-03/placement.gd:94) + [3] frames (res://tests/fixtures/p08-03/placement.gd:65) +ERROR: P08 placement: upper_stair accepted + at: push_error (core/variant/variant_utility.cpp:1023) + GDScript backtrace (most recent call first): + [0] expect (res://tests/fixtures/p08-03/placement.gd:33) + [1] build (res://tests/fixtures/p08-03/placement.gd:111) + [2] click (res://tests/fixtures/p08-03/placement.gd:94) + [3] frames (res://tests/fixtures/p08-03/placement.gd:65) +ERROR: P08 placement: refused_demolition result code + at: push_error (core/variant/variant_utility.cpp:1023) + GDScript backtrace (most recent call first): + [0] expect (res://tests/fixtures/p08-03/placement.gd:33) + [1] demolish (res://tests/fixtures/p08-03/placement.gd:145) + [2] frames (res://tests/fixtures/p08-03/placement.gd:65) +ERROR: P08 placement: refused_demolition result reason + at: push_error (core/variant/variant_utility.cpp:1023) + GDScript backtrace (most recent call first): + [0] expect (res://tests/fixtures/p08-03/placement.gd:33) + [1] demolish (res://tests/fixtures/p08-03/placement.gd:146) + [2] frames (res://tests/fixtures/p08-03/placement.gd:65) +ERROR: P08 placement: demolition result code + at: push_error (core/variant/variant_utility.cpp:1023) + GDScript backtrace (most recent call first): + [0] expect (res://tests/fixtures/p08-03/placement.gd:33) + [1] demolish (res://tests/fixtures/p08-03/placement.gd:145) + [2] frames (res://tests/fixtures/p08-03/placement.gd:65) +ERROR: P08 placement: rebuild accepted + at: push_error (core/variant/variant_utility.cpp:1023) + GDScript backtrace (most recent call first): + [0] expect (res://tests/fixtures/p08-03/placement.gd:33) + [1] build (res://tests/fixtures/p08-03/placement.gd:111) + [2] click (res://tests/fixtures/p08-03/placement.gd:94) + [3] frames (res://tests/fixtures/p08-03/placement.gd:65) +ERROR: P08 placement: normal save + at: push_error (core/variant/variant_utility.cpp:1023) + GDScript backtrace (most recent call first): + [0] expect (res://tests/fixtures/p08-03/placement.gd:33) + [1] writer (res://tests/fixtures/p08-03/placement.gd:223) + [2] button (res://tests/fixtures/p08-03/placement.gd:73) + [3] frames (res://tests/fixtures/p08-03/placement.gd:65) diff --git a/docs/plans/original-completion-2026-10-02/execution/p08-03/repair-local-checker-tests.log b/docs/plans/original-completion-2026-10-02/execution/p08-03/repair-local-checker-tests.log new file mode 100644 index 0000000..91ceaea --- /dev/null +++ b/docs/plans/original-completion-2026-10-02/execution/p08-03/repair-local-checker-tests.log @@ -0,0 +1,5 @@ +................................... +---------------------------------------------------------------------- +Ran 35 tests in 18.245s + +OK diff --git a/tests/fixtures/p08-03/checker_fixture.py b/tests/fixtures/p08-03/checker_fixture.py index 639ad9d..7161127 100644 --- a/tests/fixtures/p08-03/checker_fixture.py +++ b/tests/fixtures/p08-03/checker_fixture.py @@ -241,6 +241,24 @@ def make(base, source, checker): formal = folder/'formal' formal.mkdir() for member in checker.MEMBERS: (formal/member).write_text('synthetic '+member+'\n') + shutil.copytree(formal, folder/'formal.bak') + shutil.copyfile(formal/'manifest.json', folder/'formal-slot-metadata.json') + profile = '/synthetic-private-profile' + absolute = profile+'/saves/manual_ui' + put(folder/'formal-export.json', {'pid':pids[0], 'requested_path':'user://saves/manual_ui', + 'absolute_path':absolute, 'user_data_dir':profile, + 'packages':{name:{'present':True, 'source_path':absolute+suffix, + 'members':{n:{'sha256':sha(folder/name/n),'bytes':(folder/name/n).stat().st_size} for n in checker.MEMBERS}} + for name,suffix in [('formal',''),('formal.bak','.bak')]}, + 'slot_metadata':{'file':'formal-slot-metadata.json','sha256':sha(formal/'manifest.json')}}) + for role,pid,op in [('writer',pids[0],'save'),('reader',pids[1],'load')]: + stem = role+'-save-result-0001' + (folder/(stem+'.var')).write_bytes(b'synthetic raw Dictionary stand-in') + put(folder/(stem+'.json'), {'pid':pid,'operation':'runtime_operation', + 'requested_path':'user://saves/manual_ui','absolute_path':absolute,'user_data_dir':profile, + 'result':typed({'ok':True,'operation':op,'save_path':'user://saves/manual_ui'}), + 'raw_sha256':sha(folder/(stem+'.var'))}) + put(folder/(role+'-save-results.json'), {'pid':pid,'records':[stem]}) put(folder/'save.json', {'members': {n: sha(formal/n) for n in checker.MEMBERS}, 'pid': pids[0], 'cell': target, 'instance_id': 'f4'}) row = {'id': 'resume_overlap', 'kind': 'build', 'position': position(target), @@ -259,6 +277,14 @@ def make(base, source, checker): pair('pair', [101, 102], 'handler') pair('os-pair', [201, 202], 'native_os') shutil.copytree(out/'pair/formal', out/'pair/initial-formal') + initial = copy.deepcopy(json.loads((out/'pair/formal-export.json').read_text())) + initial.update(requested_path='user://saves/p08_initial',absolute_path='/synthetic-private-profile/saves/p08_initial') + initial['packages'] = {'initial-formal':initial['packages']['formal'], + 'initial-formal.bak':{'present':False,'source_path':initial['absolute_path']+'.bak','members':{}}} + initial['packages']['initial-formal']['source_path'] = initial['absolute_path'] + shutil.copyfile(out/'pair/initial-formal/manifest.json',out/'pair/initial-formal-slot-metadata.json') + initial['slot_metadata']['file'] = 'initial-formal-slot-metadata.json' + put(out/'pair/initial-formal-export.json',initial) for role, pid in (('writer',201),('reader',202)): prefix = 'os-runs/'+role+'/' folder = out/prefix diff --git a/tests/fixtures/p08-03/passive_placement.gd b/tests/fixtures/p08-03/passive_placement.gd index 83ced98..ced5883 100644 --- a/tests/fixtures/p08-03/passive_placement.gd +++ b/tests/fixtures/p08-03/passive_placement.gd @@ -73,6 +73,7 @@ func setup() -> void: if role == "writer": await load_menu(main.find_child("SaveLoadMenu", true, false)) var stored: Dictionary = SaveService.inspect_snapshot(ProjectSettings.globalize_path(String(main.manual_save_path))) + record_save_result("initial_inspect", String(main.manual_save_path), stored) expect(bool(stored.get("ok", false)), "read-only initial formal inspection") expect(runtime.create_snapshot() == stored.get("snapshot", {}), "OS initial formal model exact") expect(clock.paused, "diagnostic formal fixture starts paused") @@ -154,12 +155,7 @@ func load_menu(menu: MenuButton) -> void: await request(action) func archive_formal() -> void: - DirAccess.make_dir_recursive_absolute(directory.path_join("formal")) - for member: String in MEMBERS: - var source := ProjectSettings.globalize_path(String(main.manual_save_path)).path_join(member) - var destination := directory.path_join("formal/" + member) - expect(DirAccess.copy_absolute(source, destination) == OK, "archive formal " + member) - expect(FileAccess.get_sha256(source) == FileAccess.get_sha256(destination), "archive formal SHA " + member) + export_package(String(main.manual_save_path), "formal") func finish_observations() -> void: json_file(role + "-requests.json", {"pid": OS.get_process_id(), "requests": all_requests}) diff --git a/tests/fixtures/p08-03/placement.gd b/tests/fixtures/p08-03/placement.gd index f2beafc..a9ab948 100644 --- a/tests/fixtures/p08-03/placement.gd +++ b/tests/fixtures/p08-03/placement.gd @@ -18,6 +18,9 @@ var target := Vector3i(-8, 0, 0) var budget: Dictionary = {} var input_method := "handler" var observed_cell: Variant = null +var save_serial := 0 +var save_records: Array = [] +const INITIAL_SAVE := "user://saves/p08_initial" func annotate(row: Dictionary) -> void: row["input_method"] = input_method @@ -69,6 +72,8 @@ func button(node: BaseButton) -> void: expect(node != null and not node.disabled, "available production button") if node == null or node.disabled: return + if node.toggle_mode: + node.button_pressed = not node.button_pressed node.pressed.emit() await frames() @@ -77,7 +82,9 @@ func choose(id: StringName) -> void: await button(main.palette_toggle_button) var node: Button = main.build_buttons[id] await reveal(node) - await button(node) + if not node.button_pressed: + await button(node) + expect(node.button_pressed and main._selected_definition_id == id, "chosen production definition") func click(cell: Vector3i) -> Dictionary: var view: WorldPreview = main.world_preview @@ -253,6 +260,7 @@ func run(p_tree: SceneTree, output: String, requested_role: String) -> Dictionar expect(DirAccess.make_dir_recursive_absolute(directory) == OK, "diagnostic directory") runtime = tree.root.get_node("SimulationRuntime") clock = tree.root.get_node("GameClock") + runtime.save_operation_completed.connect(record_save_operation) await setup() runtime.command_completed.connect(observe) main.world_preview.cell_clicked.connect(selected_cell) @@ -265,10 +273,12 @@ func run(p_tree: SceneTree, output: String, requested_role: String) -> Dictionar if requested_role == "standalone": role = "reader" actions = [] + save_records = [] await reader(false) json_file(role + "-actions.json", {"setup": "diagnostic", "input_method": input_method, "actions": actions}) finish_observations() runtime.command_completed.disconnect(observe) + runtime.save_operation_completed.disconnect(record_save_operation) var cleanup: Dictionary = await Cleanup.free_owner_and_wait(tree, main) expect(cleanup.get(&"passed", false), "ordinary Main/audio cleanup") json_file(role + "-process.json", {"pid": OS.get_process_id(), "engine": Engine.get_version_info(), "checks": checks, "failures": failures, "role": requested_role, "setup": "diagnostic"}) @@ -296,18 +306,78 @@ func setup() -> void: clock.set_paused(true) tree.root.size = Vector2i(1440, 810) main = load("res://scenes/main/main.tscn").instantiate() - main.manual_save_path = directory.path_join("formal") + main.manual_save_path = "user://saves/manual_ui" + if role == "reader": + import_package(directory.path_join("formal"), String(main.manual_save_path)) tree.root.add_child(main) runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false}) func save_initial_fixture() -> void: # Explicit diagnostic input, not an OS action or natural-play save. - var saved: Dictionary = SaveService.save_snapshot(directory.path_join("initial-formal"), runtime.create_snapshot()) + var saved: Dictionary = SaveService.save_snapshot(INITIAL_SAVE, runtime.create_snapshot()) + record_save_result("initial_save", INITIAL_SAVE, saved) expect(bool(saved.get("ok", false)), "diagnostic initial formal save") + export_package(INITIAL_SAVE, "initial-formal") + # Seed the private normal slot, so its later normal save produces a backup. + import_package(ProjectSettings.globalize_path(INITIAL_SAVE), String(main.manual_save_path)) json_file("diagnostic-budget.json", budget) func archive_formal() -> void: - pass + export_package(String(main.manual_save_path), "formal") + +func record_save_operation(result: Dictionary) -> void: + record_save_result("runtime_operation", String(main.manual_save_path), result) + +func record_save_result(operation: String, path: String, result: Dictionary) -> void: + save_serial += 1 + var stem := "%s-save-result-%04d" % [role, save_serial] + var raw := FileAccess.open(directory.path_join(stem + ".var"), FileAccess.WRITE) + expect(raw != null, "raw save result") + if raw != null: + raw.store_buffer(var_to_bytes(result)) + raw.close() + json_file(stem + ".json", {"pid": OS.get_process_id(), "operation": operation, + "requested_path": path, "absolute_path": ProjectSettings.globalize_path(path), + "user_data_dir": OS.get_user_data_dir(), "result": Codec.new().typed(result), + "raw_sha256": FileAccess.get_sha256(directory.path_join(stem + ".var"))}) + save_records.append(stem) + json_file(role + "-save-results.json", {"pid": OS.get_process_id(), "records": save_records}) + +func import_package(source: String, destination: String) -> void: + var absolute := ProjectSettings.globalize_path(destination) + expect(DirAccess.make_dir_recursive_absolute(absolute) == OK, "private save directory") + for member: String in MEMBERS: + var origin := source.path_join(member) + var copy := absolute.path_join(member) + expect(DirAccess.copy_absolute(origin, copy) == OK, "private save input " + member) + expect(FileAccess.get_sha256(origin) == FileAccess.get_sha256(copy), "private save input SHA " + member) + +func export_package(path: String, name: String) -> void: + var absolute := ProjectSettings.globalize_path(path) + var pins := {} + for suffix: String in ["", ".bak"]: + var source := absolute + suffix + var present := DirAccess.dir_exists_absolute(source) + pins[name + suffix] = {"source_path": source, "present": present, "members": {}} + if not present: + expect(not suffix.is_empty(), "formal package exists") + continue + var destination := directory.path_join(name + suffix) + expect(DirAccess.make_dir_recursive_absolute(destination) == OK, "export directory") + for member: String in MEMBERS: + var origin := source.path_join(member) + var copy := destination.path_join(member) + expect(DirAccess.copy_absolute(origin, copy) == OK, "export " + member) + var digest := FileAccess.get_sha256(origin) + expect(not digest.is_empty() and digest == FileAccess.get_sha256(copy), "export SHA " + member) + pins[name + suffix]["members"][member] = {"sha256": digest, "bytes": FileAccess.get_file_as_bytes(copy).size()} + # Slot metadata lives in the existing manifest; copy it verbatim. + var metadata := absolute.path_join("manifest.json") + var metadata_copy := directory.path_join(name + "-slot-metadata.json") + expect(DirAccess.copy_absolute(metadata, metadata_copy) == OK, "slot metadata export") + json_file(name + "-export.json", {"pid": OS.get_process_id(), "requested_path": path, + "absolute_path": absolute, "user_data_dir": OS.get_user_data_dir(), "packages": pins, + "slot_metadata": {"file": name + "-slot-metadata.json", "sha256": FileAccess.get_sha256(metadata_copy)}}) func finish_observations() -> void: pass diff --git a/tools/check_p08_normal_placement_evidence.py b/tools/check_p08_normal_placement_evidence.py index 5003095..a53f963 100644 --- a/tools/check_p08_normal_placement_evidence.py +++ b/tools/check_p08_normal_placement_evidence.py @@ -316,6 +316,20 @@ def check(project_root, evidence_dir): process = document(directory+'/'+role+'-process.json') require(process['pid'] == pid and process['failures'] == 0 and process['checks'] > 0, 'process attribution') require(bool(process['engine']['hash']) and bool(process['engine']['string']), 'engine version') + save_log = document(directory+'/'+role+'-save-results.json') + require(save_log['pid'] == pid and save_log['records'], 'missing actual save results') + operations = [] + for stem in save_log['records']: + record = document(directory+'/'+stem+'.json') + raw = artifact(directory+'/'+stem+'.var') + require(record['pid'] == pid and record['raw_sha256'] == sha(raw), 'save result attribution/SHA') + path = record['requested_path'] + require(path.startswith('user://') and record['absolute_path'] == str(Path(record['user_data_dir'])/path[7:]), 'save outside private user profile') + result = decode(record['result']) + require(isinstance(result, dict) and result.get('ok') is True, 'actual save/load failed') + if record['operation'] == 'runtime_operation': + operations.append(result.get('operation')) + require(('save' in operations) if role == 'writer' else any(op in ('load','recovery','recovery_candidate') for op in operations), 'missing actual normal save/load result') doc = document(directory+'/'+role+'-actions.json') require(doc['setup'] == 'diagnostic', 'unlabelled diagnostic') expected = WRITER if role == 'writer' else ['resume_overlap'] @@ -361,6 +375,30 @@ def check(project_root, evidence_dir): require(set(saved['members']) == set(MEMBERS) and saved['pid'] == expected_pids[0], 'missing formal members/writer') for member in MEMBERS: require(saved['members'][member] == sha(artifact(directory+'/formal/'+member)), 'formal member changed') + exported = document(directory+'/formal-export.json') + require(exported['pid'] == expected_pids[0] and exported['requested_path'] == 'user://saves/manual_ui', 'formal export attribution') + absolute = str(Path(exported['user_data_dir'])/'saves/manual_ui') + require(exported['absolute_path'] == absolute, 'save outside private user profile') + for name in ('formal','formal.bak'): + package = exported['packages'][name] + require(package['present'] and package['source_path'] == absolute+('.bak' if name.endswith('.bak') else '') and set(package['members']) == set(MEMBERS), 'missing formal backup export') + for member, pin in package['members'].items(): + file = artifact(directory+'/'+name+'/'+member) + require(pin == {'sha256':sha(file),'bytes':file.stat().st_size}, 'formal export SHA') + metadata = exported['slot_metadata'] + require(metadata['file'] == 'formal-slot-metadata.json' and metadata['sha256'] == sha(artifact(directory+'/'+metadata['file'])) == saved['members']['manifest.json'], 'slot metadata SHA') + if not native_os: + initial = document(directory+'/initial-formal-export.json') + initial_absolute = str(Path(initial['user_data_dir'])/'saves/p08_initial') + require(initial['pid'] == expected_pids[0] and initial['requested_path'] == 'user://saves/p08_initial' and initial['absolute_path'] == initial_absolute, 'initial formal private path') + package = initial['packages']['initial-formal'] + require(package['present'] and package['source_path'] == initial_absolute and set(package['members']) == set(MEMBERS), 'initial formal members') + for member, pin in package['members'].items(): + file = artifact(directory+'/initial-formal/'+member) + require(pin == {'sha256':sha(file),'bytes':file.stat().st_size}, 'initial formal SHA') + require(initial['packages']['initial-formal.bak'] == {'present':False,'source_path':initial_absolute+'.bak','members':{}}, 'unexpected initial backup') + metadata = initial['slot_metadata'] + require(metadata['file'] == 'initial-formal-slot-metadata.json' and metadata['sha256'] == sha(artifact(directory+'/'+metadata['file'])) == package['members']['manifest.json']['sha256'], 'initial slot metadata SHA') facilities = keyed(decode(checkpoint)['systems']['runtime']['construction']['facilities'], 'instance_id') require(saved['instance_id'] in facilities and facilities[saved['instance_id']]['origin'] == saved['cell'], 'wrong resume instance') by_id = {r['id']: r for r in rows} diff --git a/tools/test_p08_normal_placement_evidence.py b/tools/test_p08_normal_placement_evidence.py index bc02bd5..16f83e9 100644 --- a/tools/test_p08_normal_placement_evidence.py +++ b/tools/test_p08_normal_placement_evidence.py @@ -124,15 +124,27 @@ class EvidenceTests(unittest.TestCase): def test_wrong_resume(self): path = 'pair/reader-checkpoint.json' + original = (self.out/path).read_bytes() model = C.decode(json.loads((self.out/path).read_text())) model['clock']['absolute_minute'] += 1 F.put(self.out/path, F.typed(model)); F.repin(self.out) self.reject('wrong resume model') + (self.out/path).write_bytes(original); F.repin(self.out) + self.edit('pair/reader-save-result-0001.json', lambda d: d.update(result=F.typed({'ok':False,'operation':'load','code':'UNSAFE_PATH'}))) + self.reject('actual save/load failed') + self.edit('pair/reader-save-result-0001.json', lambda d: d.update(result=F.typed({'ok':True,'operation':'load'}), requested_path='/outside/formal')) + self.reject('save outside private user profile') def test_missing_formal_member(self): - (self.out/'pair/formal/journal.log').unlink() - F.repin(self.out) - self.reject('journal.log') + for name in ('formal','formal.bak'): + with self.subTest(package=name): + file = self.out/('pair/'+name+'/journal.log') + original = file.read_bytes() + file.unlink(); F.repin(self.out) + self.reject('journal.log') + file.write_bytes(original); F.repin(self.out) + self.edit('pair/formal-export.json', lambda d: d['slot_metadata'].update(sha256='0'*64)) + self.reject('slot metadata SHA') def test_missing_model_member(self): path = 'pair/writer-unsupported-before.json' @@ -238,7 +250,9 @@ class EvidenceTests(unittest.TestCase): private_project=C.runner.private_project, fresh_environment=C.runner.fresh_environment, SUPPORT=ROOT/'tools/regression', module=lambda *a:SimpleNamespace(ordinary_gate=lambda *a:{'mock':True})) outer = SimpleNamespace(native=SimpleNamespace(helper={'live_engines':lambda:[]}),finish_captured=cleanup) - with patch.object(D.subprocess,'Popen',side_effect=spawn), contextlib.redirect_stdout(io.StringIO()): + # Disk admission is an environmental input to this fully mocked collector. + # Preserve the real collector's gate; make this fixture independent of host free space. + with patch.object(D.shutil,'disk_usage',return_value=SimpleNamespace(free=1024**3)), patch.object(D.subprocess,'Popen',side_effect=spawn), contextlib.redirect_stdout(io.StringIO()): if fail_wait: with self.assertRaisesRegex(AssertionError,'native OS collection failed'): D.collect_os(r,self.root,output,stage,str(Path(self.temp.name)/'synthetic-engine-not-executable'),'1'*40,refs,protected,outer)