orchestrator/sm: commit SVN floor on proven boot, not activation

Add BootConfirmed event + CommitSvnFloor effect; Ready emits the commit
only on a proven-healthy boot, decoupling the anti-rollback floor advance
from ActivateUpdate. Conforms to CSA resiliency: the floor advances after
a higher-SVN image successfully boots and is deemed stable.
diff --git a/services/orchestrator/sm/src/lib.rs b/services/orchestrator/sm/src/lib.rs
index bb9fc22..d2265cf 100644
--- a/services/orchestrator/sm/src/lib.rs
+++ b/services/orchestrator/sm/src/lib.rs
@@ -486,6 +486,14 @@
 
             State::Ready => match event {
                 Event::UpdateRequest => Outcome::Transition(State::Updating),
+                // Proven-boot checkpoint: the image authenticated at
+                // `ActivateUpdate`, but the SVN floor only advances now, once
+                // the shell reports it healthy. Handled in place — confirming a
+                // running image is not a state change.
+                Event::BootConfirmed(id) => {
+                    ctx.emit(Effect::CommitSvnFloor(*id));
+                    Outcome::Handled
+                }
                 _ => Outcome::Super,
             },
 
diff --git a/services/orchestrator/sm/src/model.rs b/services/orchestrator/sm/src/model.rs
index 0bf3d86..d13891c 100644
--- a/services/orchestrator/sm/src/model.rs
+++ b/services/orchestrator/sm/src/model.rs
@@ -195,6 +195,11 @@
     UpdateVerified,
     /// The staged update failed authentication.
     UpdateRejected,
+    /// The activated image proved itself healthy at runtime (supervised
+    /// health / attestation pass — not mere boot). Gates the anti-rollback
+    /// commit: only now is it safe to advance the SVN floor past this image,
+    /// because it has demonstrated it runs, not merely that it authenticated.
+    BootConfirmed(ComponentId),
     /// This component was found corrupt at runtime.
     CorruptionDetected(ComponentId),
     /// This component's golden image has been restored.
@@ -234,6 +239,13 @@
     StageUpdate,
     ActivateUpdate,
     DiscardStaged,
+    /// Advance the anti-rollback (SVN) floor past `id`'s now-confirmed image.
+    /// Deliberately decoupled from [`ActivateUpdate`]: activation happens on
+    /// authentication (`UpdateVerified`), but the floor may only move once the
+    /// image has proven healthy ([`Event::BootConfirmed`]). Committing the
+    /// floor earlier would burn anti-rollback on an image that authenticated
+    /// but has not yet demonstrated it can boot and run.
+    CommitSvnFloor(ComponentId),
     RestoreGoldenImage(ComponentId),
     /// Report that a component has been isolated (held in reset and removed
     /// from the trust chain) so management software is aware the platform is
diff --git a/services/orchestrator/sm/src/tests.rs b/services/orchestrator/sm/src/tests.rs
index 5055b0a..f41979a 100644
--- a/services/orchestrator/sm/src/tests.rs
+++ b/services/orchestrator/sm/src/tests.rs
@@ -1025,6 +1025,42 @@
     assert!(!effects.contains(&Effect::RestoreGoldenImage(C0)));
 }
 
+/// The anti-rollback floor is committed only on a proven-healthy boot, never
+/// at activation. `UpdateVerified` activates the image (authentication) but
+/// must NOT emit `CommitSvnFloor`; a later `BootConfirmed` (the runtime health
+/// proof) is what advances the floor. This pins the decoupling so activation
+/// can never silently commit the floor early.
+#[test]
+fn svn_floor_commits_on_boot_confirmed_not_on_activation() {
+    // Activation alone: no floor commit yet.
+    let (activated, activated_state) = drive(
+        passive_required(&[C0]),
+        &[
+            BOOT,
+            Event::VerificationPassed(C0),
+            Event::UpdateRequest,
+            Event::UpdateVerified,
+        ],
+    );
+    assert_eq!(activated_state, State::Ready);
+    assert!(activated.contains(&Effect::ActivateUpdate));
+    assert!(!activated.contains(&Effect::CommitSvnFloor(C0)));
+
+    // Proven-healthy boot: the floor advances now, without leaving Ready.
+    let (confirmed, confirmed_state) = drive(
+        passive_required(&[C0]),
+        &[
+            BOOT,
+            Event::VerificationPassed(C0),
+            Event::UpdateRequest,
+            Event::UpdateVerified,
+            Event::BootConfirmed(C0),
+        ],
+    );
+    assert_eq!(confirmed_state, State::Ready);
+    assert!(confirmed.contains(&Effect::CommitSvnFloor(C0)));
+}
+
 /// Locked is a terminal state: no effects are produced in response to any
 /// event after the machine latches.
 #[test]