P28-02-EQ: 意味を保つ軽量化:同等性の先行固定(harness)
This commit is contained in:
+5630
File diff suppressed because it is too large
Load Diff
@@ -721,3 +721,69 @@ read-only directory and its copy has the recorded bytes and SHA). The engine (/A
|
||||
the machine model stay read live. Everything else verify_eq recomputes from the bundle and the
|
||||
checkout as before.
|
||||
|
||||
## S6 implementation (repair 12; EQ_S6_AFTER_VERSION_PLAN)
|
||||
|
||||
Implemented by Claude Opus 5.5 in the Desktop control session (user delegation). Repair for
|
||||
the independent review of S a46418d (changes_requested: EQ5). All earlier text is retained.
|
||||
|
||||
### The after version through the frozen harness (EQ5, high finding)
|
||||
|
||||
- `BEFORE_SOURCE_MAP.json`: the registered base product source map, 1407 rows of
|
||||
`{bytes, sha256}`, written from `collector.source_map` of the S_eq checkout. Its meaning is
|
||||
fixed by the existing `SOURCE_SHA`; `compare_versions.before_source_map()` rejects duplicate
|
||||
keys, row shape errors, a different row count and any digest other than `SOURCE_SHA`.
|
||||
- `compare_versions.admit_source(source, registered)` is the single admission rule of both
|
||||
versions. `registered` must be a sorted duplicate-free list. It runs
|
||||
`check_source_closure(before_source_map(), source, registered)` and returns the closure with
|
||||
`version` (before when `registered` is empty, otherwise after) and `after_sha256`. The before
|
||||
version is exactly the old `SOURCE_SHA` requirement; the after version needs the changed set
|
||||
equal to `registered` (a subset of `OPT_PATHS`), with no added or missing source.
|
||||
- `run_eq`: `--opt-changed PATH` (repeatable, sorted by main, duplicates rejected by the rule).
|
||||
`actual_pins(..., registered)` records `registered_changes` and `source_closure`; the
|
||||
admission records `version` and `measured_source` (`verify_eq.MEASURED`: before
|
||||
`S_eq-native-not-Q`, after `S_opt-native-not-Q`, OPT_CONTRACT_T1 §5); `pins_after` is
|
||||
recomputed with the same registration. The EQ file pins (`worker`) and `frozen_commit`
|
||||
include `BEFORE_SOURCE_MAP.json` (`verify_eq.DATA_NAMES`).
|
||||
- `verify_eq.recompute` replaces the `SOURCE_SHA` line with `admit_source` on the recorded
|
||||
map and registration and requires the recorded closure, version and label exactly;
|
||||
`verify` passes the recorded registration to `actual_pins` on the checkout. The result
|
||||
carries `measured_source`, `version` and `registered_changes`.
|
||||
- `verify_eq.py --compare-versions --before <bundle> --after <bundle>` (`compare_bundles`):
|
||||
the bundle-level entry of the two-version comparator (SPEC §2, §3.2; OPT_CONTRACT_T1 §2-§4).
|
||||
Both seals, passed executions, admission copies, versions, closures and labels are checked
|
||||
again; every non-product pin (`dependencies`, `frozen`, `worker`, `fixture`, `engine`,
|
||||
`engine_version`, `readonly`, `original_manifest`, `machine`, `platform`) must be equal; the
|
||||
sealed joined reports of R-A and O-A are compared by `compare_reports` (differences,
|
||||
exclusions, digest modes and violations all returned); the stored checkers' existing-defect
|
||||
scenarios of both versions are listed and `after_all_e_pass` reports whether the after
|
||||
version has none (the OPT acceptance judges it). Each bundle's raw recomputation, same-role
|
||||
determinism and checkout pins are proven by that version's own native-verify.
|
||||
|
||||
### Low findings
|
||||
|
||||
- `eq_probe.gd` E5 detail: `latest_digest_dirty_before` is `runtime.get("_latest_digest_dirty")`
|
||||
read just before the operation (null if a version has no such field), replacing the constant
|
||||
`digest_materialized_before: true`. The checker accepts only `false` or `null`.
|
||||
- `run_eq.bundled_inputs`: the bundled manifest must have `ROOT_MANIFEST_SHA` (the value
|
||||
`collector.inputs` fixes; a unit test checks the literal in the collector source), and each
|
||||
destination string must be absolute and already normalized (`os.path.normpath`) inside the
|
||||
read-only directory. This is textual because the sandboxed verification cannot read the
|
||||
originals; the run checks the resolved originals through `collector.inputs`.
|
||||
- `verify_eq.seal`/`check_seal`: bundle schema `p28-02-eq-bundle-v2` with `directories`, the
|
||||
sorted list of every directory (empty `<phase>/startup-profile` included). A file-only copy
|
||||
of a sealed bundle now fails the seal check itself.
|
||||
|
||||
### Tests and evidence kinds
|
||||
|
||||
- 17 engine-free tests in `S6SourceClosure`, `S6Admission`, `S6Bundles` and `S6SealAndProbe`.
|
||||
`S6Bundles` builds one sealed before and one sealed after synthetic bundle per class with
|
||||
the real parallel recompute, compares them unmocked, and tampers APFS clones (`cp -c`) only;
|
||||
the real state difference is a changed K value in the after R-A report. The main test in
|
||||
`S6Admission` mocks `actual_pins` only to read the registration passed by main.
|
||||
- Diagnostic, not formal: `EQ_S6_AFTER_ADMISSION_DIAG_R01` (Root control folder) ran the real
|
||||
`run_eq.py --check` on a throwaway after-version checkout; the admitted and rejected cases
|
||||
and their exits are in its SUMMARY.json.
|
||||
- The first S6 bundle tests copied 709 MB synthetic bundles with `shutil.copytree` and ran
|
||||
out of disk space during a class-targeted run (not retained); they now use clones.
|
||||
- Full local run UNIT_OUTPUT_S6_ATTEMPT_01: 179 tests OK in 207.351 s, exit 0.
|
||||
- Local run under the Runner validation seatbelt profile, UNIT_OUTPUT_S6_SEATBELT_01: 179 tests OK in 206.326 s, Python exit 0, outer 207.695 s.
|
||||
|
||||
@@ -1,4 +1,78 @@
|
||||
# P28-02-EQ progress — S5 (repair 10, Claude Opus 5.5 Desktop implementation)
|
||||
# P28-02-EQ progress — S6 (repair 12, Claude Opus 5.5 Desktop implementation)
|
||||
|
||||
Implementer: Claude Opus 5.5 in the Desktop control session, by the user's delegation
|
||||
("Codexの使用量上限いきそうなので、作業をひきとって"; Root relayed 「実装担当含めOpusに委任」).
|
||||
The registered contract keeps worker codex; no Codex/Astra run was started for this stage.
|
||||
Submission goes through the normal Runner submit, which makes the commit. 正式受入は保留、原本加算0。
|
||||
|
||||
- S a46418d064ebf8c8e03043bae50c29ac174b5472 (repair 11): its exact-S native passed
|
||||
(native-a46418d-r01, 7 phases, cleanup errors empty, sealed), and its formal validation
|
||||
cb059f305dd34e7aa811b40ad1cfb6f2 passed both checks (unit 162 tests OK; native-verify
|
||||
exit 0, verified). The fresh independent Opus review (session
|
||||
local_179020e9-e4c8-45ab-aaa0-be27aaa3f3d6, DESKTOP_RESULT.json
|
||||
c08eedb2ff81fd308e0771aeb4330076389561255a2bb964567be28f478592cd) returned
|
||||
changes_requested: EQ1-EQ4 and EQ6 met, EQ5 not met. The normal intake recorded
|
||||
review_failed and repair_pending; the normal repair made repairs 12. All of it is retained.
|
||||
- The high finding is accepted: the source closure was only unit-tested. run_eq and verify_eq
|
||||
required the before SOURCE_SHA, so the frozen EQ could not admit an OPT S that changes only
|
||||
registered allowed files (OPT_CONTRACT_T1 §2 and §4). S6 (EQ_S6_AFTER_VERSION_PLAN):
|
||||
- BEFORE_SOURCE_MAP.json holds the registered base product source map (1407 rows); its
|
||||
digest must equal the existing SOURCE_SHA on every read. compare_versions.admit_source
|
||||
admits the before version (no registration, no difference) or an after version (the
|
||||
changed set equals the sorted registered OPT files; no added or missing source).
|
||||
- run_eq --opt-changed PATH (repeatable) registers the OPT files; actual_pins records
|
||||
registered_changes and source_closure, the admission records version and
|
||||
measured_source (before S_eq-native-not-Q, after S_opt-native-not-Q; never Q).
|
||||
verify_eq re-runs the closure with the recorded registration and requires the recorded
|
||||
closure, version and label; actual_pins on the checkout gets the same registration.
|
||||
- verify_eq --compare-versions --before <bundle> --after <bundle> is the bundle-level entry
|
||||
of the comparator (SPEC §2): both seals, versions and closures again, every non-product
|
||||
pin equal, compare_reports on the sealed A-phase reports of R and O, existing defects of
|
||||
both versions listed (after_all_e_pass for the OPT judgment).
|
||||
- The three low findings are fixed: the probe records the actual _latest_digest_dirty before
|
||||
each E5 operation (false or null accepted, true rejected); bundled_inputs requires the
|
||||
collector's fixed manifest SHA and normalized absolute destinations inside the read-only
|
||||
directory; the seal (bundle schema v2) records every directory, empty ones included.
|
||||
- Evidence kinds are kept apart:
|
||||
- Unit, engine-free, real code paths: admit_source; run_eq.actual_pins with a synthetic map
|
||||
(only collector.source_map, inputs, host SHAs of the synthetic fixture/engine and the
|
||||
machine query are patched); recompute of a real synthetic after bundle; compare_bundles
|
||||
unmocked on a real before and a real after synthetic bundle (positive, a real state
|
||||
difference, pin drift, closure and label tampering, seal tampering, swapped, same bundle).
|
||||
One unit test (S6Admission main) mocks actual_pins only to see the registration main
|
||||
passes; it is not counted as admission evidence.
|
||||
- Diagnostic, not formal (EQ_S6_AFTER_ADMISSION_DIAG_R01 in the Root control folder): the
|
||||
real run_eq.py --check, unmocked, on a throwaway after-version checkout (an APFS clone of
|
||||
this working tree as its own temporary git repository, one allowed OPT file changed and
|
||||
committed). Before version and the registered after version were admitted; no
|
||||
registration, a wrong registration, a registration without change, an added source and a
|
||||
non-OPT change were rejected with the source closure error. No engine was started.
|
||||
- Unit evidence (engine-free `python3 -B test_eq.py`; full raw outputs with SHA files):
|
||||
- Class-targeted runs during development were displayed through tail and are not claimed
|
||||
as retained raw results. One of them ran out of disk space (the first S6 bundle tests
|
||||
copied 709 MB synthetic bundles); the tests now clone with cp -c and build two bundles
|
||||
per class.
|
||||
- UNIT_OUTPUT_S6_ATTEMPT_01 (final, all changes included): 179 tests OK in 207.351 s,
|
||||
Python exit 0; same bytes as UNIT_OUTPUT.txt,
|
||||
SHA-256 9dd93de5fd3b66791d6902d51a055caa2c3dd559de6c42019ebb1ab147b2af11.
|
||||
- UNIT_OUTPUT_S6_SEATBELT_01: the same suite under the Runner validation seatbelt profile,
|
||||
run locally (see its exit line in IMPLEMENTATION.md). Local runs are not formal validation.
|
||||
- Test bodies changed for S6, each recorded here: the E5 fixture detail carries
|
||||
latest_digest_dirty_before false instead of digest_materialized_before true, and the
|
||||
materialization test rejects true; s4_bundle_fixture takes an optional registration and
|
||||
pin overrides and records commit, version, measured_source, registered_changes,
|
||||
source_closure and the non-product pins; the S4 probe-admission mock returns a before
|
||||
source_closure; the stored-result test records registered_changes [] in its admission and
|
||||
mock; the bundled-inputs test holds its synthetic manifest to its own SHA in place of the
|
||||
collector's and expects the manifest error before the escape error. All other earlier test
|
||||
bodies and raw outputs are unchanged; 17 S6 tests were added.
|
||||
|
||||
Next: Runner submit → exact-S native 7 phases (before version, no --opt-changed) → bundle →
|
||||
p28-02-eq-unit and p28-02-eq-native-verify → fresh independent Opus review of all 6
|
||||
conditions → normal qualification, approval, integration and final → OPT/whole-unit.
|
||||
23/88、4/30は不変。
|
||||
|
||||
## Retained S5 progress (unchanged historical text)
|
||||
|
||||
Implementer: Claude Opus 5.5 in the Desktop control session, by the user's delegation
|
||||
("Codexの使用量上限いきそうなので、作業をひきとって"; Root relayed 「実装担当含めOpusに委任」).
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
846ab3b45705f269a81181c818dc5e3b8bc0057863794d59e8d641fe5a4e4c32 UNIT_OUTPUT.txt
|
||||
9dd93de5fd3b66791d6902d51a055caa2c3dd559de6c42019ebb1ab147b2af11 UNIT_OUTPUT.txt
|
||||
|
||||
@@ -149,6 +149,23 @@ test_probe_static_full_tree_blob_and_stream_requirements (__main__.S5.test_probe
|
||||
test_publish_pairs_bound_to_source_causes (__main__.S5.test_publish_pairs_bound_to_source_causes) ... ok
|
||||
test_signed_instance_ids_raw_kept_and_excluded (__main__.S5.test_signed_instance_ids_raw_kept_and_excluded) ... ok
|
||||
test_validate_tree_equals_s1_reference_on_mutations (__main__.S5.test_validate_tree_equals_s1_reference_on_mutations) ... ok
|
||||
test_bundled_destination_must_be_normalized_inside_readonly (__main__.S6Admission.test_bundled_destination_must_be_normalized_inside_readonly) ... ok
|
||||
test_check_path_admits_registered_after_version (__main__.S6Admission.test_check_path_admits_registered_after_version) ... ok
|
||||
test_check_path_rejects_unregistered_or_unchanged (__main__.S6Admission.test_check_path_rejects_unregistered_or_unchanged) ... ok
|
||||
test_main_passes_sorted_registration_and_records_version (__main__.S6Admission.test_main_passes_sorted_registration_and_records_version) ... ok
|
||||
test_root_manifest_pin_equals_collector_literal (__main__.S6Admission.test_root_manifest_pin_equals_collector_literal) ... ok
|
||||
test_after_version_bundle_recomputes (__main__.S6Bundles.test_after_version_bundle_recomputes) ... ok
|
||||
test_cross_version_comparison (__main__.S6Bundles.test_cross_version_comparison) ... ok
|
||||
test_cross_version_pin_closure_and_seal_rejections (__main__.S6Bundles.test_cross_version_pin_closure_and_seal_rejections) ... ok
|
||||
test_cross_version_real_difference_is_not_ok (__main__.S6Bundles.test_cross_version_real_difference_is_not_ok) ... ok
|
||||
test_recompute_rejects_version_closure_and_label_changes (__main__.S6Bundles.test_recompute_rejects_version_closure_and_label_changes) ... ok
|
||||
test_digest_state_values (__main__.S6SealAndProbe.test_digest_state_values) ... ok
|
||||
test_probe_records_actual_digest_state (__main__.S6SealAndProbe.test_probe_records_actual_digest_state) ... ok
|
||||
test_seal_records_every_directory (__main__.S6SealAndProbe.test_seal_records_every_directory) ... ok
|
||||
test_admit_before_and_registered_after (__main__.S6SourceClosure.test_admit_before_and_registered_after) ... ok
|
||||
test_before_map_is_the_frozen_source_map (__main__.S6SourceClosure.test_before_map_is_the_frozen_source_map) ... ok
|
||||
test_before_map_tampering_rejected (__main__.S6SourceClosure.test_before_map_tampering_rejected) ... ok
|
||||
test_unregistered_added_missing_and_malformed_registrations_rejected (__main__.S6SourceClosure.test_unregistered_added_missing_and_malformed_registrations_rejected) ... ok
|
||||
test_fixed_scenario_paths (__main__.SourcesAndMetrics.test_fixed_scenario_paths) ... ok
|
||||
test_metric_ranges (__main__.SourcesAndMetrics.test_metric_ranges) ... ok
|
||||
test_source_closure_synthetic_registered_map (__main__.SourcesAndMetrics.test_source_closure_synthetic_registered_map) ... ok
|
||||
@@ -163,6 +180,6 @@ test_nonstring_keys_keep_types_and_values (__main__.TypedTrees.test_nonstring_ke
|
||||
test_unsupported_and_malformed (__main__.TypedTrees.test_unsupported_and_malformed) ... ok
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Ran 162 tests in 120.853s
|
||||
Ran 179 tests in 207.351s
|
||||
|
||||
OK
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
9dd93de5fd3b66791d6902d51a055caa2c3dd559de6c42019ebb1ab147b2af11 UNIT_OUTPUT_S6_ATTEMPT_01.txt
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
test_all_operations (__main__.Diffs.test_all_operations) ... ok
|
||||
test_invalid_operations (__main__.Diffs.test_invalid_operations) ... ok
|
||||
test_reconstruction_and_proofs (__main__.Diffs.test_reconstruction_and_proofs) ... ok
|
||||
test_attempt_to_normalize_violation_rejected (__main__.Normalization.test_attempt_to_normalize_violation_rejected) ... ok
|
||||
test_environment_values_differ_but_normal_form_equal (__main__.Normalization.test_environment_values_differ_but_normal_form_equal) ... ok
|
||||
test_every_save_event_reversible (__main__.Normalization.test_every_save_event_reversible) ... ok
|
||||
test_f3_keeps_relative_paths_and_words (__main__.Normalization.test_f3_keeps_relative_paths_and_words) ... ok
|
||||
test_guard_mismatch (__main__.Normalization.test_guard_mismatch) ... ok
|
||||
test_nested_lookalike_is_not_eligible (__main__.Normalization.test_nested_lookalike_is_not_eligible) ... ok
|
||||
test_original_transport_sha_detects_forged_timing (__main__.Normalization.test_original_transport_sha_detects_forged_timing) ... ok
|
||||
test_partial_timing_payload_rejected (__main__.Normalization.test_partial_timing_payload_rejected) ... ok
|
||||
test_prefix_boundary_and_reserved_token (__main__.Normalization.test_prefix_boundary_and_reserved_token) ... ok
|
||||
test_sidecar_missing_duplicate_and_forged_paths (__main__.Normalization.test_sidecar_missing_duplicate_and_forged_paths) ... ok
|
||||
test_snapshot_and_latest_reversible (__main__.Normalization.test_snapshot_and_latest_reversible) ... ok
|
||||
test_snapshot_event_outside_recent_is_not_eligible (__main__.Normalization.test_snapshot_event_outside_recent_is_not_eligible) ... ok
|
||||
test_wrong_event_and_source_are_preserved (__main__.Normalization.test_wrong_event_and_source_are_preserved) ... ok
|
||||
test_wrong_timing_type_and_negative (__main__.Normalization.test_wrong_timing_type_and_negative) ... ok
|
||||
test_all_events_beyond_recent_window (__main__.Reports.test_all_events_beyond_recent_window) ... ok
|
||||
test_all_semantic_layers_compared (__main__.Reports.test_all_semantic_layers_compared) ... ok
|
||||
test_digest_alone_is_not_enough (__main__.Reports.test_digest_alone_is_not_enough) ... ok
|
||||
test_event_original_types_and_order (__main__.Reports.test_event_original_types_and_order) ... ok
|
||||
test_event_start_and_ids (__main__.Reports.test_event_start_and_ids) ... ok
|
||||
test_exclusions_keep_actual_values (__main__.Reports.test_exclusions_keep_actual_values) ... ok
|
||||
test_handler_sets_and_counts (__main__.Reports.test_handler_sets_and_counts) ... ok
|
||||
test_observation_full_layers (__main__.Reports.test_observation_full_layers) ... ok
|
||||
test_product_digest_self_consistency (__main__.Reports.test_product_digest_self_consistency) ... ok
|
||||
test_same_report (__main__.Reports.test_same_report) ... ok
|
||||
test_scenario_existing_failures_preserved (__main__.Reports.test_scenario_existing_failures_preserved) ... ok
|
||||
test_timing_digest_rule (__main__.Reports.test_timing_digest_rule) ... ok
|
||||
test_unknown_or_missing_fields_rejected (__main__.Reports.test_unknown_or_missing_fields_rejected) ... ok
|
||||
test_violation_presence_and_difference (__main__.Reports.test_violation_presence_and_difference) ... ok
|
||||
test_O_cadence (__main__.S2Format.test_O_cadence) ... ok
|
||||
test_counter_and_published_ids_binding (__main__.S2Format.test_counter_and_published_ids_binding) ... ok
|
||||
test_diff_and_same_as_S_transport (__main__.S2Format.test_diff_and_same_as_S_transport) ... ok
|
||||
test_digest_and_scenario_contradiction (__main__.S2Format.test_digest_and_scenario_contradiction) ... ok
|
||||
test_event_no_drop_duplicate_remap (__main__.S2Format.test_event_no_drop_duplicate_remap) ... ok
|
||||
test_event_original_field_types_mandatory (__main__.S2Format.test_event_original_field_types_mandatory) ... ok
|
||||
test_guard_actual_not_assumed (__main__.S2Format.test_guard_actual_not_assumed) ... ok
|
||||
test_harness_digest_allows_only_dictionary_order (__main__.S2Format.test_harness_digest_allows_only_dictionary_order) ... ok
|
||||
test_join_is_copy_and_retains_normalization_proof (__main__.S2Format.test_join_is_copy_and_retains_normalization_proof) ... ok
|
||||
test_kernel_cache_contradictions (__main__.S2Format.test_kernel_cache_contradictions) ... ok
|
||||
test_lifecycle_guard_errors_and_boundaries (__main__.S2Format.test_lifecycle_guard_errors_and_boundaries) ... ok
|
||||
test_missing_full_state_not_digest_only (__main__.S2Format.test_missing_full_state_not_digest_only) ... ok
|
||||
test_normalized_digest_and_raw_tree_are_both_bound (__main__.S2Format.test_normalized_digest_and_raw_tree_are_both_bound) ... ok
|
||||
test_product_event_shape_and_typed_array (__main__.S2Format.test_product_event_shape_and_typed_array) ... ok
|
||||
test_raw_observation_binding (__main__.S2Format.test_raw_observation_binding) ... ok
|
||||
test_roundtrip_tree_sha_and_raw_sha (__main__.S2Format.test_roundtrip_tree_sha_and_raw_sha) ... ok
|
||||
test_s2_valid_and_not_formal_acceptance (__main__.S2Format.test_s2_valid_and_not_formal_acceptance) ... ok
|
||||
test_state_witness_and_observation_mutation (__main__.S2Format.test_state_witness_and_observation_mutation) ... ok
|
||||
test_stdout_exactly_one_line (__main__.S2Format.test_stdout_exactly_one_line) ... ok
|
||||
test_autoload_class_and_script_import (__main__.S2Static.test_autoload_class_and_script_import) ... ok
|
||||
test_comment_does_not_supply_proof (__main__.S2Static.test_comment_does_not_supply_proof) ... ok
|
||||
test_current_probe (__main__.S2Static.test_current_probe) ... ok
|
||||
test_full_state_and_godot_roundtrip_required (__main__.S2Static.test_full_state_and_godot_roundtrip_required) ... ok
|
||||
test_no_changes_outside_preparation_or_step (__main__.S2Static.test_no_changes_outside_preparation_or_step) ... ok
|
||||
test_no_event_cap_or_duplicate_report (__main__.S2Static.test_no_event_cap_or_duplicate_report) ... ok
|
||||
test_pause_and_step_arguments (__main__.S2Static.test_pause_and_step_arguments) ... ok
|
||||
test_textual_stringname_ordering (__main__.S2Static.test_textual_stringname_ordering) ... ok
|
||||
test_unlisted_live_method_and_late_preparation (__main__.S2Static.test_unlisted_live_method_and_late_preparation) ... ok
|
||||
test_variant_inference (__main__.S2Static.test_variant_inference) ... ok
|
||||
test_X_recent_limit_and_counter (__main__.S3a.test_X_recent_limit_and_counter) ... ok
|
||||
test_duplicate_JSON_key_rejected (__main__.S3a.test_duplicate_JSON_key_rejected) ... ok
|
||||
test_e3_NA_cannot_be_PASS_or_applied (__main__.S3a.test_e3_NA_cannot_be_PASS_or_applied) ... ok
|
||||
test_e3_coverage_input_and_event_count_binding (__main__.S3a.test_e3_coverage_input_and_event_count_binding) ... ok
|
||||
test_e3_invented_codes_and_missing_load_code (__main__.S3a.test_e3_invented_codes_and_missing_load_code) ... ok
|
||||
test_e3_valid_R_and_O_records (__main__.S3a.test_e3_valid_R_and_O_records) ... ok
|
||||
test_envelope_duplicates_not_defect (__main__.S3a.test_envelope_duplicates_not_defect) ... ok
|
||||
test_event_count_defect_not_silently_accepted (__main__.S3a.test_event_count_defect_not_silently_accepted) ... ok
|
||||
test_exact_phase_keys (__main__.S3a.test_exact_phase_keys) ... ok
|
||||
test_interval_gap_rejected (__main__.S3a.test_interval_gap_rejected) ... ok
|
||||
test_invalid_input_accepted_is_not_PASS (__main__.S3a.test_invalid_input_accepted_is_not_PASS) ... ok
|
||||
test_load_payload_and_pause_outcome (__main__.S3a.test_load_payload_and_pause_outcome) ... ok
|
||||
test_operation_moved_outside_scenario_rejected (__main__.S3a.test_operation_moved_outside_scenario_rejected) ... ok
|
||||
test_package_fixed_mutations_and_hashes (__main__.S3a.test_package_fixed_mutations_and_hashes) ... ok
|
||||
test_probe_connection_required (__main__.S3a.test_probe_connection_required) ... ok
|
||||
test_rejection_mutation_retained_as_defect (__main__.S3a.test_rejection_mutation_retained_as_defect) ... ok
|
||||
test_save_refusal_makes_load_NA (__main__.S3a.test_save_refusal_makes_load_NA) ... ok
|
||||
test_scenario_mutation_static_boundary (__main__.S3a.test_scenario_mutation_static_boundary) ... ok
|
||||
test_signals_all_minutes_and_order (__main__.S3a.test_signals_all_minutes_and_order) ... ok
|
||||
test_E5_cached_digest_defect_is_comparable (__main__.S3b.test_E5_cached_digest_defect_is_comparable) ... ok
|
||||
test_NA_not_PASS (__main__.S3b.test_NA_not_PASS) ... ok
|
||||
test_aliasing_cannot_be_PASS (__main__.S3b.test_aliasing_cannot_be_PASS) ... ok
|
||||
test_aliasing_missing_record (__main__.S3b.test_aliasing_missing_record) ... ok
|
||||
test_all_paths_and_ownership_R_O (__main__.S3b.test_all_paths_and_ownership_R_O) ... ok
|
||||
test_digest_defect_other_checkpoint_still_rejected (__main__.S3b.test_digest_defect_other_checkpoint_still_rejected) ... ok
|
||||
test_digest_materialization_required (__main__.S3b.test_digest_materialization_required) ... ok
|
||||
test_empty_registry_not_NA (__main__.S3b.test_empty_registry_not_NA) ... ok
|
||||
test_event_count_mismatch (__main__.S3b.test_event_count_mismatch) ... ok
|
||||
test_facts_normalization_is_reversible (__main__.S3b.test_facts_normalization_is_reversible) ... ok
|
||||
test_first_placeable_choice (__main__.S3b.test_first_placeable_choice) ... ok
|
||||
test_interval_gap (__main__.S3b.test_interval_gap) ... ok
|
||||
test_joined_s3b_self_comparison (__main__.S3b.test_joined_s3b_self_comparison) ... ok
|
||||
test_load_X_not_digest_only (__main__.S3b.test_load_X_not_digest_only) ... ok
|
||||
test_load_extra_event (__main__.S3b.test_load_extra_event) ... ok
|
||||
test_missing_path_and_combination (__main__.S3b.test_missing_path_and_combination) ... ok
|
||||
test_missing_preview_observation (__main__.S3b.test_missing_preview_observation) ... ok
|
||||
test_missing_trial (__main__.S3b.test_missing_trial) ... ok
|
||||
test_nested_mutation_required (__main__.S3b.test_nested_mutation_required) ... ok
|
||||
test_no_staleness_assertion_for_non_tick (__main__.S3b.test_no_staleness_assertion_for_non_tick) ... ok
|
||||
test_ownership_fact_dictionary_order_fallback (__main__.S3b.test_ownership_fact_dictionary_order_fallback) ... ok
|
||||
test_path_order (__main__.S3b.test_path_order) ... ok
|
||||
test_restore_extra_event (__main__.S3b.test_restore_extra_event) ... ok
|
||||
test_signal_facts_cannot_be_dropped (__main__.S3b.test_signal_facts_cannot_be_dropped) ... ok
|
||||
test_sort_rule_rejected (__main__.S3b.test_sort_rule_rejected) ... ok
|
||||
test_static_handler_and_outside_mutations (__main__.S3b.test_static_handler_and_outside_mutations) ... ok
|
||||
test_unpreviewed_choice (__main__.S3b.test_unpreviewed_choice) ... ok
|
||||
test_wrong_registry_in_either_role (__main__.S3b.test_wrong_registry_in_either_role) ... ok
|
||||
test_checkout_temp_destination_rejected (__main__.S4Bundle.test_checkout_temp_destination_rejected) ... ok
|
||||
test_existing_output_never_overwritten (__main__.S4Bundle.test_existing_output_never_overwritten) ... ok
|
||||
test_import_closure_rejections (__main__.S4Bundle.test_import_closure_rejections) ... ok
|
||||
test_missing_bundle_manifest_seal_and_changed_bytes (__main__.S4Bundle.test_missing_bundle_manifest_seal_and_changed_bytes) ... ok
|
||||
test_missing_private_evidence_and_extra_rejected (__main__.S4Bundle.test_missing_private_evidence_and_extra_rejected) ... ok
|
||||
test_missing_profile_not_export_success (__main__.S4Bundle.test_missing_profile_not_export_success) ... ok
|
||||
test_phase_role_pid_audio_and_guard_rejected (__main__.S4Bundle.test_phase_role_pid_audio_and_guard_rejected) ... ok
|
||||
test_rebound_lifecycle_and_scenario_contradictions (__main__.S4Bundle.test_rebound_lifecycle_and_scenario_contradictions) ... ok
|
||||
test_seal_normal_and_changes_not_native_proof (__main__.S4Bundle.test_seal_normal_and_changes_not_native_proof) ... ok
|
||||
test_synthetic_phase_all_real_checkers_not_native_proof (__main__.S4Bundle.test_synthetic_phase_all_real_checkers_not_native_proof) ... ok
|
||||
test_copied_manifest_root_and_destination_count_rejected (__main__.S4NativeBundleLogic.test_copied_manifest_root_and_destination_count_rejected) ... ok
|
||||
test_failure_keeps_raw_and_no_bundle_without_starting_engine (__main__.S4NativeBundleLogic.test_failure_keeps_raw_and_no_bundle_without_starting_engine) ... ok
|
||||
test_gate_none_success_not_boolean (__main__.S4NativeBundleLogic.test_gate_none_success_not_boolean) ... ok
|
||||
test_receipt_and_source_rejection_matrix (__main__.S4NativeBundleLogic.test_receipt_and_source_rejection_matrix) ... ok
|
||||
test_synthetic_full_bundle_positive_not_native_evidence (__main__.S4NativeBundleLogic.test_synthetic_full_bundle_positive_not_native_evidence) ... ok
|
||||
test_check_accepts_true (__main__.S4ProbeAdmission.test_check_accepts_true) ... ok
|
||||
test_check_rejects_false_all_errors (__main__.S4ProbeAdmission.test_check_rejects_false_all_errors) ... ok
|
||||
test_run_admission_accepts_true_mock_launch_only (__main__.S4ProbeAdmission.test_run_admission_accepts_true_mock_launch_only) ... ok
|
||||
test_run_admission_rejects_false_without_engine (__main__.S4ProbeAdmission.test_run_admission_rejects_false_without_engine) ... ok
|
||||
test_nine_original_destinations_required (__main__.S4RemainingBindings.test_nine_original_destinations_required) ... ok
|
||||
test_nonzero_determinism_rejected_after_real_join (__main__.S4RemainingBindings.test_nonzero_determinism_rejected_after_real_join) ... ok
|
||||
test_owned_lifecycle_exit_diagnostic_and_profile_rejections (__main__.S4RemainingBindings.test_owned_lifecycle_exit_diagnostic_and_profile_rejections) ... ok
|
||||
test_verifier_accepts_true_recomputes_raw (__main__.S4RemainingBindings.test_verifier_accepts_true_recomputes_raw) ... ok
|
||||
test_verifier_rejects_false_with_every_error (__main__.S4RemainingBindings.test_verifier_rejects_false_with_every_error) ... ok
|
||||
test_cached_verdict_not_trusted (__main__.S4StoredResultBindings.test_cached_verdict_not_trusted) ... ok
|
||||
test_missing_sidecar_rejected_after_seal (__main__.S4StoredResultBindings.test_missing_sidecar_rejected_after_seal) ... ok
|
||||
test_raw_joined_substitution_rejected_after_seal (__main__.S4StoredResultBindings.test_raw_joined_substitution_rejected_after_seal) ... ok
|
||||
test_blob_transport_is_verified_and_bound (__main__.S5.test_blob_transport_is_verified_and_bound) ... ok
|
||||
test_blob_trees_get_the_same_full_validation_as_inline (__main__.S5.test_blob_trees_get_the_same_full_validation_as_inline) ... ok
|
||||
test_bundled_inputs_replace_unreadable_originals (__main__.S5.test_bundled_inputs_replace_unreadable_originals) ... ok
|
||||
test_compare_and_digest_equal_s1_reference (__main__.S5.test_compare_and_digest_equal_s1_reference) ... ok
|
||||
test_event_stream_replacement_is_existing_defect_not_pass (__main__.S5.test_event_stream_replacement_is_existing_defect_not_pass) ... ok
|
||||
test_every_checkpoint_full_tree_verified_like_inline (__main__.S5.test_every_checkpoint_full_tree_verified_like_inline)
|
||||
S5 drops the diff chain: each checkpoint carries a complete tree, verified alone. ... ok
|
||||
test_fast_normalization_equals_original (__main__.S5.test_fast_normalization_equals_original) ... ok
|
||||
test_labelled_harness_digest_falls_back_to_dictionary_order_only (__main__.S5.test_labelled_harness_digest_falls_back_to_dictionary_order_only) ... ok
|
||||
test_lifecycle_binds_replacements_guard_text_and_blobs (__main__.S5.test_lifecycle_binds_replacements_guard_text_and_blobs) ... ok
|
||||
test_manifest_numbers_keep_their_type (__main__.S5.test_manifest_numbers_keep_their_type) ... ok
|
||||
test_memo_scope_same_results_and_restores_gc (__main__.S5.test_memo_scope_same_results_and_restores_gc) ... ok
|
||||
test_path_copy_builders_equal_deepcopy_reference (__main__.S5.test_path_copy_builders_equal_deepcopy_reference) ... ok
|
||||
test_probe_static_full_tree_blob_and_stream_requirements (__main__.S5.test_probe_static_full_tree_blob_and_stream_requirements) ... ok
|
||||
test_publish_pairs_bound_to_source_causes (__main__.S5.test_publish_pairs_bound_to_source_causes) ... ok
|
||||
test_signed_instance_ids_raw_kept_and_excluded (__main__.S5.test_signed_instance_ids_raw_kept_and_excluded) ... ok
|
||||
test_validate_tree_equals_s1_reference_on_mutations (__main__.S5.test_validate_tree_equals_s1_reference_on_mutations) ... ok
|
||||
test_bundled_destination_must_be_normalized_inside_readonly (__main__.S6Admission.test_bundled_destination_must_be_normalized_inside_readonly) ... ok
|
||||
test_check_path_admits_registered_after_version (__main__.S6Admission.test_check_path_admits_registered_after_version) ... ok
|
||||
test_check_path_rejects_unregistered_or_unchanged (__main__.S6Admission.test_check_path_rejects_unregistered_or_unchanged) ... ok
|
||||
test_main_passes_sorted_registration_and_records_version (__main__.S6Admission.test_main_passes_sorted_registration_and_records_version) ... ok
|
||||
test_root_manifest_pin_equals_collector_literal (__main__.S6Admission.test_root_manifest_pin_equals_collector_literal) ... ok
|
||||
test_after_version_bundle_recomputes (__main__.S6Bundles.test_after_version_bundle_recomputes) ... ok
|
||||
test_cross_version_comparison (__main__.S6Bundles.test_cross_version_comparison) ... ok
|
||||
test_cross_version_pin_closure_and_seal_rejections (__main__.S6Bundles.test_cross_version_pin_closure_and_seal_rejections) ... ok
|
||||
test_cross_version_real_difference_is_not_ok (__main__.S6Bundles.test_cross_version_real_difference_is_not_ok) ... ok
|
||||
test_recompute_rejects_version_closure_and_label_changes (__main__.S6Bundles.test_recompute_rejects_version_closure_and_label_changes) ... ok
|
||||
test_digest_state_values (__main__.S6SealAndProbe.test_digest_state_values) ... ok
|
||||
test_probe_records_actual_digest_state (__main__.S6SealAndProbe.test_probe_records_actual_digest_state) ... ok
|
||||
test_seal_records_every_directory (__main__.S6SealAndProbe.test_seal_records_every_directory) ... ok
|
||||
test_admit_before_and_registered_after (__main__.S6SourceClosure.test_admit_before_and_registered_after) ... ok
|
||||
test_before_map_is_the_frozen_source_map (__main__.S6SourceClosure.test_before_map_is_the_frozen_source_map) ... ok
|
||||
test_before_map_tampering_rejected (__main__.S6SourceClosure.test_before_map_tampering_rejected) ... ok
|
||||
test_unregistered_added_missing_and_malformed_registrations_rejected (__main__.S6SourceClosure.test_unregistered_added_missing_and_malformed_registrations_rejected) ... ok
|
||||
test_fixed_scenario_paths (__main__.SourcesAndMetrics.test_fixed_scenario_paths) ... ok
|
||||
test_metric_ranges (__main__.SourcesAndMetrics.test_metric_ranges) ... ok
|
||||
test_source_closure_synthetic_registered_map (__main__.SourcesAndMetrics.test_source_closure_synthetic_registered_map) ... ok
|
||||
test_source_pin_is_not_caller_controlled (__main__.SourcesAndMetrics.test_source_pin_is_not_caller_controlled) ... ok
|
||||
test_array_order_and_all_differences (__main__.TypedTrees.test_array_order_and_all_differences) ... ok
|
||||
test_components_and_sha (__main__.TypedTrees.test_components_and_sha) ... ok
|
||||
test_deep_state_not_truncated (__main__.TypedTrees.test_deep_state_not_truncated) ... ok
|
||||
test_duplicate_textual_keys_rejected (__main__.TypedTrees.test_duplicate_textual_keys_rejected) ... ok
|
||||
test_exact_and_types (__main__.TypedTrees.test_exact_and_types) ... ok
|
||||
test_nested_order_all_paths (__main__.TypedTrees.test_nested_order_all_paths) ... ok
|
||||
test_nonstring_keys_keep_types_and_values (__main__.TypedTrees.test_nonstring_keys_keep_types_and_values) ... ok
|
||||
test_unsupported_and_malformed (__main__.TypedTrees.test_unsupported_and_malformed) ... ok
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Ran 179 tests in 207.351s
|
||||
|
||||
OK
|
||||
+1
@@ -0,0 +1 @@
|
||||
be2dd93c3cbb44b3ac74ab003f11ba368da3f4009b3b5595336f591dff52cceb UNIT_OUTPUT_S6_SEATBELT_01.txt
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
test_all_operations (__main__.Diffs.test_all_operations) ... ok
|
||||
test_invalid_operations (__main__.Diffs.test_invalid_operations) ... ok
|
||||
test_reconstruction_and_proofs (__main__.Diffs.test_reconstruction_and_proofs) ... ok
|
||||
test_attempt_to_normalize_violation_rejected (__main__.Normalization.test_attempt_to_normalize_violation_rejected) ... ok
|
||||
test_environment_values_differ_but_normal_form_equal (__main__.Normalization.test_environment_values_differ_but_normal_form_equal) ... ok
|
||||
test_every_save_event_reversible (__main__.Normalization.test_every_save_event_reversible) ... ok
|
||||
test_f3_keeps_relative_paths_and_words (__main__.Normalization.test_f3_keeps_relative_paths_and_words) ... ok
|
||||
test_guard_mismatch (__main__.Normalization.test_guard_mismatch) ... ok
|
||||
test_nested_lookalike_is_not_eligible (__main__.Normalization.test_nested_lookalike_is_not_eligible) ... ok
|
||||
test_original_transport_sha_detects_forged_timing (__main__.Normalization.test_original_transport_sha_detects_forged_timing) ... ok
|
||||
test_partial_timing_payload_rejected (__main__.Normalization.test_partial_timing_payload_rejected) ... ok
|
||||
test_prefix_boundary_and_reserved_token (__main__.Normalization.test_prefix_boundary_and_reserved_token) ... ok
|
||||
test_sidecar_missing_duplicate_and_forged_paths (__main__.Normalization.test_sidecar_missing_duplicate_and_forged_paths) ... ok
|
||||
test_snapshot_and_latest_reversible (__main__.Normalization.test_snapshot_and_latest_reversible) ... ok
|
||||
test_snapshot_event_outside_recent_is_not_eligible (__main__.Normalization.test_snapshot_event_outside_recent_is_not_eligible) ... ok
|
||||
test_wrong_event_and_source_are_preserved (__main__.Normalization.test_wrong_event_and_source_are_preserved) ... ok
|
||||
test_wrong_timing_type_and_negative (__main__.Normalization.test_wrong_timing_type_and_negative) ... ok
|
||||
test_all_events_beyond_recent_window (__main__.Reports.test_all_events_beyond_recent_window) ... ok
|
||||
test_all_semantic_layers_compared (__main__.Reports.test_all_semantic_layers_compared) ... ok
|
||||
test_digest_alone_is_not_enough (__main__.Reports.test_digest_alone_is_not_enough) ... ok
|
||||
test_event_original_types_and_order (__main__.Reports.test_event_original_types_and_order) ... ok
|
||||
test_event_start_and_ids (__main__.Reports.test_event_start_and_ids) ... ok
|
||||
test_exclusions_keep_actual_values (__main__.Reports.test_exclusions_keep_actual_values) ... ok
|
||||
test_handler_sets_and_counts (__main__.Reports.test_handler_sets_and_counts) ... ok
|
||||
test_observation_full_layers (__main__.Reports.test_observation_full_layers) ... ok
|
||||
test_product_digest_self_consistency (__main__.Reports.test_product_digest_self_consistency) ... ok
|
||||
test_same_report (__main__.Reports.test_same_report) ... ok
|
||||
test_scenario_existing_failures_preserved (__main__.Reports.test_scenario_existing_failures_preserved) ... ok
|
||||
test_timing_digest_rule (__main__.Reports.test_timing_digest_rule) ... ok
|
||||
test_unknown_or_missing_fields_rejected (__main__.Reports.test_unknown_or_missing_fields_rejected) ... ok
|
||||
test_violation_presence_and_difference (__main__.Reports.test_violation_presence_and_difference) ... ok
|
||||
test_O_cadence (__main__.S2Format.test_O_cadence) ... ok
|
||||
test_counter_and_published_ids_binding (__main__.S2Format.test_counter_and_published_ids_binding) ... ok
|
||||
test_diff_and_same_as_S_transport (__main__.S2Format.test_diff_and_same_as_S_transport) ... ok
|
||||
test_digest_and_scenario_contradiction (__main__.S2Format.test_digest_and_scenario_contradiction) ... ok
|
||||
test_event_no_drop_duplicate_remap (__main__.S2Format.test_event_no_drop_duplicate_remap) ... ok
|
||||
test_event_original_field_types_mandatory (__main__.S2Format.test_event_original_field_types_mandatory) ... ok
|
||||
test_guard_actual_not_assumed (__main__.S2Format.test_guard_actual_not_assumed) ... ok
|
||||
test_harness_digest_allows_only_dictionary_order (__main__.S2Format.test_harness_digest_allows_only_dictionary_order) ... ok
|
||||
test_join_is_copy_and_retains_normalization_proof (__main__.S2Format.test_join_is_copy_and_retains_normalization_proof) ... ok
|
||||
test_kernel_cache_contradictions (__main__.S2Format.test_kernel_cache_contradictions) ... ok
|
||||
test_lifecycle_guard_errors_and_boundaries (__main__.S2Format.test_lifecycle_guard_errors_and_boundaries) ... ok
|
||||
test_missing_full_state_not_digest_only (__main__.S2Format.test_missing_full_state_not_digest_only) ... ok
|
||||
test_normalized_digest_and_raw_tree_are_both_bound (__main__.S2Format.test_normalized_digest_and_raw_tree_are_both_bound) ... ok
|
||||
test_product_event_shape_and_typed_array (__main__.S2Format.test_product_event_shape_and_typed_array) ... ok
|
||||
test_raw_observation_binding (__main__.S2Format.test_raw_observation_binding) ... ok
|
||||
test_roundtrip_tree_sha_and_raw_sha (__main__.S2Format.test_roundtrip_tree_sha_and_raw_sha) ... ok
|
||||
test_s2_valid_and_not_formal_acceptance (__main__.S2Format.test_s2_valid_and_not_formal_acceptance) ... ok
|
||||
test_state_witness_and_observation_mutation (__main__.S2Format.test_state_witness_and_observation_mutation) ... ok
|
||||
test_stdout_exactly_one_line (__main__.S2Format.test_stdout_exactly_one_line) ... ok
|
||||
test_autoload_class_and_script_import (__main__.S2Static.test_autoload_class_and_script_import) ... ok
|
||||
test_comment_does_not_supply_proof (__main__.S2Static.test_comment_does_not_supply_proof) ... ok
|
||||
test_current_probe (__main__.S2Static.test_current_probe) ... ok
|
||||
test_full_state_and_godot_roundtrip_required (__main__.S2Static.test_full_state_and_godot_roundtrip_required) ... ok
|
||||
test_no_changes_outside_preparation_or_step (__main__.S2Static.test_no_changes_outside_preparation_or_step) ... ok
|
||||
test_no_event_cap_or_duplicate_report (__main__.S2Static.test_no_event_cap_or_duplicate_report) ... ok
|
||||
test_pause_and_step_arguments (__main__.S2Static.test_pause_and_step_arguments) ... ok
|
||||
test_textual_stringname_ordering (__main__.S2Static.test_textual_stringname_ordering) ... ok
|
||||
test_unlisted_live_method_and_late_preparation (__main__.S2Static.test_unlisted_live_method_and_late_preparation) ... ok
|
||||
test_variant_inference (__main__.S2Static.test_variant_inference) ... ok
|
||||
test_X_recent_limit_and_counter (__main__.S3a.test_X_recent_limit_and_counter) ... ok
|
||||
test_duplicate_JSON_key_rejected (__main__.S3a.test_duplicate_JSON_key_rejected) ... ok
|
||||
test_e3_NA_cannot_be_PASS_or_applied (__main__.S3a.test_e3_NA_cannot_be_PASS_or_applied) ... ok
|
||||
test_e3_coverage_input_and_event_count_binding (__main__.S3a.test_e3_coverage_input_and_event_count_binding) ... ok
|
||||
test_e3_invented_codes_and_missing_load_code (__main__.S3a.test_e3_invented_codes_and_missing_load_code) ... ok
|
||||
test_e3_valid_R_and_O_records (__main__.S3a.test_e3_valid_R_and_O_records) ... ok
|
||||
test_envelope_duplicates_not_defect (__main__.S3a.test_envelope_duplicates_not_defect) ... ok
|
||||
test_event_count_defect_not_silently_accepted (__main__.S3a.test_event_count_defect_not_silently_accepted) ... ok
|
||||
test_exact_phase_keys (__main__.S3a.test_exact_phase_keys) ... ok
|
||||
test_interval_gap_rejected (__main__.S3a.test_interval_gap_rejected) ... ok
|
||||
test_invalid_input_accepted_is_not_PASS (__main__.S3a.test_invalid_input_accepted_is_not_PASS) ... ok
|
||||
test_load_payload_and_pause_outcome (__main__.S3a.test_load_payload_and_pause_outcome) ... ok
|
||||
test_operation_moved_outside_scenario_rejected (__main__.S3a.test_operation_moved_outside_scenario_rejected) ... ok
|
||||
test_package_fixed_mutations_and_hashes (__main__.S3a.test_package_fixed_mutations_and_hashes) ... ok
|
||||
test_probe_connection_required (__main__.S3a.test_probe_connection_required) ... ok
|
||||
test_rejection_mutation_retained_as_defect (__main__.S3a.test_rejection_mutation_retained_as_defect) ... ok
|
||||
test_save_refusal_makes_load_NA (__main__.S3a.test_save_refusal_makes_load_NA) ... ok
|
||||
test_scenario_mutation_static_boundary (__main__.S3a.test_scenario_mutation_static_boundary) ... ok
|
||||
test_signals_all_minutes_and_order (__main__.S3a.test_signals_all_minutes_and_order) ... ok
|
||||
test_E5_cached_digest_defect_is_comparable (__main__.S3b.test_E5_cached_digest_defect_is_comparable) ... ok
|
||||
test_NA_not_PASS (__main__.S3b.test_NA_not_PASS) ... ok
|
||||
test_aliasing_cannot_be_PASS (__main__.S3b.test_aliasing_cannot_be_PASS) ... ok
|
||||
test_aliasing_missing_record (__main__.S3b.test_aliasing_missing_record) ... ok
|
||||
test_all_paths_and_ownership_R_O (__main__.S3b.test_all_paths_and_ownership_R_O) ... ok
|
||||
test_digest_defect_other_checkpoint_still_rejected (__main__.S3b.test_digest_defect_other_checkpoint_still_rejected) ... ok
|
||||
test_digest_materialization_required (__main__.S3b.test_digest_materialization_required) ... ok
|
||||
test_empty_registry_not_NA (__main__.S3b.test_empty_registry_not_NA) ... ok
|
||||
test_event_count_mismatch (__main__.S3b.test_event_count_mismatch) ... ok
|
||||
test_facts_normalization_is_reversible (__main__.S3b.test_facts_normalization_is_reversible) ... ok
|
||||
test_first_placeable_choice (__main__.S3b.test_first_placeable_choice) ... ok
|
||||
test_interval_gap (__main__.S3b.test_interval_gap) ... ok
|
||||
test_joined_s3b_self_comparison (__main__.S3b.test_joined_s3b_self_comparison) ... ok
|
||||
test_load_X_not_digest_only (__main__.S3b.test_load_X_not_digest_only) ... ok
|
||||
test_load_extra_event (__main__.S3b.test_load_extra_event) ... ok
|
||||
test_missing_path_and_combination (__main__.S3b.test_missing_path_and_combination) ... ok
|
||||
test_missing_preview_observation (__main__.S3b.test_missing_preview_observation) ... ok
|
||||
test_missing_trial (__main__.S3b.test_missing_trial) ... ok
|
||||
test_nested_mutation_required (__main__.S3b.test_nested_mutation_required) ... ok
|
||||
test_no_staleness_assertion_for_non_tick (__main__.S3b.test_no_staleness_assertion_for_non_tick) ... ok
|
||||
test_ownership_fact_dictionary_order_fallback (__main__.S3b.test_ownership_fact_dictionary_order_fallback) ... ok
|
||||
test_path_order (__main__.S3b.test_path_order) ... ok
|
||||
test_restore_extra_event (__main__.S3b.test_restore_extra_event) ... ok
|
||||
test_signal_facts_cannot_be_dropped (__main__.S3b.test_signal_facts_cannot_be_dropped) ... ok
|
||||
test_sort_rule_rejected (__main__.S3b.test_sort_rule_rejected) ... ok
|
||||
test_static_handler_and_outside_mutations (__main__.S3b.test_static_handler_and_outside_mutations) ... ok
|
||||
test_unpreviewed_choice (__main__.S3b.test_unpreviewed_choice) ... ok
|
||||
test_wrong_registry_in_either_role (__main__.S3b.test_wrong_registry_in_either_role) ... ok
|
||||
test_checkout_temp_destination_rejected (__main__.S4Bundle.test_checkout_temp_destination_rejected) ... ok
|
||||
test_existing_output_never_overwritten (__main__.S4Bundle.test_existing_output_never_overwritten) ... ok
|
||||
test_import_closure_rejections (__main__.S4Bundle.test_import_closure_rejections) ... ok
|
||||
test_missing_bundle_manifest_seal_and_changed_bytes (__main__.S4Bundle.test_missing_bundle_manifest_seal_and_changed_bytes) ... ok
|
||||
test_missing_private_evidence_and_extra_rejected (__main__.S4Bundle.test_missing_private_evidence_and_extra_rejected) ... ok
|
||||
test_missing_profile_not_export_success (__main__.S4Bundle.test_missing_profile_not_export_success) ... ok
|
||||
test_phase_role_pid_audio_and_guard_rejected (__main__.S4Bundle.test_phase_role_pid_audio_and_guard_rejected) ... ok
|
||||
test_rebound_lifecycle_and_scenario_contradictions (__main__.S4Bundle.test_rebound_lifecycle_and_scenario_contradictions) ... ok
|
||||
test_seal_normal_and_changes_not_native_proof (__main__.S4Bundle.test_seal_normal_and_changes_not_native_proof) ... ok
|
||||
test_synthetic_phase_all_real_checkers_not_native_proof (__main__.S4Bundle.test_synthetic_phase_all_real_checkers_not_native_proof) ... ok
|
||||
test_copied_manifest_root_and_destination_count_rejected (__main__.S4NativeBundleLogic.test_copied_manifest_root_and_destination_count_rejected) ... ok
|
||||
test_failure_keeps_raw_and_no_bundle_without_starting_engine (__main__.S4NativeBundleLogic.test_failure_keeps_raw_and_no_bundle_without_starting_engine) ... ok
|
||||
test_gate_none_success_not_boolean (__main__.S4NativeBundleLogic.test_gate_none_success_not_boolean) ... ok
|
||||
test_receipt_and_source_rejection_matrix (__main__.S4NativeBundleLogic.test_receipt_and_source_rejection_matrix) ... ok
|
||||
test_synthetic_full_bundle_positive_not_native_evidence (__main__.S4NativeBundleLogic.test_synthetic_full_bundle_positive_not_native_evidence) ... ok
|
||||
test_check_accepts_true (__main__.S4ProbeAdmission.test_check_accepts_true) ... ok
|
||||
test_check_rejects_false_all_errors (__main__.S4ProbeAdmission.test_check_rejects_false_all_errors) ... ok
|
||||
test_run_admission_accepts_true_mock_launch_only (__main__.S4ProbeAdmission.test_run_admission_accepts_true_mock_launch_only) ... ok
|
||||
test_run_admission_rejects_false_without_engine (__main__.S4ProbeAdmission.test_run_admission_rejects_false_without_engine) ... ok
|
||||
test_nine_original_destinations_required (__main__.S4RemainingBindings.test_nine_original_destinations_required) ... ok
|
||||
test_nonzero_determinism_rejected_after_real_join (__main__.S4RemainingBindings.test_nonzero_determinism_rejected_after_real_join) ... ok
|
||||
test_owned_lifecycle_exit_diagnostic_and_profile_rejections (__main__.S4RemainingBindings.test_owned_lifecycle_exit_diagnostic_and_profile_rejections) ... ok
|
||||
test_verifier_accepts_true_recomputes_raw (__main__.S4RemainingBindings.test_verifier_accepts_true_recomputes_raw) ... ok
|
||||
test_verifier_rejects_false_with_every_error (__main__.S4RemainingBindings.test_verifier_rejects_false_with_every_error) ... ok
|
||||
test_cached_verdict_not_trusted (__main__.S4StoredResultBindings.test_cached_verdict_not_trusted) ... ok
|
||||
test_missing_sidecar_rejected_after_seal (__main__.S4StoredResultBindings.test_missing_sidecar_rejected_after_seal) ... ok
|
||||
test_raw_joined_substitution_rejected_after_seal (__main__.S4StoredResultBindings.test_raw_joined_substitution_rejected_after_seal) ... ok
|
||||
test_blob_transport_is_verified_and_bound (__main__.S5.test_blob_transport_is_verified_and_bound) ... ok
|
||||
test_blob_trees_get_the_same_full_validation_as_inline (__main__.S5.test_blob_trees_get_the_same_full_validation_as_inline) ... ok
|
||||
test_bundled_inputs_replace_unreadable_originals (__main__.S5.test_bundled_inputs_replace_unreadable_originals) ... ok
|
||||
test_compare_and_digest_equal_s1_reference (__main__.S5.test_compare_and_digest_equal_s1_reference) ... ok
|
||||
test_event_stream_replacement_is_existing_defect_not_pass (__main__.S5.test_event_stream_replacement_is_existing_defect_not_pass) ... ok
|
||||
test_every_checkpoint_full_tree_verified_like_inline (__main__.S5.test_every_checkpoint_full_tree_verified_like_inline)
|
||||
S5 drops the diff chain: each checkpoint carries a complete tree, verified alone. ... ok
|
||||
test_fast_normalization_equals_original (__main__.S5.test_fast_normalization_equals_original) ... ok
|
||||
test_labelled_harness_digest_falls_back_to_dictionary_order_only (__main__.S5.test_labelled_harness_digest_falls_back_to_dictionary_order_only) ... ok
|
||||
test_lifecycle_binds_replacements_guard_text_and_blobs (__main__.S5.test_lifecycle_binds_replacements_guard_text_and_blobs) ... ok
|
||||
test_manifest_numbers_keep_their_type (__main__.S5.test_manifest_numbers_keep_their_type) ... ok
|
||||
test_memo_scope_same_results_and_restores_gc (__main__.S5.test_memo_scope_same_results_and_restores_gc) ... ok
|
||||
test_path_copy_builders_equal_deepcopy_reference (__main__.S5.test_path_copy_builders_equal_deepcopy_reference) ... ok
|
||||
test_probe_static_full_tree_blob_and_stream_requirements (__main__.S5.test_probe_static_full_tree_blob_and_stream_requirements) ... ok
|
||||
test_publish_pairs_bound_to_source_causes (__main__.S5.test_publish_pairs_bound_to_source_causes) ... ok
|
||||
test_signed_instance_ids_raw_kept_and_excluded (__main__.S5.test_signed_instance_ids_raw_kept_and_excluded) ... ok
|
||||
test_validate_tree_equals_s1_reference_on_mutations (__main__.S5.test_validate_tree_equals_s1_reference_on_mutations) ... ok
|
||||
test_bundled_destination_must_be_normalized_inside_readonly (__main__.S6Admission.test_bundled_destination_must_be_normalized_inside_readonly) ... ok
|
||||
test_check_path_admits_registered_after_version (__main__.S6Admission.test_check_path_admits_registered_after_version) ... ok
|
||||
test_check_path_rejects_unregistered_or_unchanged (__main__.S6Admission.test_check_path_rejects_unregistered_or_unchanged) ... ok
|
||||
test_main_passes_sorted_registration_and_records_version (__main__.S6Admission.test_main_passes_sorted_registration_and_records_version) ... ok
|
||||
test_root_manifest_pin_equals_collector_literal (__main__.S6Admission.test_root_manifest_pin_equals_collector_literal) ... ok
|
||||
test_after_version_bundle_recomputes (__main__.S6Bundles.test_after_version_bundle_recomputes) ... ok
|
||||
test_cross_version_comparison (__main__.S6Bundles.test_cross_version_comparison) ... ok
|
||||
test_cross_version_pin_closure_and_seal_rejections (__main__.S6Bundles.test_cross_version_pin_closure_and_seal_rejections) ... ok
|
||||
test_cross_version_real_difference_is_not_ok (__main__.S6Bundles.test_cross_version_real_difference_is_not_ok) ... ok
|
||||
test_recompute_rejects_version_closure_and_label_changes (__main__.S6Bundles.test_recompute_rejects_version_closure_and_label_changes) ... ok
|
||||
test_digest_state_values (__main__.S6SealAndProbe.test_digest_state_values) ... ok
|
||||
test_probe_records_actual_digest_state (__main__.S6SealAndProbe.test_probe_records_actual_digest_state) ... ok
|
||||
test_seal_records_every_directory (__main__.S6SealAndProbe.test_seal_records_every_directory) ... ok
|
||||
test_admit_before_and_registered_after (__main__.S6SourceClosure.test_admit_before_and_registered_after) ... ok
|
||||
test_before_map_is_the_frozen_source_map (__main__.S6SourceClosure.test_before_map_is_the_frozen_source_map) ... ok
|
||||
test_before_map_tampering_rejected (__main__.S6SourceClosure.test_before_map_tampering_rejected) ... ok
|
||||
test_unregistered_added_missing_and_malformed_registrations_rejected (__main__.S6SourceClosure.test_unregistered_added_missing_and_malformed_registrations_rejected) ... ok
|
||||
test_fixed_scenario_paths (__main__.SourcesAndMetrics.test_fixed_scenario_paths) ... ok
|
||||
test_metric_ranges (__main__.SourcesAndMetrics.test_metric_ranges) ... ok
|
||||
test_source_closure_synthetic_registered_map (__main__.SourcesAndMetrics.test_source_closure_synthetic_registered_map) ... ok
|
||||
test_source_pin_is_not_caller_controlled (__main__.SourcesAndMetrics.test_source_pin_is_not_caller_controlled) ... ok
|
||||
test_array_order_and_all_differences (__main__.TypedTrees.test_array_order_and_all_differences) ... ok
|
||||
test_components_and_sha (__main__.TypedTrees.test_components_and_sha) ... ok
|
||||
test_deep_state_not_truncated (__main__.TypedTrees.test_deep_state_not_truncated) ... ok
|
||||
test_duplicate_textual_keys_rejected (__main__.TypedTrees.test_duplicate_textual_keys_rejected) ... ok
|
||||
test_exact_and_types (__main__.TypedTrees.test_exact_and_types) ... ok
|
||||
test_nested_order_all_paths (__main__.TypedTrees.test_nested_order_all_paths) ... ok
|
||||
test_nonstring_keys_keep_types_and_values (__main__.TypedTrees.test_nonstring_keys_keep_types_and_values) ... ok
|
||||
test_unsupported_and_malformed (__main__.TypedTrees.test_unsupported_and_malformed) ... ok
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Ran 179 tests in 206.326s
|
||||
|
||||
OK
|
||||
@@ -973,7 +973,7 @@ def _check_e45(raw, evidence, previous):
|
||||
obs, detail = ev['observations'], ev['detail']
|
||||
eq.require(eq.exact(detail['signals'], ev['signals']), 'scenario signals/facts binding')
|
||||
if scenario['id'].startswith('E5/'):
|
||||
eq.require(set(detail) == {'target', 'location', 'direction', 'digest_materialized_before', 'held_before', 'held_after', 'signals'}, 'E5 detail fields')
|
||||
eq.require(set(detail) == {'target', 'location', 'direction', 'latest_digest_dirty_before', 'held_before', 'held_after', 'signals'}, 'E5 detail fields')
|
||||
else:
|
||||
common = {'save_budget_before', 'save_budget_after', 'signals'}
|
||||
specific = {
|
||||
@@ -1067,7 +1067,9 @@ def _check_e45(raw, evidence, previous):
|
||||
_, target, location, direction = scenario['id'].split('/')
|
||||
eq.require(op == direction and all(detail[k] == v for k, v in [('target', target), ('location', location), ('direction', direction)]), 'E5 target/direction')
|
||||
eq.require(result['applicability'] == 'applied' and result['reason'] is None and len(obs) == 4, 'E5 coverage/NA')
|
||||
eq.require(detail['digest_materialized_before'] is True, 'E5 digest not materialized')
|
||||
# S6: the probe records the product's actual _latest_digest_dirty before the operation;
|
||||
# false (materialized) or null (this version has no such field) only.
|
||||
eq.require(detail['latest_digest_dirty_before'] is False or detail['latest_digest_dirty_before'] is None, 'E5 digest not materialized')
|
||||
minutes = [o['checkpoint']['minute'] for o in obs]
|
||||
eq.require(minutes == [minutes[0], minutes[0]+1, minutes[0]+1+int(direction == 'inside_out'), minutes[0]+2+int(direction == 'inside_out')], 'E5 preparation/operation/reset tick sequence')
|
||||
held = eq._transport(detail['held_before'], None)
|
||||
|
||||
@@ -863,6 +863,44 @@ def check_source_closure(before_map, after_map, allowed_changed):
|
||||
return {'ok': not errors, 'errors': errors, 'before_sha256': digest, 'added': added, 'missing': missing, 'changed': changed, 'allowed_changed': sorted(allowed)}
|
||||
|
||||
|
||||
# S6 (EQ_S6_AFTER_VERSION_PLAN): the fixed "before" of the source closure. The file holds
|
||||
# the registered base product source map; SOURCE_SHA fixes its meaning, so it is checked
|
||||
# on every read rather than trusted.
|
||||
BEFORE_SOURCE_MAP = Path(__file__).resolve().parent / 'BEFORE_SOURCE_MAP.json'
|
||||
|
||||
|
||||
def _unique_object(pairs):
|
||||
keys = [key for key, _ in pairs]
|
||||
require(len(keys) == len(set(keys)), 'duplicate JSON key')
|
||||
return dict(pairs)
|
||||
|
||||
|
||||
def before_source_map():
|
||||
value = json.loads(BEFORE_SOURCE_MAP.read_text(encoding='utf-8'), object_pairs_hook=_unique_object)
|
||||
require(type(value) is dict and len(value) == 1407, 'before map is not 1407 sources')
|
||||
for name, row in value.items():
|
||||
require(type(row) is dict and set(row) == {'bytes', 'sha256'} and type(row['bytes']) is int and
|
||||
type(row['sha256']) is str and re.fullmatch('[0-9a-f]{64}', row['sha256']), 'before map row')
|
||||
digest = hashlib.sha256(json.dumps(value, sort_keys=True, separators=(',', ':')).encode()).hexdigest()
|
||||
require(digest == SOURCE_SHA, 'before map is not the frozen 1407-source map')
|
||||
return value
|
||||
|
||||
|
||||
def admit_source(source, registered):
|
||||
"""Admission rule of both versions (OPT_CONTRACT_T1 sections 2 and 4).
|
||||
|
||||
registered is the sorted, duplicate-free list of OPT files the version changes. Empty
|
||||
means the before version (no difference from the frozen map, as SOURCE_SHA required
|
||||
before S6); otherwise the after version, whose changed set must equal registered with
|
||||
no added or missing source. Anything else is rejected with every closure error."""
|
||||
require(type(registered) is list and all(type(p) is str for p in registered), 'registered change list required')
|
||||
require(registered == sorted(set(registered)), 'registered changes must be sorted and unique')
|
||||
closure = check_source_closure(before_source_map(), source, registered)
|
||||
require(closure['ok'], 'source closure: ' + repr(closure['errors']))
|
||||
after = hashlib.sha256(json.dumps(source, sort_keys=True, separators=(',', ':')).encode()).hexdigest()
|
||||
return dict(closure, version='after' if registered else 'before', after_sha256=after)
|
||||
|
||||
|
||||
K_COMPARE = frozenset(('absolute_minute', 'speed', 'paused', 'snapshot_version',
|
||||
'_accumulator', '_skip_next_process', '_pause_request_serial',
|
||||
'_abort_current_tick_requested', '_last_tick_advanced', '_phase_handlers'))
|
||||
|
||||
@@ -844,8 +844,9 @@ func run_e5() -> void:
|
||||
"snapshot_published": returned = snapshot_reference
|
||||
"state_updated": returned = state_reference
|
||||
var held_copy: Dictionary = returned.duplicate(true)
|
||||
# S6: the actual digest state just before the operation (null if this version has no such field).
|
||||
var detail: Dictionary = {"target": target, "location": location, "direction": direction,
|
||||
"digest_materialized_before": true, "held_before": transport(held_copy)}
|
||||
"latest_digest_dirty_before": runtime.get("_latest_digest_dirty"), "held_before": transport(held_copy)}
|
||||
if direction == "outside_in":
|
||||
if location == "dict":
|
||||
returned[&"systems"][&"runtime"][&"id_generator"][&"counters"]["__p28_eq_probe"] = 1
|
||||
|
||||
@@ -43,6 +43,9 @@ FROZEN = {
|
||||
'registration/ROOT_PROPOSAL_BYTE_TRANSFER.json': '63197f650849084f769e487a76458361886a37090042f1e351851c75b0384da2',
|
||||
}
|
||||
require, sha, pin, write = verify.require, verify.sha, verify.pin, verify.write
|
||||
# collector.inputs fixes the Root input manifest by this SHA (collect_p28_01.py inputs());
|
||||
# the bundled copy is held to the same value (S6; test_eq checks both literals agree).
|
||||
ROOT_MANIFEST_SHA = '9dac0a51d52087e691c5723b4f78f171f7998c78b582b2b6c4c904798e77644d'
|
||||
|
||||
|
||||
def load_dependencies(root=ROOT):
|
||||
@@ -83,13 +86,21 @@ def bundled_inputs(copies, readonly):
|
||||
The formal native-verify runs under the Runner seatbelt, which cannot read the original
|
||||
read-only directory or the fixture outside the checkout. The same rule applies: every
|
||||
manifest destination lies in the original read-only directory, and its copy has the
|
||||
recorded bytes and SHA. The manifest SHA is bound by the caller to the admission pin."""
|
||||
recorded bytes and SHA. The manifest SHA is bound by the caller to the admission pin.
|
||||
|
||||
S6: the copy must also be the manifest collector.inputs fixes, and each destination must
|
||||
be a normalized absolute path ('..' never appears) inside the read-only directory. This
|
||||
is checked by text because the sandboxed verification cannot read the originals; the run
|
||||
itself checked the resolved originals through collector.inputs."""
|
||||
manifest_path = copies / 'ROOT_INPUT_MANIFEST.json'
|
||||
require(sha(manifest_path) == ROOT_MANIFEST_SHA, 'Root input manifest changed')
|
||||
manifest = verify.read(manifest_path)
|
||||
pins = {}
|
||||
for row in manifest['files']:
|
||||
destination = Path(row['destination'])
|
||||
require(destination.is_absolute() and destination.is_relative_to(readonly), 'input escapes read-only directory')
|
||||
text = row['destination']
|
||||
require(type(text) is str and os.path.isabs(text) and os.path.normpath(text) == text, 'input escapes read-only directory')
|
||||
destination = Path(text)
|
||||
require(destination.is_relative_to(readonly), 'input escapes read-only directory')
|
||||
relative = destination.relative_to(readonly)
|
||||
copy = copies / relative
|
||||
require(copy.is_file() and not copy.is_symlink(), 'bundled read-only input missing')
|
||||
@@ -114,14 +125,17 @@ def bundle_inputs(out, readonly, fixture, pins):
|
||||
require(bundled_inputs(target / 'readonly', readonly) == pins['readonly'], 'read-only input copies differ')
|
||||
|
||||
|
||||
def actual_pins(root, readonly, fixture, engine, collector, standard, copies=None):
|
||||
def actual_pins(root, readonly, fixture, engine, collector, standard, copies=None, registered=()):
|
||||
"""registered: the OPT files the measured version changes (S6, --opt-changed); empty for
|
||||
the before version. eq.admit_source decides the version from the frozen before map."""
|
||||
local = root / HERE.relative_to(ROOT)
|
||||
dependencies = {n: sha(root / n) for n in DEPENDENCIES}
|
||||
require(dependencies == DEPENDENCIES, 'dependencies changed')
|
||||
frozen = {n: sha(local / n) for n in FROZEN}
|
||||
require(frozen == FROZEN, 'frozen specification changed')
|
||||
source = collector.source_map(standard, root)
|
||||
require(len(source) == 1407 and collector.digest(source) == eq.SOURCE_SHA, 'full 1407 source pin')
|
||||
closure = eq.admit_source(source, list(registered))
|
||||
require(len(source) == 1407 and collector.digest(source) == closure['after_sha256'], 'full 1407 source pin')
|
||||
require(sha(fixture) == collector.FIXTURE_SHA and sha(engine) == collector.ENGINE_SHA, 'fixture/engine pin')
|
||||
inputs = collector.inputs(readonly) if copies is None else bundled_inputs(copies, readonly)
|
||||
require(len(inputs['files']) == 9, 'nine original destinations required')
|
||||
@@ -131,7 +145,8 @@ def actual_pins(root, readonly, fixture, engine, collector, standard, copies=Non
|
||||
machine = subprocess.check_output(['sysctl', '-n', 'hw.model'], text=True).strip()
|
||||
require(machine == 'Mac16,10', 'machine pin')
|
||||
return {'source': source, 'source_sha256': collector.digest(source), 'dependencies': dependencies,
|
||||
'frozen': frozen, 'worker': {n: sha(local / n) for n in sorted(verify.LOCAL_NAMES)},
|
||||
'registered_changes': closure['allowed_changed'], 'source_closure': closure,
|
||||
'frozen': frozen, 'worker': {n: sha(local / n) for n in sorted(verify.LOCAL_NAMES | verify.DATA_NAMES)},
|
||||
'fixture': pin(fixture), 'engine': pin(engine), 'engine_version': collector.VERSION,
|
||||
'project': source['project.godot'], 'readonly': inputs, 'original_manifest': manifest,
|
||||
'machine': machine, 'platform': sys.platform}
|
||||
@@ -139,7 +154,7 @@ def actual_pins(root, readonly, fixture, engine, collector, standard, copies=Non
|
||||
|
||||
def frozen_commit(source):
|
||||
commit = subprocess.check_output(['git', '-C', str(ROOT), 'rev-parse', 'HEAD'], text=True).strip()
|
||||
for name in list(source) + [str((HERE / n).relative_to(ROOT)) for n in verify.LOCAL_NAMES]:
|
||||
for name in list(source) + [str((HERE / n).relative_to(ROOT)) for n in sorted(verify.LOCAL_NAMES | verify.DATA_NAMES)]:
|
||||
data = subprocess.check_output(['git', '-C', str(ROOT), 'show', commit + ':' + name])
|
||||
require(data == (ROOT / name).read_bytes(), 'not frozen S: ' + name)
|
||||
return commit
|
||||
@@ -264,7 +279,8 @@ def run(admission, out, collector, standard, outer, diag):
|
||||
'profiles_retained': {str(p): collector.profile_files(p) for p in profiles}}
|
||||
try:
|
||||
execution['pins_after'] = actual_pins(ROOT, Path(admission['readonly_path']), Path(admission['fixture']),
|
||||
Path(admission['engine']), collector, standard)
|
||||
Path(admission['engine']), collector, standard,
|
||||
registered=admission['pins']['registered_changes'])
|
||||
require((ROOT / 'project.godot').read_bytes() == public and (stage / 'project.godot').read_bytes() == public, 'project restoration')
|
||||
if error is None:
|
||||
with check.memo_scope():
|
||||
@@ -288,15 +304,19 @@ def main():
|
||||
mode.add_argument('--run', action='store_true')
|
||||
for name in ('readonly', 'fixture', 'engine', 'output'):
|
||||
parser.add_argument('--' + name, type=Path, required=True)
|
||||
# S6: OPT_CONTRACT_T1 §4 runs this same command on the OPT S; each changed OPT file is
|
||||
# registered here (repeat the option). None means the before version.
|
||||
parser.add_argument('--opt-changed', action='append', default=[], metavar='PATH')
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
readonly, fixture, engine, out = [p.resolve() for p in (args.readonly, args.fixture, args.engine, args.output)]
|
||||
require(not out.exists(), 'new output required')
|
||||
collector, standard, outer, diag = load_dependencies()
|
||||
pins = actual_pins(ROOT, readonly, fixture, engine, collector, standard)
|
||||
pins = actual_pins(ROOT, readonly, fixture, engine, collector, standard, registered=sorted(args.opt_changed))
|
||||
static = check.check_probe((HERE / 'eq_probe.gd').read_text())
|
||||
require(static['ok'], 'probe checker: ' + repr(static['errors']))
|
||||
admission = {'measured_source': 'S_eq-native-not-Q', 'candidate_Q_measured': False,
|
||||
version = pins['source_closure']['version']
|
||||
admission = {'measured_source': verify.MEASURED[version], 'version': version, 'candidate_Q_measured': False,
|
||||
'commit': frozen_commit(pins['source']), 'source_map': pins['source'], 'pins': pins,
|
||||
'readonly_path': str(readonly), 'fixture': str(fixture), 'engine': str(engine),
|
||||
'phases': verify.PHASES, 'plan': plan(collector, standard, engine, fixture, out)}
|
||||
|
||||
@@ -1067,7 +1067,7 @@ def e45_fixture(role='R'):
|
||||
post['checkpoint']['latest'] = transport(after_held)
|
||||
post['checkpoint']['computed_latest_digest'] = 'c'*64
|
||||
current = observed(tree, minute+1)
|
||||
detail = {'target': target, 'location': location, 'direction': direction, 'digest_materialized_before': True,
|
||||
detail = {'target': target, 'location': location, 'direction': direction, 'latest_digest_dirty_before': False,
|
||||
'held_before': transport(held), 'held_after': transport(after_held)}
|
||||
emit('E5/'+target+'/'+location+'/'+direction, direction, [begin, baseline, post, copy.deepcopy(current)], detail)
|
||||
return raw, evidence, previous
|
||||
@@ -1132,7 +1132,7 @@ class S3b(unittest.TestCase):
|
||||
self.reject(lambda r, e, p: e[5]['detail'].update(held_after=e[5]['detail']['held_before']))
|
||||
|
||||
def test_digest_materialization_required(self):
|
||||
self.reject(lambda r, e, p: e[5]['detail'].update(digest_materialized_before=False))
|
||||
self.reject(lambda r, e, p: e[5]['detail'].update(latest_digest_dirty_before=True))
|
||||
|
||||
def test_NA_not_PASS(self):
|
||||
self.reject(lambda r, e, p: r['scenarios'][18]['result'].update(applicability='PASS'))
|
||||
@@ -1371,16 +1371,28 @@ def s4_owned_fixture(directory, item, pid):
|
||||
('process.json', 'started.json', 'ended.json', 'adapter-owned-cleanup.json', 'outer-owned-finally.json')}}
|
||||
|
||||
|
||||
def s4_bundle_fixture(directory, collector, standard):
|
||||
"""Synthetic receipts only. Production validators are real; no Godot claim."""
|
||||
def s4_bundle_fixture(directory, collector, standard, registered=(), pin_overrides=None):
|
||||
"""Synthetic receipts only. Production validators are real; no Godot claim.
|
||||
S6: registered gives an after-version admission whose map changes exactly those OPT
|
||||
files (synthetic SHAs); pin_overrides replaces non-product pins."""
|
||||
source = collector.source_map(standard, verifier.ROOT)
|
||||
if registered:
|
||||
source = copy.deepcopy(source)
|
||||
for name in registered: source[name] = {'bytes': source[name]['bytes'], 'sha256': 'f'*64}
|
||||
closure = eq.admit_source(source, list(registered))
|
||||
for name in ('eq_probe.gd',): (directory/name).write_bytes((verifier.HERE/name).read_bytes())
|
||||
(directory/'profile_guard.gd').write_text('synthetic guard bytes')
|
||||
(directory/'guarded.gd').write_text(driver.wrapper_text(directory))
|
||||
verifier.write(directory/'root-input-manifest.json', {'synthetic_not_native': True})
|
||||
pins = {'worker': {'eq_probe.gd': verifier.sha(directory/'eq_probe.gd')},
|
||||
'readonly': {'files': {'run-r02/profile_guard.gd': verifier.sha(directory/'profile_guard.gd')},
|
||||
'manifest_sha256': verifier.sha(directory/'root-input-manifest.json')}}
|
||||
'manifest_sha256': verifier.sha(directory/'root-input-manifest.json')},
|
||||
'registered_changes': closure['allowed_changed'], 'source_closure': closure,
|
||||
'dependencies': dict(driver.DEPENDENCIES), 'frozen': dict(driver.FROZEN),
|
||||
'fixture': {'bytes': 7, 'sha256': collector.FIXTURE_SHA}, 'engine': {'bytes': 1, 'sha256': collector.ENGINE_SHA},
|
||||
'engine_version': collector.VERSION, 'original_manifest': {'synthetic_not_native': True},
|
||||
'machine': 'synthetic', 'platform': 'synthetic'}
|
||||
pins.update(pin_overrides or {})
|
||||
commands = [collector.argv_for(Path('/synthetic/Godot'), directory/'stage', directory/'guarded.gd', True),
|
||||
collector.parse_argv(Path('/synthetic/Godot'), directory/'stage', directory/'eq_probe.gd'),
|
||||
collector.parse_argv(Path('/synthetic/Godot'), directory/'stage', directory/'guarded.gd')] + [
|
||||
@@ -1412,8 +1424,8 @@ def s4_bundle_fixture(directory, collector, standard):
|
||||
for kind in ('startup', 'runtime'): retained[item[kind+'_profile']] = row['profile_copies'][kind]['files']
|
||||
rows.append(row)
|
||||
cleanup.append({'pid': pid, 'engine_ended': True, 'outer_cleanup_errors': [], 'foreign_process_or_group_signals': 0})
|
||||
admission = {'source_map': source, 'phases': verifier.PHASES, 'plan': plan, 'pins': pins,
|
||||
'measured_source': 'S_eq-native-not-Q', 'candidate_Q_measured': False,
|
||||
admission = {'source_map': source, 'phases': verifier.PHASES, 'plan': plan, 'pins': pins, 'commit': 'synthetic-' + closure['version'],
|
||||
'version': closure['version'], 'measured_source': verifier.MEASURED[closure['version']], 'candidate_Q_measured': False,
|
||||
'readonly_path': '/synthetic/readonly', 'fixture': '/synthetic/fixture', 'engine': '/synthetic/Godot'}
|
||||
verifier.write(directory/'admission.json', admission)
|
||||
return {'passed': True, 'error': None, 'cleanup_errors': [], 'cleanup': cleanup, 'admission': admission,
|
||||
@@ -1594,7 +1606,7 @@ class S4ProbeAdmission(unittest.TestCase):
|
||||
errors = [] if ok else ['first static violation', 'second static violation']
|
||||
with patch.object(sys, 'argv', argv), \
|
||||
patch.object(driver, 'load_dependencies', return_value=(None, None, None, None)), \
|
||||
patch.object(driver, 'actual_pins', return_value={'source': {}}), \
|
||||
patch.object(driver, 'actual_pins', return_value={'source': {}, 'source_closure': {'version': 'before'}}), \
|
||||
patch.object(checker, 'check_probe', return_value={'ok': ok, 'errors': errors}) as probe, \
|
||||
patch.object(driver, 'frozen_commit', return_value='synthetic'), \
|
||||
patch.object(driver, 'plan', return_value=[]), \
|
||||
@@ -1685,7 +1697,7 @@ class S4StoredResultBindings(unittest.TestCase):
|
||||
# exercised without a mock by S4NativeBundleLogic and S4RemainingBindings.
|
||||
with synthetic_directory() as name:
|
||||
directory = Path(name)
|
||||
admission = dict(readonly_path=name, fixture=name, engine=name, pins={})
|
||||
admission = dict(readonly_path=name, fixture=name, engine=name, pins={'registered_changes': []})
|
||||
verifier.write(directory/'execution.json', {'admission': admission})
|
||||
result = {'phases': {'R-A': {'raw': {'kind': 'raw'}, 'joined': {'kind': 'joined'},
|
||||
'proofs': [{'sidecar': 'required'}], 'checker': {'ok': True}}}}
|
||||
@@ -1699,7 +1711,7 @@ class S4StoredResultBindings(unittest.TestCase):
|
||||
verifier.write(directory/'R-A'/(key+'.json'), value)
|
||||
verifier.seal(directory)
|
||||
with patch.object(driver, 'load_dependencies', return_value=(None, None, None, None)), \
|
||||
patch.object(driver, 'actual_pins', return_value={}), \
|
||||
patch.object(driver, 'actual_pins', return_value={'registered_changes': []}), \
|
||||
patch.object(verifier, 'recompute', return_value=result) as recompute:
|
||||
with self.assertRaisesRegex(eq.EvidenceError, 'recomputed results differ|copy differs'):
|
||||
verifier.verify(verifier.ROOT, directory)
|
||||
@@ -1713,6 +1725,7 @@ class S4StoredResultBindings(unittest.TestCase):
|
||||
# S5 (EQ_S5_SCALE_PLAN r01/r02): content-addressed blobs, exact fast paths, event stream.
|
||||
import gzip
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
|
||||
@@ -2283,6 +2296,8 @@ class S5(unittest.TestCase):
|
||||
(original / relative).parent.mkdir(parents=True, exist_ok=True); (original / relative).write_bytes(content)
|
||||
rows.append({'destination': str(original / relative), 'bytes': len(content), 'sha256': hashlib.sha256(content).hexdigest()})
|
||||
original.joinpath('ROOT_INPUT_MANIFEST.json').write_text(json.dumps({'files': rows}))
|
||||
manifest_pin = patch.object(driver, 'ROOT_MANIFEST_SHA', verifier.sha(original / 'ROOT_INPUT_MANIFEST.json'))
|
||||
manifest_pin.start(); self.addCleanup(manifest_pin.stop)
|
||||
fixture = self.root / 'reference.variant'; fixture.write_bytes(b'fixture')
|
||||
pins = {'fixture': verifier.pin(fixture), 'readonly': driver.bundled_inputs(original, original)}
|
||||
driver.bundle_inputs(self.root, original, fixture, pins)
|
||||
@@ -2298,8 +2313,11 @@ class S5(unittest.TestCase):
|
||||
outside = json.loads((self.root / 'inputs' / 'readonly' / 'ROOT_INPUT_MANIFEST.json').read_text())
|
||||
outside['files'][0]['destination'] = '/elsewhere/a.py'
|
||||
(self.root / 'inputs' / 'readonly' / 'ROOT_INPUT_MANIFEST.json').write_text(json.dumps(outside))
|
||||
with self.assertRaisesRegex(eq.EvidenceError, 'input escapes read-only directory'):
|
||||
with self.assertRaisesRegex(eq.EvidenceError, 'Root input manifest changed'):
|
||||
driver.bundled_inputs(self.root / 'inputs' / 'readonly', original)
|
||||
with patch.object(driver, 'ROOT_MANIFEST_SHA', verifier.sha(self.root / 'inputs' / 'readonly' / 'ROOT_INPUT_MANIFEST.json')):
|
||||
with self.assertRaisesRegex(eq.EvidenceError, 'input escapes read-only directory'):
|
||||
driver.bundled_inputs(self.root / 'inputs' / 'readonly', original)
|
||||
|
||||
def test_probe_static_full_tree_blob_and_stream_requirements(self):
|
||||
probe = (verifier.HERE/'eq_probe.gd').read_text()
|
||||
@@ -2319,5 +2337,298 @@ class S5(unittest.TestCase):
|
||||
self.assertFalse(checker.check_probe(probe.replace(old, new))['ok'])
|
||||
|
||||
|
||||
# S6 (EQ_S6_AFTER_VERSION_PLAN): the after version through the frozen harness, the
|
||||
# cross-version comparison, and the three low-severity review findings.
|
||||
OPT_FILE = 'autoload/simulation_runtime.gd'
|
||||
|
||||
|
||||
def s6_after_map(*names):
|
||||
after = copy.deepcopy(eq.before_source_map())
|
||||
for name in names: after[name] = {'bytes': after[name]['bytes'], 'sha256': 'f'*64}
|
||||
return after
|
||||
|
||||
|
||||
def s6_sealed_bundle(directory, logic, registered=(), pin_overrides=None):
|
||||
execution = s4_bundle_fixture(directory, logic.collector, logic.standard, registered, pin_overrides)
|
||||
# The production parallel path of native-verify (one worker process per role phase).
|
||||
with patch.object(logic.standard, 'profile_path', side_effect=lambda name: Path('/private')/name):
|
||||
result = verifier.recompute(directory, execution, logic.collector, logic.standard, logic.diag, parallel=True)
|
||||
verifier.write(directory/'execution.json', execution)
|
||||
verifier.write(directory/'results.json', result)
|
||||
for phase, values in result['phases'].items():
|
||||
for name, value in values.items(): verifier.write(directory/phase/(name+'.json'), value)
|
||||
verifier.seal(directory)
|
||||
return result
|
||||
|
||||
|
||||
class S6SourceClosure(unittest.TestCase):
|
||||
def test_before_map_is_the_frozen_source_map(self):
|
||||
before = eq.before_source_map()
|
||||
self.assertEqual(len(before), 1407)
|
||||
self.assertTrue(eq.check_source_closure(before, before, [])['ok'])
|
||||
self.assertIn(OPT_FILE, before)
|
||||
|
||||
def test_before_map_tampering_rejected(self):
|
||||
original = json.loads(eq.BEFORE_SOURCE_MAP.read_text())
|
||||
changed = copy.deepcopy(original); changed[OPT_FILE]['sha256'] = 'f'*64
|
||||
fewer = copy.deepcopy(original); fewer.pop(OPT_FILE)
|
||||
row = json.dumps(original[OPT_FILE])
|
||||
duplicate = '{' + json.dumps(OPT_FILE) + ': ' + row + ', ' + json.dumps(original, sort_keys=True)[1:]
|
||||
with synthetic_directory() as name:
|
||||
for label, content in (('changed', json.dumps(changed)), ('fewer', json.dumps(fewer)), ('duplicate', duplicate),
|
||||
('extra field', json.dumps({**original, OPT_FILE: {**original[OPT_FILE], 'x': 1}}))):
|
||||
target = Path(name)/(label.replace(' ', '-')+'.json'); target.write_text(content)
|
||||
with self.subTest(label=label), patch.object(eq, 'BEFORE_SOURCE_MAP', target), \
|
||||
self.assertRaises(eq.EvidenceError):
|
||||
eq.before_source_map()
|
||||
|
||||
def test_admit_before_and_registered_after(self):
|
||||
before = eq.admit_source(eq.before_source_map(), [])
|
||||
self.assertEqual((before['version'], before['changed'], before['after_sha256']), ('before', [], eq.SOURCE_SHA))
|
||||
names = sorted([OPT_FILE, 'scripts/core/simulation_snapshot.gd'])
|
||||
after = eq.admit_source(s6_after_map(*names), names)
|
||||
self.assertEqual((after['version'], after['changed'], after['allowed_changed']), ('after', names, names))
|
||||
self.assertNotEqual(after['after_sha256'], eq.SOURCE_SHA)
|
||||
|
||||
def test_unregistered_added_missing_and_malformed_registrations_rejected(self):
|
||||
before = eq.before_source_map()
|
||||
added = s6_after_map(OPT_FILE); added['scripts/core/new_file.gd'] = {'bytes': 1, 'sha256': 'a'*64}
|
||||
missing = s6_after_map(OPT_FILE); missing.pop('scripts/core/simulation_snapshot.gd')
|
||||
outside = sorted(set(before) - eq.OPT_PATHS)[0]
|
||||
cases = [
|
||||
('unregistered change', s6_after_map(OPT_FILE), []),
|
||||
('registered but unchanged', before, [OPT_FILE]),
|
||||
('registered subset only', s6_after_map(OPT_FILE, 'scripts/core/simulation_snapshot.gd'), [OPT_FILE]),
|
||||
('outside OPT paths', s6_after_map(outside), [outside]),
|
||||
('added source', added, [OPT_FILE]),
|
||||
('missing source', missing, [OPT_FILE]),
|
||||
('duplicate registration', s6_after_map(OPT_FILE), [OPT_FILE, OPT_FILE]),
|
||||
('unsorted registration', s6_after_map(OPT_FILE, 'scripts/core/command_queue.gd'), ['scripts/core/simulation_snapshot.gd', 'scripts/core/command_queue.gd']),
|
||||
('tuple registration', s6_after_map(OPT_FILE), (OPT_FILE,)),
|
||||
]
|
||||
for label, source, registered in cases:
|
||||
with self.subTest(label=label), self.assertRaises(eq.EvidenceError):
|
||||
eq.admit_source(source, registered)
|
||||
|
||||
|
||||
class S6Admission(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.collector, cls.standard, cls.outer, cls.diag = driver.load_dependencies()
|
||||
|
||||
def pins(self, source, registered):
|
||||
original_sha = driver.sha
|
||||
def synthetic_sha(path):
|
||||
if Path(path) == Path('/synthetic/fixture'): return self.collector.FIXTURE_SHA
|
||||
if Path(path) == Path('/synthetic/Godot'): return self.collector.ENGINE_SHA
|
||||
return original_sha(path)
|
||||
with synthetic_directory() as name:
|
||||
root = Path(name)
|
||||
(root/'ROOT_INPUT_MANIFEST.json').write_text(json.dumps({'files': [{'destination': str(n)} for n in range(9)]}))
|
||||
with patch.object(driver, 'sha', side_effect=synthetic_sha), \
|
||||
patch.object(driver.subprocess, 'check_output', return_value='Mac16,10\n'), \
|
||||
patch.object(driver, 'pin', return_value={'bytes': 1, 'sha256': 'a'*64}), \
|
||||
patch.object(self.collector, 'source_map', return_value=source), \
|
||||
patch.object(self.collector, 'inputs', return_value={'files': {str(n): 'sha' for n in range(9)}}):
|
||||
return driver.actual_pins(verifier.ROOT, root, Path('/synthetic/fixture'), Path('/synthetic/Godot'),
|
||||
self.collector, self.standard, registered=registered)
|
||||
|
||||
def test_check_path_admits_registered_after_version(self):
|
||||
pins = self.pins(s6_after_map(OPT_FILE), [OPT_FILE])
|
||||
self.assertEqual(pins['registered_changes'], [OPT_FILE])
|
||||
self.assertEqual((pins['source_closure']['version'], pins['source_closure']['changed']), ('after', [OPT_FILE]))
|
||||
self.assertIn('BEFORE_SOURCE_MAP.json', pins['worker'])
|
||||
before = self.pins(eq.before_source_map(), [])
|
||||
self.assertEqual((before['source_closure']['version'], before['source_sha256']), ('before', eq.SOURCE_SHA))
|
||||
|
||||
def test_check_path_rejects_unregistered_or_unchanged(self):
|
||||
for label, source, registered in (('unregistered', s6_after_map(OPT_FILE), []),
|
||||
('unchanged', eq.before_source_map(), [OPT_FILE])):
|
||||
with self.subTest(label=label), self.assertRaisesRegex(eq.EvidenceError, 'source closure'):
|
||||
self.pins(source, registered)
|
||||
|
||||
def test_main_passes_sorted_registration_and_records_version(self):
|
||||
with synthetic_directory() as name:
|
||||
argv = ['run_eq.py', '--check', '--readonly', name, '--fixture', name, '--engine', name,
|
||||
'--output', str(Path(name)/'new-output'), '--opt-changed', 'scripts/core/simulation_snapshot.gd',
|
||||
'--opt-changed', OPT_FILE]
|
||||
pins = {'source': {}, 'source_closure': {'version': 'after'}}
|
||||
with patch.object(sys, 'argv', argv), \
|
||||
patch.object(driver, 'load_dependencies', return_value=(None, None, None, None)), \
|
||||
patch.object(driver, 'actual_pins', return_value=pins) as actual, \
|
||||
patch.object(checker, 'check_probe', return_value={'ok': True, 'errors': []}), \
|
||||
patch.object(driver, 'frozen_commit', return_value='synthetic'), \
|
||||
patch.object(driver, 'plan', return_value=[]), \
|
||||
patch('builtins.print') as output:
|
||||
self.assertEqual(driver.main(), 0)
|
||||
self.assertEqual(actual.call_args.kwargs['registered'], sorted([OPT_FILE, 'scripts/core/simulation_snapshot.gd']))
|
||||
admission = json.loads(output.call_args.args[0])
|
||||
self.assertEqual((admission['version'], admission['measured_source']), ('after', 'S_opt-native-not-Q'))
|
||||
self.assertFalse(admission['candidate_Q_measured'])
|
||||
|
||||
def test_root_manifest_pin_equals_collector_literal(self):
|
||||
collector_text = (verifier.ROOT/driver.COLLECTOR).read_text()
|
||||
inputs_body = collector_text[collector_text.index('def inputs(readonly):'):collector_text.index('def reused_observations')]
|
||||
self.assertEqual(inputs_body.count(driver.ROOT_MANIFEST_SHA), 1)
|
||||
|
||||
def test_bundled_destination_must_be_normalized_inside_readonly(self):
|
||||
with synthetic_directory() as name:
|
||||
root = Path(name); readonly = root/'readonly'; copies = root/'copies'; copies.mkdir()
|
||||
(copies/'a.py').write_bytes(b'alpha')
|
||||
for destination in (str(readonly/'..'/'copies'/'a.py'), str(readonly)+'/./a.py', 'relative/a.py'):
|
||||
manifest = copies/'ROOT_INPUT_MANIFEST.json'
|
||||
manifest.write_text(json.dumps({'files': [{'destination': destination, 'bytes': 5,
|
||||
'sha256': hashlib.sha256(b'alpha').hexdigest()}]}))
|
||||
with self.subTest(destination=destination), \
|
||||
patch.object(driver, 'ROOT_MANIFEST_SHA', verifier.sha(manifest)), \
|
||||
self.assertRaisesRegex(eq.EvidenceError, 'input escapes read-only directory'):
|
||||
driver.bundled_inputs(copies, readonly)
|
||||
|
||||
|
||||
class S6Bundles(unittest.TestCase):
|
||||
"""A sealed before bundle and a sealed after bundle per class, each built by the real
|
||||
recompute (no part mocked). The cross-version comparison runs unmocked on them, for
|
||||
the positive path and for real differences; tampering runs on APFS clones (cp -c),
|
||||
so copies take no space and the suite stays within the validation limit."""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.collector, cls.standard, cls.outer, cls.diag = driver.load_dependencies()
|
||||
cls.versions = synthetic_directory(); root = Path(cls.versions.name)
|
||||
cls.before, cls.after = root/'before', root/'after'
|
||||
cls.before.mkdir(); cls.after.mkdir()
|
||||
s6_sealed_bundle(cls.before, cls)
|
||||
cls.after_result = s6_sealed_bundle(cls.after, cls, registered=[OPT_FILE])
|
||||
cls.directory, cls.execution = cls.before, verifier.read(cls.before/'execution.json')
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.versions.cleanup()
|
||||
|
||||
recompute = S4NativeBundleLogic.recompute
|
||||
|
||||
def copied(self, source, name):
|
||||
target = Path(self.versions.name)/name
|
||||
subprocess.run(['/bin/cp', '-c', '-R', str(source), str(target)], check=True)
|
||||
self.addCleanup(shutil.rmtree, target)
|
||||
verifier.check_seal(target)
|
||||
return target
|
||||
|
||||
def resealed(self, directory, mutate, name='execution.json'):
|
||||
"""Change a recorded file of a copy and seal it again (no recomputation)."""
|
||||
for seal in ('bundle.json', 'seal.json'): (directory/seal).unlink()
|
||||
if name == 'execution.json':
|
||||
execution = verifier.read(directory/'execution.json'); mutate(execution['admission'])
|
||||
for old in ('execution.json', 'admission.json'): (directory/old).unlink()
|
||||
verifier.write(directory/'execution.json', execution); verifier.write(directory/'admission.json', execution['admission'])
|
||||
else:
|
||||
value = verifier.read(directory/name); mutate(value)
|
||||
(directory/name).unlink(); verifier.write(directory/name, value)
|
||||
verifier.seal(directory)
|
||||
|
||||
def test_recompute_rejects_version_closure_and_label_changes(self):
|
||||
mutations = [
|
||||
('registered without change', lambda x: x['admission']['pins'].update(registered_changes=[OPT_FILE])),
|
||||
('closure record', lambda x: x['admission']['pins']['source_closure'].update(changed=[OPT_FILE])),
|
||||
('version', lambda x: x['admission'].update(version='after')),
|
||||
('label', lambda x: x['admission'].update(measured_source='S_opt-native-not-Q')),
|
||||
('registration missing', lambda x: x['admission']['pins'].pop('registered_changes')),
|
||||
]
|
||||
for name, mutate in mutations:
|
||||
value = copy.deepcopy(self.execution); mutate(value)
|
||||
with self.subTest(name=name), self.assertRaises((eq.EvidenceError, KeyError)): self.recompute(value)
|
||||
|
||||
def test_after_version_bundle_recomputes(self):
|
||||
result = self.after_result
|
||||
self.assertEqual((result['version'], result['measured_source'], result['registered_changes']),
|
||||
('after', 'S_opt-native-not-Q', [OPT_FILE]))
|
||||
self.assertFalse(result['candidate_Q_measured'])
|
||||
self.assertTrue(all(c['ok'] and c['differences'] == [] for c in result['comparisons'].values()))
|
||||
self.assertEqual(verifier.read(self.after/'results.json'), result)
|
||||
|
||||
def test_cross_version_comparison(self):
|
||||
result = verifier.compare_bundles(self.before, self.after)
|
||||
self.assertTrue(result['ok'])
|
||||
self.assertEqual(set(result['comparisons']), {'R', 'O'})
|
||||
self.assertTrue(all(c['ok'] and c['differences'] == [] for c in result['comparisons'].values()))
|
||||
self.assertEqual(result['versions']['after']['source_closure']['changed'], [OPT_FILE])
|
||||
self.assertEqual(result['versions']['before']['measured_source'], 'S_eq-native-not-Q')
|
||||
# The synthetic E5 aliasing defects are reported per version, never turned into a pass.
|
||||
self.assertTrue(result['existing_defects']['after']['R'])
|
||||
self.assertFalse(result['after_all_e_pass'])
|
||||
self.assertFalse(result['formal_acceptance'])
|
||||
for label, call in (('swapped', lambda: verifier.compare_bundles(self.after, self.before)),
|
||||
('same bundle', lambda: verifier.compare_bundles(self.before, self.before))):
|
||||
with self.subTest(label=label), self.assertRaises(eq.EvidenceError): call()
|
||||
|
||||
def test_cross_version_pin_closure_and_seal_rejections(self):
|
||||
drifted = self.copied(self.after, 'drifted')
|
||||
self.resealed(drifted, lambda a: a['pins'].update(frozen={'SPEC.md': 'f'*64}))
|
||||
with self.assertRaisesRegex(eq.EvidenceError, 'non-product pin differs between versions: frozen'):
|
||||
verifier.compare_bundles(self.before, drifted)
|
||||
closure = self.copied(self.after, 'closure')
|
||||
self.resealed(closure, lambda a: a['pins']['source_closure'].update(changed=[]))
|
||||
with self.assertRaisesRegex(eq.EvidenceError, 'after bundle version/closure'):
|
||||
verifier.compare_bundles(self.before, closure)
|
||||
label = self.copied(self.after, 'label')
|
||||
self.resealed(label, lambda a: a.update(measured_source='S_eq-native-not-Q'))
|
||||
with self.assertRaisesRegex(eq.EvidenceError, 'after bundle version/closure'):
|
||||
verifier.compare_bundles(self.before, label)
|
||||
extra = self.copied(self.after, 'extra')
|
||||
(extra/'extra.json').write_text('{}')
|
||||
with self.assertRaisesRegex(eq.EvidenceError, 'manifest/file/directory mismatch'):
|
||||
verifier.compare_bundles(self.before, extra)
|
||||
|
||||
def test_cross_version_real_difference_is_not_ok(self):
|
||||
# A real state difference in the sealed after report, found by the unmocked comparison.
|
||||
changed = self.copied(self.after, 'changed')
|
||||
self.resealed(changed, lambda report: report['checkpoints'][-1]['K'].update(speed=7), name='R-A/joined.json')
|
||||
result = verifier.compare_bundles(self.before, changed)
|
||||
self.assertFalse(result['ok'])
|
||||
self.assertTrue(result['comparisons']['R']['differences'])
|
||||
self.assertEqual(result['comparisons']['O']['differences'], [])
|
||||
|
||||
|
||||
class S6SealAndProbe(unittest.TestCase):
|
||||
def test_seal_records_every_directory(self):
|
||||
with synthetic_directory() as name:
|
||||
directory = Path(name)
|
||||
(directory/'R-A'/'startup-profile').mkdir(parents=True)
|
||||
verifier.write(directory/'R-A'/'raw.json', {'synthetic_not_native': True})
|
||||
verifier.seal(directory)
|
||||
self.assertEqual(verifier.read(directory/'bundle.json')['directories'], ['R-A', 'R-A/startup-profile'])
|
||||
verifier.check_seal(directory)
|
||||
(directory/'R-A'/'startup-profile').rmdir()
|
||||
with self.assertRaisesRegex(eq.EvidenceError, 'manifest/file/directory mismatch'): verifier.check_seal(directory)
|
||||
(directory/'R-A'/'startup-profile').mkdir(); (directory/'extra-empty').mkdir()
|
||||
with self.assertRaisesRegex(eq.EvidenceError, 'manifest/file/directory mismatch'): verifier.check_seal(directory)
|
||||
|
||||
def test_probe_records_actual_digest_state(self):
|
||||
probe = (verifier.HERE/'eq_probe.gd').read_text()
|
||||
self.assertEqual(probe.count('"latest_digest_dirty_before": runtime.get("_latest_digest_dirty")'), 1)
|
||||
self.assertNotIn('digest_materialized_before', probe)
|
||||
self.assertTrue(checker.check_probe(probe)['ok'])
|
||||
|
||||
def test_digest_state_values(self):
|
||||
def e5_rows(evidence):
|
||||
return [row for row in evidence if row['id'].startswith('E5/')]
|
||||
def refreshed(raw, evidence):
|
||||
# The raw result facts are the probe's copy of the detail; keep them bound.
|
||||
results = {s['id']: s['result'] for s in raw['scenarios']}
|
||||
for row in evidence: results[row['id']]['facts'] = checker.e45_facts(row['detail'])
|
||||
raw, evidence, previous = e45_fixture()
|
||||
self.assertEqual(len(e5_rows(evidence)), 16)
|
||||
for row in e5_rows(evidence): row['detail']['latest_digest_dirty_before'] = None
|
||||
refreshed(raw, evidence)
|
||||
self.assertEqual(len(checker.check_e45(raw, evidence, previous)), 21)
|
||||
for label, mutate, message in (('dirty', lambda d: d.update(latest_digest_dirty_before=True), 'E5 digest not materialized'),
|
||||
('integer', lambda d: d.update(latest_digest_dirty_before=0), 'E5 digest not materialized'),
|
||||
('missing', lambda d: d.pop('latest_digest_dirty_before'), 'E5 detail fields')):
|
||||
raw, evidence, previous = e45_fixture()
|
||||
mutate(e5_rows(evidence)[0]['detail'])
|
||||
refreshed(raw, evidence)
|
||||
with self.subTest(label=label), self.assertRaisesRegex(eq.EvidenceError, message):
|
||||
checker.check_e45(raw, evidence, previous)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
|
||||
@@ -31,6 +31,10 @@ def split_evidence(files):
|
||||
|
||||
|
||||
LOCAL_NAMES = {'run_eq.py', 'verify_eq.py', 'check_eq.py', 'compare_versions.py', 'eq_probe.gd', 'test_eq.py'}
|
||||
# S6: EQ data pinned with the code (the fixed before map of the source closure).
|
||||
DATA_NAMES = {'BEFORE_SOURCE_MAP.json'}
|
||||
# S6: the version a native measured, from its admitted source closure. Never Q.
|
||||
MEASURED = {'before': 'S_eq-native-not-Q', 'after': 'S_opt-native-not-Q'}
|
||||
require = eq.require
|
||||
|
||||
|
||||
@@ -71,10 +75,18 @@ def inventory(directory, seals=False):
|
||||
return result
|
||||
|
||||
|
||||
def directories(directory):
|
||||
"""Every directory below the bundle root, empty ones included (S6: the verifier binds
|
||||
each <phase>/startup-profile directory's existence, so the seal records them too)."""
|
||||
directory = Path(directory)
|
||||
return sorted(p.relative_to(directory).as_posix() for p in directory.rglob('*') if p.is_dir() and not p.is_symlink())
|
||||
|
||||
|
||||
def seal(directory):
|
||||
directory = Path(directory)
|
||||
require(not (directory / 'bundle.json').exists() and not (directory / 'seal.json').exists(), 'already sealed')
|
||||
write(directory / 'bundle.json', {'schema': 'p28-02-eq-bundle-v1', 'files': inventory(directory)})
|
||||
write(directory / 'bundle.json', {'schema': 'p28-02-eq-bundle-v2', 'files': inventory(directory),
|
||||
'directories': directories(directory)})
|
||||
write(directory / 'seal.json', {'bundle': pin(directory / 'bundle.json')})
|
||||
|
||||
|
||||
@@ -82,7 +94,8 @@ def check_seal(directory):
|
||||
directory = Path(directory)
|
||||
manifest, sealed = read(directory / 'bundle.json'), read(directory / 'seal.json')
|
||||
require(sealed == {'bundle': pin(directory / 'bundle.json')}, 'seal mismatch')
|
||||
require(manifest == {'schema': 'p28-02-eq-bundle-v1', 'files': inventory(directory)}, 'manifest/file mismatch')
|
||||
require(manifest == {'schema': 'p28-02-eq-bundle-v2', 'files': inventory(directory),
|
||||
'directories': directories(directory)}, 'manifest/file/directory mismatch')
|
||||
|
||||
|
||||
def check_audio(audio, pid):
|
||||
@@ -191,7 +204,7 @@ def recompute(directory, execution, collector, standard, diag, parallel=False):
|
||||
require(execution['passed'] is True and execution['error'] is None and execution['cleanup_errors'] == [], 'execution failed')
|
||||
admission = execution['admission']
|
||||
require(read(directory / 'admission.json') == admission, 'admission copy')
|
||||
require(admission['measured_source'] == 'S_eq-native-not-Q' and admission['candidate_Q_measured'] is False, 'Q measurement forbidden')
|
||||
require(admission['candidate_Q_measured'] is False, 'Q measurement forbidden')
|
||||
require(admission['phases'] == PHASES, 'phase plan')
|
||||
from run_eq import wrapper_text
|
||||
original_out = Path(admission['plan'][0]['folder']).parent
|
||||
@@ -209,7 +222,11 @@ def recompute(directory, execution, collector, standard, diag, parallel=False):
|
||||
static = check.check_probe((directory / 'eq_probe.gd').read_text())
|
||||
require(static['ok'], 'probe checker: ' + repr(static['errors']))
|
||||
source = admission['source_map']
|
||||
require(len(source) == 1407 and collector.digest(source) == eq.SOURCE_SHA, 'full source map pin')
|
||||
# S6: the frozen before map and the registered OPT change set decide the version; the
|
||||
# recorded closure, version and label must be exactly what the closure gives again.
|
||||
closure = eq.admit_source(source, admission['pins']['registered_changes'])
|
||||
require(admission['pins']['source_closure'] == closure and admission['version'] == closure['version'], 'recorded source closure/version')
|
||||
require(admission['measured_source'] == MEASURED[closure['version']], 'measured version label')
|
||||
require(execution['source_after'] == source and execution['player_before'] == execution['player_after'], 'source/player preservation')
|
||||
require(execution['pins_after'] == admission['pins'], 'pin preservation')
|
||||
rows = execution['phases']
|
||||
@@ -281,10 +298,65 @@ def recompute(directory, execution, collector, standard, diag, parallel=False):
|
||||
result = eq.compare_reports(joined[role + '-A'], joined[role + '-B'])
|
||||
require(result['ok'] and result['differences'] == [], 'same-role determinism failed: ' + role)
|
||||
comparisons[role] = result
|
||||
return wire({'phases': results, 'comparisons': comparisons, 'measured_source': 'S_eq-native-not-Q',
|
||||
return wire({'phases': results, 'comparisons': comparisons, 'measured_source': admission['measured_source'],
|
||||
'version': closure['version'], 'registered_changes': closure['allowed_changed'],
|
||||
'candidate_Q_measured': False, 'original_credit': 0, 'formal_acceptance': False})
|
||||
|
||||
|
||||
VERSION_PINS = ('dependencies', 'frozen', 'worker', 'fixture', 'engine', 'engine_version', 'readonly',
|
||||
'original_manifest', 'machine', 'platform')
|
||||
|
||||
|
||||
def existing_defects(directory, phase):
|
||||
"""Scenario outcomes the stored checker recorded as existing product defects."""
|
||||
checker = read(Path(directory) / phase / 'checker.json')
|
||||
require(checker['ok'] is True, 'stored checker failed: ' + phase)
|
||||
return [{'scenario': row['id'], 'status': row['status'], 'kinds': [defect['kind'] for defect in row['defects']]}
|
||||
for key in ('e3', 'e45') for row in checker[key] if row['status'].startswith('existing-defect')]
|
||||
|
||||
|
||||
def compare_bundles(before, after):
|
||||
"""S6 cross-version comparison of two sealed EQ bundles (SPEC §2/§3.2, OPT_CONTRACT_T1 §2-§4).
|
||||
|
||||
Each bundle is proven by its own version's native-verify (raw recomputation, same-role
|
||||
determinism, checkout pins). Here both seals, versions and source closures are checked
|
||||
again, every non-product pin must be equal (the EQ, DIAG, collector and RESTART bytes,
|
||||
fixture, engine and inputs), and the sealed joined reports of the A phases are compared
|
||||
by compare_reports. Differences, exclusions and existing defects are all returned."""
|
||||
before, after = Path(before).resolve(), Path(after).resolve()
|
||||
require(before != after, 'two different bundles required')
|
||||
admissions = {}
|
||||
for version, directory in (('before', before), ('after', after)):
|
||||
check_seal(directory)
|
||||
execution = read(directory / 'execution.json')
|
||||
require(execution['passed'] is True and execution['error'] is None, version + ' native failed')
|
||||
admission = execution['admission']
|
||||
require(read(directory / 'admission.json') == admission, version + ' admission copy')
|
||||
closure = eq.admit_source(admission['source_map'], admission['pins']['registered_changes'])
|
||||
require(closure == admission['pins']['source_closure'] and closure['version'] == version == admission['version'] and
|
||||
admission['measured_source'] == MEASURED[version] and admission['candidate_Q_measured'] is False,
|
||||
version + ' bundle version/closure')
|
||||
admissions[version] = admission
|
||||
for key in VERSION_PINS:
|
||||
require(admissions['before']['pins'][key] == admissions['after']['pins'][key], 'non-product pin differs between versions: ' + key)
|
||||
comparisons, reports = {}, {}
|
||||
for role in ('R', 'O'):
|
||||
phase = role + '-A'
|
||||
for version, directory in (('before', before), ('after', after)):
|
||||
eq.add_blob_root(directory / phase / 'evidence' / 'blobs')
|
||||
reports[version] = read(directory / phase / 'joined.json')
|
||||
eq.ACTIVE_PROFILES[:] = [reports['before']['profile_root'], reports['after']['profile_root']]
|
||||
comparisons[role] = eq.compare_reports(reports['before'], reports['after'])
|
||||
defects = {version: {role: existing_defects(directory, role + '-A') for role in ('R', 'O')}
|
||||
for version, directory in (('before', before), ('after', after))}
|
||||
return wire({'ok': all(c['ok'] and c['differences'] == [] for c in comparisons.values()),
|
||||
'versions': {version: {'commit': a['commit'], 'measured_source': a['measured_source'],
|
||||
'source_closure': a['pins']['source_closure']} for version, a in admissions.items()},
|
||||
'comparisons': comparisons, 'existing_defects': defects,
|
||||
'after_all_e_pass': not any(defects['after'].values()),
|
||||
'formal_acceptance': False, 'original_credit': 0})
|
||||
|
||||
|
||||
def verify(project_root, directory):
|
||||
from run_eq import load_dependencies, actual_pins
|
||||
project_root, directory = Path(project_root).resolve(), Path(directory).resolve()
|
||||
@@ -297,7 +369,8 @@ def verify(project_root, directory):
|
||||
# against them by run_eq before any phase.
|
||||
inputs = directory / 'inputs'
|
||||
require(actual_pins(project_root, Path(admission['readonly_path']), inputs / 'fixture' / Path(admission['fixture']).name,
|
||||
Path(admission['engine']), collector, standard, copies=inputs / 'readonly') == admission['pins'],
|
||||
Path(admission['engine']), collector, standard, copies=inputs / 'readonly',
|
||||
registered=admission['pins']['registered_changes']) == admission['pins'],
|
||||
'actual input/source/EQ pins differ')
|
||||
result = recompute(directory, execution, collector, standard, diag, parallel=True)
|
||||
require(result == read(directory / 'results.json'), 'recomputed results differ')
|
||||
@@ -310,6 +383,20 @@ def verify(project_root, directory):
|
||||
def main():
|
||||
if sys.argv[1:] == ['--phase-worker']:
|
||||
return phase_worker()
|
||||
if sys.argv[1:2] == ['--compare-versions']:
|
||||
parser = argparse.ArgumentParser(description=compare_bundles.__doc__)
|
||||
parser.add_argument('--compare-versions', action='store_true', required=True)
|
||||
parser.add_argument('--before', type=Path, required=True)
|
||||
parser.add_argument('--after', type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
with check.memo_scope():
|
||||
result = compare_bundles(args.before, args.after)
|
||||
print(json.dumps({'status': 'compared', 'result': result}, ensure_ascii=False, allow_nan=False))
|
||||
return 0 if result['ok'] else 1
|
||||
except (ValueError, OSError, KeyError, TypeError, IndexError) as error:
|
||||
print(json.dumps({'status': 'failed', 'error': str(error), 'formal_acceptance': False}))
|
||||
return 1
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--project-root', type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
|
||||
Reference in New Issue
Block a user