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

This commit is contained in:
Agent Control Runner
2026-10-14 03:52:45 +09:00
parent 7dd450c64c
commit c2b59f04f9
13 changed files with 409 additions and 53 deletions
@@ -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; services, balance values or definitions are changed. New art is unnecessary;
the demolition button reuses Main's existing styling and audio. 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 ## Evidence boundary
`placement.gd` contains the shared finite scenario and handler endpoints. `placement.gd` contains the shared finite scenario and handler endpoints.
@@ -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"
}
@@ -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 only private-profile settings and is restored in finally. Only captured
Popen children are cleaned by `owned_outer.finish_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 Actual argv is `[engine, --path, stage, --script, passive_os.gd]`, with no
headless flag or headless proof gate. `started.json`, `process-started.json`, headless flag or headless proof gate. `started.json`, `process-started.json`,
`ended.json`, raw engine log, actual profile/engine report, source/stage/default `ended.json`, raw engine log, actual profile/engine report, source/stage/default
@@ -5,7 +5,7 @@
"P08-03" "P08-03"
], ],
"base_commit": "12430ba89970bda349781e51157e72e48a4ac06f", "base_commit": "12430ba89970bda349781e51157e72e48a4ac06f",
"actual_worktree_HEAD": "12430ba89970bda349781e51157e72e48a4ac06f", "actual_worktree_HEAD": "7dd450c64c897fc8e9e6bbd9fa242f28f42cb1af",
"worker_committed": false, "worker_committed": false,
"final_commit": null, "final_commit": null,
"final_commit_note": "Root must freeze and record the submitted candidate; worker git writes are prohibited.", "final_commit_note": "Root must freeze and record the submitted candidate; worker git writes are prohibited.",
@@ -18,27 +18,27 @@
}, },
"final_local_files_sha256": { "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/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/CONDITION_MATRIX.md": "3cbdc2bd8eccb8cf1f060fb237072dc5b935e8cd147c74884c8c1abb2c810c81",
"docs/plans/original-completion-2026-10-02/execution/p08-03/ROOT_COLLECTION.md": "d2a0da74c3b469350e9130fbd8a024be49b4c8cc9ed09154c3966bf7642c38cc", "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": "21855e3b32263eb98127202467b770cb1334fa70c6b357825a122db385ffa8d6",
"docs/plans/original-completion-2026-10-02/execution/p08-03/passive_os_placement_recorder.gd.uid": "d7f97560c8bbdfdaed173b2b48261e65b92847bd0493dd1cc2ebb309d0622314", "docs/plans/original-completion-2026-10-02/execution/p08-03/passive_os_placement_recorder.gd.uid": "d7f97560c8bbdfdaed173b2b48261e65b92847bd0493dd1cc2ebb309d0622314",
"scenes/main/main.gd": "e015e18fee8c114ddce39e46a04be139e694a4e3569855065576c0c909b94fc6", "scenes/main/main.gd": "e015e18fee8c114ddce39e46a04be139e694a4e3569855065576c0c909b94fc6",
"scenes/main/world_preview.gd": "2f0e417ae2ff1745062bdbdcec74b3b058cfcffbbd9001ca3e6a62cd23efb47d", "scenes/main/world_preview.gd": "2f0e417ae2ff1745062bdbdcec74b3b058cfcffbbd9001ca3e6a62cd23efb47d",
"tests/fixtures/p08-03/checker_fixture.py": "56f80f0e8a44a707c2c59e385c2fa5bd3949f1dd95d4cbf40f480870aca08d45", "tests/fixtures/p08-03/checker_fixture.py": "c6dd2471f408a790f174e5ab73bbdbd899838f51a0dcde91bd0655da63238c46",
"tests/fixtures/p08-03/passive_placement.gd": "f070c93000d0eceef8cc9e518fefa2ba9c7608883ab5bf9198dff224b60d4da1", "tests/fixtures/p08-03/passive_placement.gd": "c17a1874fa585ec1ea892b51860f0997176df084b08751ea41e27ea5bc1189e8",
"tests/fixtures/p08-03/passive_placement.gd.uid": "f75e3f2d9555c30cee3a1de882f32225fc624565a78a7ef3f5c604325c578ce3", "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/fixtures/p08-03/placement.gd.uid": "5caca08c1e627e0ca07e2ab7b13bd82b24a22f77513fe87e30b79023568e1987",
"tests/ui/test_p08_normal_placement_ops.gd": "734f4f85d0b0fc45dc31fc23502425b911635667dabc0341ba0d88b0107f4405", "tests/ui/test_p08_normal_placement_ops.gd": "734f4f85d0b0fc45dc31fc23502425b911635667dabc0341ba0d88b0107f4405",
"tests/ui/test_p08_normal_placement_ops.gd.uid": "7d168ac50ca82ae1979da31c467f9a5020ca5ec7616b5bf99294413d85b3b299", "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/regression/suite-policy.json": "e875e6b05bbac8dfb98c2af007221436fa558ddb8806b6214689830295b32e8d",
"tools/run_p08_normal_placement.py": "191831b4f684bdd7e99b3be6038c15a6830f544d17c941869b1c2a8fbe4a2aa3", "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": { "executed_source_manifest": {
"path": "local-source-manifest.json", "path": "local-source-manifest.json",
"sha256": "63cd7079a4e9fb4966d829b5edcccdaedd82154c3afa2ea715622cb3d0b437b5", "sha256": "f3b5d4edb152c685bd10efa4ccbdb87fda5e3bdbb26a60c66bc12c1780a41003",
"source_file_count": 1326, "source_file_count": 1326,
"contains": "Complete local execution source map and immutable/fixture input map; no material-size omission" "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/plans/original-completion-2026-10-02/execution/p08-03/passive_os_placement_recorder.gd.uid": "d7f97560c8bbdfdaed173b2b48261e65b92847bd0493dd1cc2ebb309d0622314",
"docs/spec/acceptance/P08-03.json": "37a6a581b5a9bbe03c07a3a266b03df1f740ff488da33ecd9bd60f5b32f1efe1", "docs/spec/acceptance/P08-03.json": "37a6a581b5a9bbe03c07a3a266b03df1f740ff488da33ecd9bd60f5b32f1efe1",
"docs/traceability/original/BASELINE.txt": "75e4bdbdeb379c9269553f0c776bdb9cc96e01f2d6aa5ca650d132c63a8aeb1a", "docs/traceability/original/BASELINE.txt": "75e4bdbdeb379c9269553f0c776bdb9cc96e01f2d6aa5ca650d132c63a8aeb1a",
"tests/fixtures/p08-03/checker_fixture.py": "56f80f0e8a44a707c2c59e385c2fa5bd3949f1dd95d4cbf40f480870aca08d45", "tests/fixtures/p08-03/checker_fixture.py": "c6dd2471f408a790f174e5ab73bbdbd899838f51a0dcde91bd0655da63238c46",
"tests/fixtures/p08-03/passive_placement.gd": "f070c93000d0eceef8cc9e518fefa2ba9c7608883ab5bf9198dff224b60d4da1", "tests/fixtures/p08-03/passive_placement.gd": "c17a1874fa585ec1ea892b51860f0997176df084b08751ea41e27ea5bc1189e8",
"tests/fixtures/p08-03/passive_placement.gd.uid": "f75e3f2d9555c30cee3a1de882f32225fc624565a78a7ef3f5c604325c578ce3", "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/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": { "actual_local_validation": {
"path": "local-validation.json", "path": "local-validation.json",
"sha256": "09701dcb7ef6a6da695f1c9b1060107e64666a8f8f0256c3caf1da9cf2d8f487", "sha256": "531616ae3151b7a506978e43b754e1d6a372508515510da65c0fccffbbfff1c0",
"python_syntax": "pass: four changed/new Python sources compiled in memory", "python_syntax": "pass: four final Python sources compiled in memory",
"checker_mock_tests": 35, "checker_mock_tests": 35,
"checker_mock_exit_code": 0, "checker_mock_exit_code": 0,
"log_sha256": "c542a3079ac31374dd3fce5bc96ccb44ca8ccfc765f78fe9f18413c259c5f67b", "log_sha256": "0779ddd3305f3a2c6c719240e036bb1d7f0d10f97ce21854a0f946feefcace93",
"git_diff_check": "pass" "git_diff_check": "pass"
}, },
"conditions": { "conditions": {
@@ -138,5 +140,20 @@
}, },
"whole_P08_complete": false, "whole_P08_complete": false,
"P08_01_all30_claimed": 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
}
} }
@@ -3678,20 +3678,20 @@
"sha256": "8f150654767de0c66329d7d9e84d306c4fa5a65c918e04fb520b626b39f68334" "sha256": "8f150654767de0c66329d7d9e84d306c4fa5a65c918e04fb520b626b39f68334"
}, },
"tests/fixtures/p08-03/checker_fixture.py": { "tests/fixtures/p08-03/checker_fixture.py": {
"bytes": 20189, "bytes": 22311,
"sha256": "56f80f0e8a44a707c2c59e385c2fa5bd3949f1dd95d4cbf40f480870aca08d45" "sha256": "c6dd2471f408a790f174e5ab73bbdbd899838f51a0dcde91bd0655da63238c46"
}, },
"tests/fixtures/p08-03/passive_placement.gd": { "tests/fixtures/p08-03/passive_placement.gd": {
"bytes": 9121, "bytes": 8799,
"sha256": "f070c93000d0eceef8cc9e518fefa2ba9c7608883ab5bf9198dff224b60d4da1" "sha256": "c17a1874fa585ec1ea892b51860f0997176df084b08751ea41e27ea5bc1189e8"
}, },
"tests/fixtures/p08-03/passive_placement.gd.uid": { "tests/fixtures/p08-03/passive_placement.gd.uid": {
"bytes": 20, "bytes": 20,
"sha256": "f75e3f2d9555c30cee3a1de882f32225fc624565a78a7ef3f5c604325c578ce3" "sha256": "f75e3f2d9555c30cee3a1de882f32225fc624565a78a7ef3f5c604325c578ce3"
}, },
"tests/fixtures/p08-03/placement.gd": { "tests/fixtures/p08-03/placement.gd": {
"bytes": 13792, "bytes": 17684,
"sha256": "95d46b329d5b08954915a4f80b06b7774eeaf4976ef9c687e8c61433bf0d14eb" "sha256": "84296f340e26daff3e7cff50ae6e1d5ef8f717a39d9dfa9ff1b590059fa00a81"
}, },
"tests/fixtures/p08-03/placement.gd.uid": { "tests/fixtures/p08-03/placement.gd.uid": {
"bytes": 20, "bytes": 20,
@@ -5218,8 +5218,8 @@
"sha256": "928353233bf85436a5aaf8c4f77f3f82472d94b8ad2646cb1b030baf92b3b5dc" "sha256": "928353233bf85436a5aaf8c4f77f3f82472d94b8ad2646cb1b030baf92b3b5dc"
}, },
"tools/check_p08_normal_placement_evidence.py": { "tools/check_p08_normal_placement_evidence.py": {
"bytes": 31120, "bytes": 34820,
"sha256": "d79df59d4434afd5219aca9aeb4581119859d08a71c9df73ae25f01e70ab59f5" "sha256": "9805c595b95172926f515e5600aaa14e19c83f0e9a2f88406b17656076b0e82c"
}, },
"tools/check_p24_selection_evidence.py": { "tools/check_p24_selection_evidence.py": {
"bytes": 14622, "bytes": 14622,
@@ -5298,8 +5298,8 @@
"sha256": "fd9ef7ec63f53dfd5e04e261606c9303d01a2e6590e22a35fef2e35df1429280" "sha256": "fd9ef7ec63f53dfd5e04e261606c9303d01a2e6590e22a35fef2e35df1429280"
}, },
"tools/test_p08_normal_placement_evidence.py": { "tools/test_p08_normal_placement_evidence.py": {
"bytes": 12374, "bytes": 13526,
"sha256": "5a4a859817b7764a07d215c4811d2912c7f502de7219d57b11c3e3158dc177eb" "sha256": "2c493349de1d72d1eae383356f8bf3904096c810550f9de373816a25d5ae54c8"
}, },
"tools/verify_exported_opening.py": { "tools/verify_exported_opening.py": {
"bytes": 1519, "bytes": 1519,
@@ -5314,10 +5314,12 @@
"docs/plans/original-completion-2026-10-02/execution/p08-03/passive_os_placement_recorder.gd.uid": "d7f97560c8bbdfdaed173b2b48261e65b92847bd0493dd1cc2ebb309d0622314", "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/spec/acceptance/P08-03.json": "37a6a581b5a9bbe03c07a3a266b03df1f740ff488da33ecd9bd60f5b32f1efe1",
"docs/traceability/original/BASELINE.txt": "75e4bdbdeb379c9269553f0c776bdb9cc96e01f2d6aa5ca650d132c63a8aeb1a", "docs/traceability/original/BASELINE.txt": "75e4bdbdeb379c9269553f0c776bdb9cc96e01f2d6aa5ca650d132c63a8aeb1a",
"tests/fixtures/p08-03/checker_fixture.py": "56f80f0e8a44a707c2c59e385c2fa5bd3949f1dd95d4cbf40f480870aca08d45", "tests/fixtures/p08-03/checker_fixture.py": "c6dd2471f408a790f174e5ab73bbdbd899838f51a0dcde91bd0655da63238c46",
"tests/fixtures/p08-03/passive_placement.gd": "f070c93000d0eceef8cc9e518fefa2ba9c7608883ab5bf9198dff224b60d4da1", "tests/fixtures/p08-03/passive_placement.gd": "c17a1874fa585ec1ea892b51860f0997176df084b08751ea41e27ea5bc1189e8",
"tests/fixtures/p08-03/passive_placement.gd.uid": "f75e3f2d9555c30cee3a1de882f32225fc624565a78a7ef3f5c604325c578ce3", "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/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"
} }
} }
@@ -1,14 +1,14 @@
{ {
"task_id": "P08-03", "task_id": "P08-03",
"started_at": "2026-10-13T18:21:21.344634+00:00", "started_at": "2026-10-13T18:51:54.296622+00:00",
"ended_at": "2026-10-13T18:21:42.345749+00:00", "ended_at": "2026-10-13T18:52:13.521432+00:00",
"syntax": { "syntax": {
"method": "compile(source, filename, exec); no pyc", "method": "compile(source, filename, exec); no pyc",
"files": { "files": {
"tools/run_p08_normal_placement.py": "191831b4f684bdd7e99b3be6038c15a6830f544d17c941869b1c2a8fbe4a2aa3", "tools/run_p08_normal_placement.py": "191831b4f684bdd7e99b3be6038c15a6830f544d17c941869b1c2a8fbe4a2aa3",
"tools/check_p08_normal_placement_evidence.py": "d79df59d4434afd5219aca9aeb4581119859d08a71c9df73ae25f01e70ab59f5", "tools/check_p08_normal_placement_evidence.py": "9805c595b95172926f515e5600aaa14e19c83f0e9a2f88406b17656076b0e82c",
"tools/test_p08_normal_placement_evidence.py": "5a4a859817b7764a07d215c4811d2912c7f502de7219d57b11c3e3158dc177eb", "tools/test_p08_normal_placement_evidence.py": "2c493349de1d72d1eae383356f8bf3904096c810550f9de373816a25d5ae54c8",
"tests/fixtures/p08-03/checker_fixture.py": "56f80f0e8a44a707c2c59e385c2fa5bd3949f1dd95d4cbf40f480870aca08d45" "tests/fixtures/p08-03/checker_fixture.py": "c6dd2471f408a790f174e5ab73bbdbd899838f51a0dcde91bd0655da63238c46"
}, },
"passed": true "passed": true
}, },
@@ -19,13 +19,13 @@
"tools/test_p08_normal_placement_evidence.py" "tools/test_p08_normal_placement_evidence.py"
], ],
"exit_code": 0, "exit_code": 0,
"log": "local-checker-tests.log", "log": "repair-local-checker-tests.log",
"log_sha256": "c542a3079ac31374dd3fce5bc96ccb44ca8ccfc765f78fe9f18413c259c5f67b", "log_sha256": "0779ddd3305f3a2c6c719240e036bb1d7f0d10f97ce21854a0f946feefcace93",
"synthetic_only": true, "synthetic_only": true,
"test_count": 35 "test_count": 35
}, },
"local_source_manifest": "local-source-manifest.json", "local_source_manifest": "local-source-manifest.json",
"local_source_manifest_sha256": "63cd7079a4e9fb4966d829b5edcccdaedd82154c3afa2ea715622cb3d0b437b5", "local_source_manifest_sha256": "f3b5d4edb152c685bd10efa4ccbdb87fda5e3bdbb26a60c66bc12c1780a41003",
"source_and_inputs_unchanged_during_tests": true, "source_and_inputs_unchanged_during_tests": true,
"native_engines_started": 0, "native_engines_started": 0,
"OS_operations": 0, "OS_operations": 0,
@@ -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)
@@ -0,0 +1,5 @@
...................................
----------------------------------------------------------------------
Ran 35 tests in 18.245s
OK
+26
View File
@@ -241,6 +241,24 @@ def make(base, source, checker):
formal = folder/'formal' formal = folder/'formal'
formal.mkdir() formal.mkdir()
for member in checker.MEMBERS: (formal/member).write_text('synthetic '+member+'\n') 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}, put(folder/'save.json', {'members': {n: sha(formal/n) for n in checker.MEMBERS},
'pid': pids[0], 'cell': target, 'instance_id': 'f4'}) 'pid': pids[0], 'cell': target, 'instance_id': 'f4'})
row = {'id': 'resume_overlap', 'kind': 'build', 'position': position(target), row = {'id': 'resume_overlap', 'kind': 'build', 'position': position(target),
@@ -259,6 +277,14 @@ def make(base, source, checker):
pair('pair', [101, 102], 'handler') pair('pair', [101, 102], 'handler')
pair('os-pair', [201, 202], 'native_os') pair('os-pair', [201, 202], 'native_os')
shutil.copytree(out/'pair/formal', out/'pair/initial-formal') 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)): for role, pid in (('writer',201),('reader',202)):
prefix = 'os-runs/'+role+'/' prefix = 'os-runs/'+role+'/'
folder = out/prefix folder = out/prefix
+2 -6
View File
@@ -73,6 +73,7 @@ func setup() -> void:
if role == "writer": if role == "writer":
await load_menu(main.find_child("SaveLoadMenu", true, false)) await load_menu(main.find_child("SaveLoadMenu", true, false))
var stored: Dictionary = SaveService.inspect_snapshot(ProjectSettings.globalize_path(String(main.manual_save_path))) 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(bool(stored.get("ok", false)), "read-only initial formal inspection")
expect(runtime.create_snapshot() == stored.get("snapshot", {}), "OS initial formal model exact") expect(runtime.create_snapshot() == stored.get("snapshot", {}), "OS initial formal model exact")
expect(clock.paused, "diagnostic formal fixture starts paused") expect(clock.paused, "diagnostic formal fixture starts paused")
@@ -154,12 +155,7 @@ func load_menu(menu: MenuButton) -> void:
await request(action) await request(action)
func archive_formal() -> void: func archive_formal() -> void:
DirAccess.make_dir_recursive_absolute(directory.path_join("formal")) export_package(String(main.manual_save_path), "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)
func finish_observations() -> void: func finish_observations() -> void:
json_file(role + "-requests.json", {"pid": OS.get_process_id(), "requests": all_requests}) json_file(role + "-requests.json", {"pid": OS.get_process_id(), "requests": all_requests})
+74 -4
View File
@@ -18,6 +18,9 @@ var target := Vector3i(-8, 0, 0)
var budget: Dictionary = {} var budget: Dictionary = {}
var input_method := "handler" var input_method := "handler"
var observed_cell: Variant = null 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: func annotate(row: Dictionary) -> void:
row["input_method"] = input_method row["input_method"] = input_method
@@ -69,6 +72,8 @@ func button(node: BaseButton) -> void:
expect(node != null and not node.disabled, "available production button") expect(node != null and not node.disabled, "available production button")
if node == null or node.disabled: if node == null or node.disabled:
return return
if node.toggle_mode:
node.button_pressed = not node.button_pressed
node.pressed.emit() node.pressed.emit()
await frames() await frames()
@@ -77,7 +82,9 @@ func choose(id: StringName) -> void:
await button(main.palette_toggle_button) await button(main.palette_toggle_button)
var node: Button = main.build_buttons[id] var node: Button = main.build_buttons[id]
await reveal(node) 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: func click(cell: Vector3i) -> Dictionary:
var view: WorldPreview = main.world_preview 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") expect(DirAccess.make_dir_recursive_absolute(directory) == OK, "diagnostic directory")
runtime = tree.root.get_node("SimulationRuntime") runtime = tree.root.get_node("SimulationRuntime")
clock = tree.root.get_node("GameClock") clock = tree.root.get_node("GameClock")
runtime.save_operation_completed.connect(record_save_operation)
await setup() await setup()
runtime.command_completed.connect(observe) runtime.command_completed.connect(observe)
main.world_preview.cell_clicked.connect(selected_cell) 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": if requested_role == "standalone":
role = "reader" role = "reader"
actions = [] actions = []
save_records = []
await reader(false) await reader(false)
json_file(role + "-actions.json", {"setup": "diagnostic", "input_method": input_method, "actions": actions}) json_file(role + "-actions.json", {"setup": "diagnostic", "input_method": input_method, "actions": actions})
finish_observations() finish_observations()
runtime.command_completed.disconnect(observe) runtime.command_completed.disconnect(observe)
runtime.save_operation_completed.disconnect(record_save_operation)
var cleanup: Dictionary = await Cleanup.free_owner_and_wait(tree, main) var cleanup: Dictionary = await Cleanup.free_owner_and_wait(tree, main)
expect(cleanup.get(&"passed", false), "ordinary Main/audio cleanup") 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"}) 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) clock.set_paused(true)
tree.root.size = Vector2i(1440, 810) tree.root.size = Vector2i(1440, 810)
main = load("res://scenes/main/main.tscn").instantiate() 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) tree.root.add_child(main)
runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false}) runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
func save_initial_fixture() -> void: func save_initial_fixture() -> void:
# Explicit diagnostic input, not an OS action or natural-play save. # 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") 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) json_file("diagnostic-budget.json", budget)
func archive_formal() -> void: 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: func finish_observations() -> void:
pass pass
@@ -316,6 +316,20 @@ def check(project_root, evidence_dir):
process = document(directory+'/'+role+'-process.json') process = document(directory+'/'+role+'-process.json')
require(process['pid'] == pid and process['failures'] == 0 and process['checks'] > 0, 'process attribution') 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') 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') doc = document(directory+'/'+role+'-actions.json')
require(doc['setup'] == 'diagnostic', 'unlabelled diagnostic') require(doc['setup'] == 'diagnostic', 'unlabelled diagnostic')
expected = WRITER if role == 'writer' else ['resume_overlap'] 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') require(set(saved['members']) == set(MEMBERS) and saved['pid'] == expected_pids[0], 'missing formal members/writer')
for member in MEMBERS: for member in MEMBERS:
require(saved['members'][member] == sha(artifact(directory+'/formal/'+member)), 'formal member changed') 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') 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') 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} by_id = {r['id']: r for r in rows}
+18 -4
View File
@@ -124,15 +124,27 @@ class EvidenceTests(unittest.TestCase):
def test_wrong_resume(self): def test_wrong_resume(self):
path = 'pair/reader-checkpoint.json' path = 'pair/reader-checkpoint.json'
original = (self.out/path).read_bytes()
model = C.decode(json.loads((self.out/path).read_text())) model = C.decode(json.loads((self.out/path).read_text()))
model['clock']['absolute_minute'] += 1 model['clock']['absolute_minute'] += 1
F.put(self.out/path, F.typed(model)); F.repin(self.out) F.put(self.out/path, F.typed(model)); F.repin(self.out)
self.reject('wrong resume model') 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): def test_missing_formal_member(self):
(self.out/'pair/formal/journal.log').unlink() for name in ('formal','formal.bak'):
F.repin(self.out) with self.subTest(package=name):
self.reject('journal.log') 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): def test_missing_model_member(self):
path = 'pair/writer-unsupported-before.json' 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, 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})) 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) 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: if fail_wait:
with self.assertRaisesRegex(AssertionError,'native OS collection failed'): 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) D.collect_os(r,self.root,output,stage,str(Path(self.temp.name)/'synthetic-engine-not-executable'),'1'*40,refs,protected,outer)