From 4d893ed3c2cd169350ba81c79a84c7f013430156 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 13 Sep 2026 17:30:23 +0200 Subject: [PATCH] Verify factor identity again after recovery mutation Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c --- scripts/factor_recovery.py | 2 +- scripts/test_factor_recovery.py | 8 ++++++++ workplans/KEY-WP-0036-platform-factor-recovery.md | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/scripts/factor_recovery.py b/scripts/factor_recovery.py index 502d613..3fd7630 100644 --- a/scripts/factor_recovery.py +++ b/scripts/factor_recovery.py @@ -35,7 +35,7 @@ def recover(store, request): store.record(base,expected,False) # An audit outage prevents mutation. store.disable(request['user'],request['serial'],expected) current=store.snapshot(request['user'],request['serial']) - if current is None or current['active']:raise RecoveryError('readback_failed') + if current is None or current['active'] or fingerprint(dict(current,active=True))!=expected:raise RecoveryError('readback_failed') store.record(base,expected,True) return dict(base,status='recovered',changes_applied=True,replayed=False) diff --git a/scripts/test_factor_recovery.py b/scripts/test_factor_recovery.py index a22181a..25d961f 100644 --- a/scripts/test_factor_recovery.py +++ b/scripts/test_factor_recovery.py @@ -45,3 +45,11 @@ class RecoveryTests(unittest.TestCase): def test_replaced_inactive_factor_cannot_replay_old_recovery(self): r=self.approval();recover(self.store,r);self.store.state['id']=2 with self.assertRaises(RecoveryError):recover(self.store,r) + + def test_replacement_after_disable_cannot_report_success(self): + original=self.store.disable + def replace_after_disable(*args): + original(*args);self.store.state['id']=2 + self.store.disable=replace_after_disable + with self.assertRaisesRegex(RecoveryError,'readback_failed'):recover(self.store,self.approval()) + self.assertFalse(self.store.rows['support-1']['complete']) diff --git a/workplans/KEY-WP-0036-platform-factor-recovery.md b/workplans/KEY-WP-0036-platform-factor-recovery.md index 41511c2..b77b9f1 100644 --- a/workplans/KEY-WP-0036-platform-factor-recovery.md +++ b/workplans/KEY-WP-0036-platform-factor-recovery.md @@ -29,7 +29,7 @@ state_hub_task_id: "f3287544-8412-5b1f-9d16-9f7b9e895ceb" requires an operator verification attestation and matching metadata version, disables one factor with a row lock, requires pre-mutation audit durability, reads back state, records completion and reconciles retries. Reject changed or -replaced factors and reference conflicts. Eight isolated unit tests pass. +replaced factors and reference conflicts. Nine isolated unit tests pass, including replacement between disable and readback. The installed-provider Job provider-recovery-contract-02 passed real database and audit persistence, disable/readback and replay assertions in an isolated fixture. Acceptance CI includes both suites. No real user's factor was changed.