re-walk of the chain explained
diff --git a/docs/src/design/orchestrator/orchestrator-machine.md b/docs/src/design/orchestrator/orchestrator-machine.md
index dc679b4..0671828 100644
--- a/docs/src/design/orchestrator/orchestrator-machine.md
+++ b/docs/src/design/orchestrator/orchestrator-machine.md
@@ -188,6 +188,18 @@
 produces the event internally, the orchestrator intercepts and re-dispatches it
 before returning, and the decision is visible in the effect trace.
 
+**Why re-walk from `cursor = 0`?** After restoring a component the machine
+re-enters `VerifyingPlatform` and re-verifies the entire chain from scratch
+rather than resuming at the failed component. This is a deliberate conservative
+policy: a corruption event may indicate a broader integrity problem, and the
+CSA architecture's core principle — "no component executes unverified firmware"
+(NIST SP 800-193) — requires that trust be re-established end-to-end before the
+platform is considered healthy again. The CSA document does not prescribe the
+exact recovery sequencing, but the re-walk implements the spirit of that
+principle. Optional components that fail during the re-walk are skipped (held in
+reset) as during initial boot; they are re-released only if they pass
+`VerificationPassed` in the new walk.
+
 ---
 
 ### `Locked`
@@ -207,8 +219,9 @@
 | Event | Effects | Next state |
 |---|---|---|
 | `AttestationChallenge` | `SignAttestation` | `Handled` (no transition — INV6) |
-| `CorruptionDetected(id)` | — | `Recovering` (failed = Some(id) — INV5) |
-| anything else | — | `Outcome::Super` (discarded) |
+| `CorruptionDetected(id)` | `attrs.required == true` | — | `Recovering` (failed = Some(id) — INV5) |
+| `CorruptionDetected(id)` | `attrs.required == false` | `AssertReset(id)` | `Handled` (component gated; machine stays in current state) |
+| anything else | — | — | `Outcome::Super` (discarded) |
 
 ---
 
diff --git a/services/orchestrator/sm/src/lib.rs b/services/orchestrator/sm/src/lib.rs
index 9646e98..59388f9 100644
--- a/services/orchestrator/sm/src/lib.rs
+++ b/services/orchestrator/sm/src/lib.rs
@@ -153,6 +153,10 @@
     ReadFirmware(ComponentId),
     VerifyFirmware(ComponentId),
     ReleaseReset(ComponentId),
+    /// Assert reset on a component that is already running — the inverse of
+    /// [`ReleaseReset`]. Emitted when an optional component is found corrupt at
+    /// runtime: the component is gated without triggering a recovery cycle.
+    AssertReset(ComponentId),
     SignAttestation,
     AuthenticateUpdate,
     StageUpdate,
@@ -447,8 +451,25 @@
                     Outcome::Handled
                 }
                 Event::CorruptionDetected(id) => {
-                    rot.failed = Some(*id);
-                    Outcome::Transition(State::Recovering)
+                    // Respect the per-component policy encoded at chain-build time.
+                    // required: true  → recover (halt chain, restore, re-walk)
+                    // required: false → ignore corruption; component stays running
+                    //                   but is not considered trusted by the core.
+                    let required = rot
+                        .chain
+                        .iter()
+                        .find(|(cid, _)| cid == id)
+                        .map(|(_, attrs)| attrs.required)
+                        .unwrap_or(true); // unknown id: treat as required (safe default)
+                    if required {
+                        rot.failed = Some(*id);
+                        Outcome::Transition(State::Recovering)
+                    } else {
+                        // Optional: gate the component (put it back in reset) but
+                        // do not halt the chain or trigger recovery.
+                        ctx.emit(Effect::AssertReset(*id));
+                        Outcome::Handled
+                    }
                 }
                 _ => Outcome::Super,
             },
@@ -907,4 +928,46 @@
         assert!(!effects.contains(&Effect::ReleaseReset(C1)));
         assert!(!effects.contains(&Effect::RestoreGoldenImage(C1)));
     }
+
+    /// Runtime corruption of a `required: false` component gates the component
+    /// (AssertReset) but does not trigger recovery — the machine stays in Ready.
+    #[test]
+    fn optional_runtime_corruption_is_ignored() {
+        let (effects, state) = drive(
+            chain(&[
+                (C0, ComponentAttrs::passive_required()),
+                (C1, ComponentAttrs::passive_optional()),
+            ]),
+            &[
+                BOOT,
+                Event::VerificationPassed(C0),
+                Event::VerificationPassed(C1),
+                Event::CorruptionDetected(C1), // optional → gate, no recovery
+            ],
+        );
+        assert_eq!(state, State::Ready);
+        assert!(effects.contains(&Effect::AssertReset(C1)));
+        assert!(!effects.contains(&Effect::RestoreGoldenImage(C1)));
+        assert!(!effects.contains(&Effect::LatchLockdown));
+    }
+
+    /// Runtime corruption of a `required: true` component still triggers
+    /// recovery as before.
+    #[test]
+    fn required_runtime_corruption_triggers_recovery() {
+        let (effects, state) = drive(
+            chain(&[
+                (C0, ComponentAttrs::passive_required()),
+                (C1, ComponentAttrs::passive_optional()),
+            ]),
+            &[
+                BOOT,
+                Event::VerificationPassed(C0),
+                Event::VerificationPassed(C1),
+                Event::CorruptionDetected(C0), // required → Recovering
+            ],
+        );
+        assert_eq!(state, State::Recovering);
+        assert!(effects.contains(&Effect::RestoreGoldenImage(C0)));
+    }
 }