diff --git a/evidence/v02-workload-a3-continuity-20261012/README.md b/evidence/v02-workload-a3-continuity-20261012/README.md new file mode 100644 index 0000000..19ce67f --- /dev/null +++ b/evidence/v02-workload-a3-continuity-20261012/README.md @@ -0,0 +1,73 @@ +# A3 reference continuity during workload migration — Staged + +This narrow follow-up retains the historical/current workload migration already +published on PR #497 at `25affa78bcbf16e89667cbe3cbcfbc3a7fa05006`. It protects +the independently pinned A3 source-identity reference through the complete +two-report check. It provides no new source authentication or reconciliation +credit and closes no v0.2 acceptance gate. + +## Concurrent repair and preservation + +The initial PR #503 implementation was published at +`f266c4d8fa3a1d81292c57556bfdd3f968552423`, reproducing the exact approved local +tree `28e707ffb971c5a6f3f25d0730e5ac742ed0589d`. While it was being published, +another session advanced PR #497 with an independently implemented repair. + +Both implementations produced byte-identical current inventories with SHA-256 +`ed4ee7e8be9a5369d4651769dc8717456473063d2db21010544349a9848b8a00`: 61,941 claims +in 655 documents, preserving the historical 61,936 claims in 654 documents. +Each required exactly the approved five successors and awarded zero new credit. + +This follow-up keeps #497's implementation, CLI, workflow, transition contract, +canonical current inventory and evidence. The alternate implementation is +preserved in Git history at the initial #503 revision; the integration retains +both published histories. The final working tree has one production checker and +one canonical current report at `evidence/claim-workload-v02-inventory.json`. +No colliding dated evidence file is overwritten. + +## Confirmed gap and change + +The preserved report's provenance checker validates the A3 reference during the +historical replay. The surrounding transition checker snapshots the other +inputs across both historical and current replays, but it omitted this reference +from its final stability checks. Changing its bytes or replacing it with an +identical-byte symlink after either replay could therefore still return MATCH. + +After the historical replay has validated the provenance and reference shape, +the transition checker now resolves that same reference, rejects symlinks, and +checks its bytes against the just-validated pinned digest before the current +replay. At the final boundary it requires the same canonical path and identical +bytes. The initial digest comparison also closes the interval between the +historical replay's return and the new snapshot. Existing authority constants, +transition/report/provenance bytes, claim decisions and public CLI remain intact. + +## Observed regression + +Two tests cover four cases: byte mutation and identical-byte symlink replacement, +each immediately after the preserved replay and after the current replay. +At 19:47:28 UTC, all four cases exposed the gap: `ValueError not raised`. After +the production change, both tests and all four cases passed at 19:47:49 UTC. + +[regression-observation.json](regression-observation.json) preserves the exact +command, UTC times, return codes, durations, log digests and complete before/after +output. [independent-review.json](independent-review.json) records the separate +current reviewer's findings and reviewed file hashes. The final PR body records +the actual published integration head, complete bounded suite and workflow +results. Those results remain branch checks, not merged-main acceptance. + +## Continuing prerequisites + +Native admission was still denied at 19:36:09 UTC for swap-fraction, +disk-throughput and vitals-shed. This session acquired no native execution/heavy +lease and launched no full source replay. The exact original artifact metadata +was located by bounded hashing in the earlier +[native observation](https://github.com/4444J99/github-engineering-standards/blob/f266c4d8fa3a1d81292c57556bfdd3f968552423/evidence/v02-workload-migration-20261012/native-prerequisites.json); +that does not authenticate all source snapshots or rendered bodies. + +The 474 frozen workload receipt subjects remain distinct from the later 2,164 +validated dispositions. The expected 2,174 validated / 59,767 unresolved result +is still conditional. Replacement envelopes need authentic scoped actors and +independent comparison of all carried decisions plus ten new dispositions. +Public pilot evidence, retained-body assurance, adapters/synthesis, publication +clearance, human review, owner acceptance and merged-main checks remain open. +Private support remains unverified. diff --git a/evidence/v02-workload-a3-continuity-20261012/independent-review.json b/evidence/v02-workload-a3-continuity-20261012/independent-review.json new file mode 100644 index 0000000..9ac8f1d --- /dev/null +++ b/evidence/v02-workload-a3-continuity-20261012/independent-review.json @@ -0,0 +1,113 @@ +{ + "schema": "ges.independent-a3-continuity-review.v1", + "reviewer_identity": "/root/migration_review", + "reviewer_kind": "Current independent automated code-review helper", + "observed_at": "2026-10-12 19:49:07 UTC", + "result": "PASS_WITHIN_SCOPE", + "base_head": "25affa78bcbf16e89667cbe3cbcfbc3a7fa05006", + "reviewed_change": "Thirteen production lines retain the A3 source-identity reference across preserved/current workload replay; two tests exercise changed bytes and identical-byte symlink replacement after each replay.", + "reviewed_file_sha256": { + "ges/claim_workload_migration.py": "d1be041a2742e902e1a93d05235b7a21c642cebe8f4ea730248c947ddf5aecda", + "tests/test_claim_workload_migration.py": "8b5560a7c8814d8f78d3e4f0b6f6faa1a10be97451a071b6a28eae623f16955e" + }, + "reviewed_preserved_files": { + "ges/claim_workload.py": "592c7027c7a876d28f5224e231873556de3f29e6a664f8609f31eda6c623315e", + ".github/workflows/ci.yml": "4bbc3e00e820129fa1c33082d56b6862d2edd4cefb8e169d5111e0c01e35e920", + "evidence/claim-workload-v02-transition.json": "f92cbe31c618e793b9bdc07f51b609d0b464fa5f8698fe715efc6bb542089dd8", + "evidence/claim-workload-v02-inventory.json": "ed4ee7e8be9a5369d4651769dc8717456473063d2db21010544349a9848b8a00", + "evidence/claim-workload-current-provenance.json": "239fc3e89643202c9a7935749249eb4a0ca14e67a32c6e2a0517166a81e9bbf8" + }, + "observations": [ + "The source reference comes from frozen provenance after the preserved replay has validated its identity, canonical locator, and independent A3 authority.", + "The immediate reference SHA check rejects a mutation between preserved-replay completion and the new snapshot; it does not merely preserve possibly changed bytes.", + "Final canonical-path and byte checks cover changes after current replay, including an identical-byte symlink replacement.", + "The two regression tests each cover mutations after preserved replay and after current replay, for four independently failing pre-fix subcases.", + "No transition contract, report, provenance, public API, default inventory behavior, or CI workflow was changed relative to the target's existing implementation.", + "The target's existing implementation and evidence are retained; this is a narrow continuity fix rather than the earlier alternative migration implementation." + ], + "findings": [ + { + "id": "A3-CONTINUITY-01", + "severity": "MEDIUM", + "status": "RESOLVED_IN_REVIEWED_BYTES", + "defect": "The target implementation checked A3 reference integrity inside preserved replay but did not retain that integrity through the combined transition.", + "resolution": "Verify and retain the trusted reference immediately after preserved replay; recheck canonical path and exact bytes before returning MATCH." + } + ], + "reviewed_integrator_validation": { + "actual_executor": "Root integrator; not independently re-executed by this reviewer", + "command_argv": [ + "/opt/codex/runtimes/codex-primary-runtime/dependencies/python/bin/python", + "-m", + "unittest", + "discover", + "-s", + "tests", + "-p", + "test_claim_workload_migration.py", + "-k", + "source_identity", + "-v" + ], + "cwd": "/workspace/scratch/9f3cdabec13b/ges", + "before": { + "started_at": "2026-10-12T19:47:28.107165+00:00", + "duration_seconds": 0.108, + "exit_code": 1, + "tests": 2, + "failing_subcases": 4, + "observed_failure": "AssertionError: ValueError not raised", + "metadata_path": ".cache/continuation-20261012/a3-guard-before.json", + "log_path": ".cache/continuation-20261012/a3-guard-before.log", + "log_sha256": "7be72ddb5d9a595ff7e0b0bf1ad39a437437802a4c4e9df74ab4c5c1323f25b7" + }, + "after": { + "started_at": "2026-10-12T19:47:49.377687+00:00", + "duration_seconds": 0.096, + "exit_code": 0, + "tests": 2, + "failing_subcases": 0, + "observed_result": "OK", + "metadata_path": ".cache/continuation-20261012/a3-guard-after.json", + "log_path": ".cache/continuation-20261012/a3-guard-after.log", + "log_sha256": "c11cafe9a5caf1518ffd0d26f72cef73580e1b584dccc5333056550f141145c8" + }, + "complete_stdout_stderr_reviewed": true, + "recorded_log_sha256_independently_checked": true + }, + "review_commands": [ + [ + "git", + "diff", + "25affa78bcbf16e89667cbe3cbcfbc3a7fa05006", + "--", + "ges/claim_workload_migration.py", + "tests/test_claim_workload_migration.py" + ], + [ + "git", + "diff", + "--stat", + "25affa78bcbf16e89667cbe3cbcfbc3a7fa05006" + ], + [ + "cat", + ".cache/continuation-20261012/a3-guard-before.json", + ".cache/continuation-20261012/a3-guard-before.log", + ".cache/continuation-20261012/a3-guard-after.json", + ".cache/continuation-20261012/a3-guard-after.log" + ] + ], + "open_findings": [], + "limitations": [ + "This observation is an automated software review at the listed file hashes, not original semantic-envelope authority, a human approval, or release acceptance.", + "The reviewer inspected complete red/green records and independently checked their log hashes, but did not execute those tests or a full suite.", + "No authenticated source replay, native-host action, GitHub write, workflow dispatch/cancel, replacement envelope, or acceptance credit was performed by this reviewer.", + "Before/after observations do not provide an operating-system atomic snapshot or detect adversarial change-and-restore between reads.", + "Later code, contract, evidence, or workflow changes require the corresponding review and validation." + ], + "new_semantic_review_credit": 0, + "new_reconciliation_credit": 0, + "human_approval": false, + "release_acceptance": false +} diff --git a/evidence/v02-workload-a3-continuity-20261012/precommit-verification.json b/evidence/v02-workload-a3-continuity-20261012/precommit-verification.json new file mode 100644 index 0000000..d7ee552 --- /dev/null +++ b/evidence/v02-workload-a3-continuity-20261012/precommit-verification.json @@ -0,0 +1,140 @@ +{ + "base_commit": "25affa78bcbf16e89667cbe3cbcfbc3a7fa05006", + "context": "Precommit verification of narrow A3 continuity integration", + "commands": [ + { + "name": "unit", + "argv": [ + "/opt/codex/runtimes/codex-primary-runtime/dependencies/python/bin/python", + "-m", + "unittest", + "discover", + "-s", + "tests", + "-v" + ], + "started_at": "2026-10-12T19:49:41.164689+00:00", + "duration_seconds": 14.507, + "exit_code": 0, + "log_sha256": "24cf88738592122a9335e87d8fe4a42d036dc558c4c1d3008a846cc028d1a012" + }, + { + "name": "historical-workload", + "argv": [ + "/opt/codex/runtimes/codex-primary-runtime/dependencies/python/bin/python", + "-m", + "ges.claim_workload", + "--reviews", + "evidence/source-reviews", + "--artifacts", + "evidence/claim-workload-inputs/artifacts.jsonl.gz", + "--reconciliation", + "evidence/provider-oidc-reconciliation.json", + "--check", + "evidence/claim-workload-current-inventory.json", + "--historical", + "evidence/claim-workload-inventory.json", + "--provenance", + "evidence/claim-workload-current-provenance.json", + "--recorded-membership" + ], + "started_at": "2026-10-12T19:49:55.672221+00:00", + "duration_seconds": 2.019, + "exit_code": 0, + "log_sha256": "a28ed993d93829b7635e063421e5a1bd03a88460e227014823e9f6374f71415e" + }, + { + "name": "current-transition", + "argv": [ + "/opt/codex/runtimes/codex-primary-runtime/dependencies/python/bin/python", + "-m", + "ges.claim_workload_migration", + "--reviews", + "evidence/source-reviews", + "--artifacts", + "evidence/claim-workload-inputs/artifacts.jsonl.gz", + "--reconciliation", + "evidence/provider-oidc-reconciliation.json", + "--baseline", + "evidence/claim-workload-current-inventory.json", + "--historical", + "evidence/claim-workload-inventory.json", + "--provenance", + "evidence/claim-workload-current-provenance.json", + "--transition", + "evidence/claim-workload-v02-transition.json", + "--check", + "evidence/claim-workload-v02-inventory.json" + ], + "started_at": "2026-10-12T19:49:57.691076+00:00", + "duration_seconds": 3.623, + "exit_code": 0, + "log_sha256": "a78a7f73953afbaa804900c81059d77b3c84be65b6b69cc8702ad6771464a8d3" + }, + { + "name": "validate", + "argv": [ + "/opt/codex/runtimes/codex-primary-runtime/dependencies/python/bin/python", + "-m", + "ges", + "validate" + ], + "started_at": "2026-10-12T19:50:01.314126+00:00", + "duration_seconds": 0.064, + "exit_code": 0, + "log_sha256": "74bb4525b9b900006126e2c8ea295e29f1e8b454357085d3458009791496a535" + }, + { + "name": "compile", + "argv": [ + "/opt/codex/runtimes/codex-primary-runtime/dependencies/python/bin/python", + "-m", + "ges", + "compile" + ], + "started_at": "2026-10-12T19:50:01.378476+00:00", + "duration_seconds": 0.064, + "exit_code": 0, + "log_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "name": "generated", + "argv": [ + "git", + "diff", + "--exit-code", + "--", + "generated/functional-checklist.md", + "generated/source-crosswalk.md", + "generated/bindings.json" + ], + "started_at": "2026-10-12T19:50:01.442445+00:00", + "duration_seconds": 0.032, + "exit_code": 0, + "log_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "name": "whitespace", + "argv": [ + "git", + "diff", + "--check" + ], + "started_at": "2026-10-12T19:50:01.474498+00:00", + "duration_seconds": 0.008, + "exit_code": 0, + "log_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + ], + "tests_run": 707, + "unit_summary": [ + "Ran 707 tests in 14.324s", + "", + "OK" + ], + "all_passed": true, + "reviewed_files": { + "ges/claim_workload_migration.py": "d1be041a2742e902e1a93d05235b7a21c642cebe8f4ea730248c947ddf5aecda", + "tests/test_claim_workload_migration.py": "8b5560a7c8814d8f78d3e4f0b6f6faa1a10be97451a071b6a28eae623f16955e" + } +} diff --git a/evidence/v02-workload-a3-continuity-20261012/regression-observation.json b/evidence/v02-workload-a3-continuity-20261012/regression-observation.json new file mode 100644 index 0000000..fa68065 --- /dev/null +++ b/evidence/v02-workload-a3-continuity-20261012/regression-observation.json @@ -0,0 +1,57 @@ +{ + "schema": "ges.workload-a3-continuity-regression.v1", + "execution_actor": "current ChatGPT /root in workspace 9f3cdabec13b", + "base_commit": "25affa78bcbf16e89667cbe3cbcfbc3a7fa05006", + "scope": "Synthetic metadata stability regression; no source authentication, reconciliation authority or acceptance credit.", + "tests": 2, + "subcases": [ + "changed reference bytes after preserved replay", + "changed reference bytes after current replay", + "identical-byte symlink after preserved replay", + "identical-byte symlink after current replay" + ], + "before": { + "phase": "before production fix", + "base": "25affa78bcbf16e89667cbe3cbcfbc3a7fa05006", + "argv": [ + "/opt/codex/runtimes/codex-primary-runtime/dependencies/python/bin/python", + "-m", + "unittest", + "discover", + "-s", + "tests", + "-p", + "test_claim_workload_migration.py", + "-k", + "source_identity", + "-v" + ], + "started_at": "2026-10-12T19:47:28.107165+00:00", + "duration_seconds": 0.108, + "exit_code": 1, + "log_sha256": "7be72ddb5d9a595ff7e0b0bf1ad39a437437802a4c4e9df74ab4c5c1323f25b7", + "complete_log": "test_source_identity_reference_changes_after_either_replay_are_rejected (test_claim_workload_migration.WorkloadMigration.test_source_identity_reference_changes_after_either_replay_are_rejected) ... \n test_source_identity_reference_changes_after_either_replay_are_rejected (test_claim_workload_migration.WorkloadMigration.test_source_identity_reference_changes_after_either_replay_are_rejected) (after='current.json') ... FAIL\n test_source_identity_reference_changes_after_either_replay_are_rejected (test_claim_workload_migration.WorkloadMigration.test_source_identity_reference_changes_after_either_replay_are_rejected) (after='new-current.json') ... FAIL\ntest_source_identity_reference_symlinks_after_either_replay_are_rejected (test_claim_workload_migration.WorkloadMigration.test_source_identity_reference_symlinks_after_either_replay_are_rejected) ... \n test_source_identity_reference_symlinks_after_either_replay_are_rejected (test_claim_workload_migration.WorkloadMigration.test_source_identity_reference_symlinks_after_either_replay_are_rejected) (after='current.json') ... FAIL\n test_source_identity_reference_symlinks_after_either_replay_are_rejected (test_claim_workload_migration.WorkloadMigration.test_source_identity_reference_symlinks_after_either_replay_are_rejected) (after='new-current.json') ... FAIL\n\n======================================================================\nFAIL: test_source_identity_reference_changes_after_either_replay_are_rejected (test_claim_workload_migration.WorkloadMigration.test_source_identity_reference_changes_after_either_replay_are_rejected) (after='current.json')\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/workspace/scratch/9f3cdabec13b/ges/tests/test_claim_workload_migration.py\", line 219, in test_source_identity_reference_changes_after_either_replay_are_rejected\n with self.assertRaisesRegex(ValueError,\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: ValueError not raised\n\n======================================================================\nFAIL: test_source_identity_reference_changes_after_either_replay_are_rejected (test_claim_workload_migration.WorkloadMigration.test_source_identity_reference_changes_after_either_replay_are_rejected) (after='new-current.json')\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/workspace/scratch/9f3cdabec13b/ges/tests/test_claim_workload_migration.py\", line 219, in test_source_identity_reference_changes_after_either_replay_are_rejected\n with self.assertRaisesRegex(ValueError,\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: ValueError not raised\n\n======================================================================\nFAIL: test_source_identity_reference_symlinks_after_either_replay_are_rejected (test_claim_workload_migration.WorkloadMigration.test_source_identity_reference_symlinks_after_either_replay_are_rejected) (after='current.json')\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/workspace/scratch/9f3cdabec13b/ges/tests/test_claim_workload_migration.py\", line 242, in test_source_identity_reference_symlinks_after_either_replay_are_rejected\n with self.assertRaisesRegex(ValueError,\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: ValueError not raised\n\n======================================================================\nFAIL: test_source_identity_reference_symlinks_after_either_replay_are_rejected (test_claim_workload_migration.WorkloadMigration.test_source_identity_reference_symlinks_after_either_replay_are_rejected) (after='new-current.json')\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/workspace/scratch/9f3cdabec13b/ges/tests/test_claim_workload_migration.py\", line 242, in test_source_identity_reference_symlinks_after_either_replay_are_rejected\n with self.assertRaisesRegex(ValueError,\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: ValueError not raised\n\n----------------------------------------------------------------------\nRan 2 tests in 0.019s\n\nFAILED (failures=4)\n" + }, + "after": { + "phase": "after production fix", + "base": "25affa78bcbf16e89667cbe3cbcfbc3a7fa05006", + "argv": [ + "/opt/codex/runtimes/codex-primary-runtime/dependencies/python/bin/python", + "-m", + "unittest", + "discover", + "-s", + "tests", + "-p", + "test_claim_workload_migration.py", + "-k", + "source_identity", + "-v" + ], + "started_at": "2026-10-12T19:47:49.377687+00:00", + "duration_seconds": 0.096, + "exit_code": 0, + "log_sha256": "c11cafe9a5caf1518ffd0d26f72cef73580e1b584dccc5333056550f141145c8", + "complete_log": "test_source_identity_reference_changes_after_either_replay_are_rejected (test_claim_workload_migration.WorkloadMigration.test_source_identity_reference_changes_after_either_replay_are_rejected) ... ok\ntest_source_identity_reference_symlinks_after_either_replay_are_rejected (test_claim_workload_migration.WorkloadMigration.test_source_identity_reference_symlinks_after_either_replay_are_rejected) ... ok\n\n----------------------------------------------------------------------\nRan 2 tests in 0.015s\n\nOK\n" + } +} diff --git a/ges/claim_workload_migration.py b/ges/claim_workload_migration.py index bb0e909..df3b298 100644 --- a/ges/claim_workload_migration.py +++ b/ges/claim_workload_migration.py @@ -63,6 +63,15 @@ def check_transition(reviews: Path, artifacts: Path, reconciliation: Path, old = check_inventory(reviews, artifacts, reconciliation, baseline, historical=historical, provenance=provenance, recorded_membership=True) + # The old replay validates this pinned sibling reference. Keep that same + # authority stable through the current replay and the outer final checks. + reference = json.loads(before['provenance'])['source_identity_reference'] + reference_input = provenance.parent / reference['path'] + reference_path = canonical_path( + reference_input, symlink_error='Symlink source identity reference is not permitted') + reference_raw = reference_path.read_bytes() + if _sha(reference_raw) != reference['sha256']: + raise ValueError('Source identity reference changed during transition check') new = check_inventory(reviews, artifacts, reconciliation, current) old_documents = old['inputs']['claim_documents'] if added['path'] in {item['path'] for item in old_documents}: @@ -121,6 +130,10 @@ def check_transition(reviews: Path, artifacts: Path, reconciliation: Path, canonical_path(path, symlink_error='Symlink workload transition input is not permitted') if path.read_bytes() != before[label]: raise ValueError('Workload transition input changed during check: ' + label) + if (canonical_path(reference_input, + symlink_error='Symlink source identity reference is not permitted') != + reference_path or reference_path.read_bytes() != reference_raw): + raise ValueError('Source identity reference changed during transition check') if (canonical_path(transition, symlink_error='Symlink workload transition is not permitted') != transition_path or transition_path.read_bytes() != transition_raw): raise ValueError('Workload transition contract changed during check') diff --git a/tests/test_claim_workload_migration.py b/tests/test_claim_workload_migration.py index 8a54a92..a3fa882 100644 --- a/tests/test_claim_workload_migration.py +++ b/tests/test_claim_workload_migration.py @@ -202,6 +202,50 @@ def add_after_check(*args, **kwargs): with self.assertRaisesRegex(ValueError, 'membership changed during transition check'): self.check() + def test_source_identity_reference_changes_after_either_replay_are_rejected(self): + identity = json.loads(self.provenance.read_text())['source_identity_reference'] + reference = self.provenance.parent / identity['path'] + original = reference.read_bytes() + for replay in (self.baseline, self.current): + with self.subTest(after=replay.name): + def change_after_check(*args, **kwargs): + result = check_inventory(*args, **kwargs) + if args[3] == replay: + reference.write_bytes(original + b'\n') + return result + try: + with patch('ges.claim_workload_migration.check_inventory', + side_effect=change_after_check): + with self.assertRaisesRegex(ValueError, + 'Source identity reference changed'): + self.check() + finally: + reference.write_bytes(original) + + def test_source_identity_reference_symlinks_after_either_replay_are_rejected(self): + identity = json.loads(self.provenance.read_text())['source_identity_reference'] + reference = self.provenance.parent / identity['path'] + original = reference.read_bytes() + target = self.fixture.root / 'identical-reference.json' + target.write_bytes(original) + for replay in (self.baseline, self.current): + with self.subTest(after=replay.name): + def link_after_check(*args, **kwargs): + result = check_inventory(*args, **kwargs) + if args[3] == replay: + reference.unlink() + reference.symlink_to(target) + return result + try: + with patch('ges.claim_workload_migration.check_inventory', + side_effect=link_after_check): + with self.assertRaisesRegex(ValueError, + 'Symlink source identity reference'): + self.check() + finally: + reference.unlink(missing_ok=True) + reference.write_bytes(original) + def test_transition_and_bound_input_changes_during_check_are_rejected(self): real_read = Path.read_bytes for target in (self.transition, *self.inputs.values()):