Part III — The League Architecture 30. Lifecycle and graceful degradation
Next →
Chapter · Part III — The League Architecture

30. Lifecycle and graceful degradation

The Lessons from Outside survey names graceful degradation as the discipline FRC most conspicuously skips: a wire comes loose, a CAN device drops, and baseline robot code either crashes or sails on commanding a motor that isn’t there. ROS, Nav2, and the autonomous-driving stack treat managed lifecycle and health as table stakes. The component model is the right place to fix this once — in the shared shape — instead of bolting a special case onto each subsystem.

A real component has a lifecycle

A component is not just “construct it and call update forever.” Modeled on ROS 2 managed nodes — with two deliberate deviations, named below — it moves through defined states:

D2 diagram

The deviations are improvements for FRC, not inheritance. ROS 2’s primary states are Unconfigured / Inactive / Active / Finalized, and errors route through a transitional ErrorProcessing state — there is no persistent fault or degraded primary state. We add one, because an FRC mechanism can lose a device mid-match and must keep ticking in that condition for minutes, not merely transition through it. And we split ROS 2’s single Active into enabled / running, matching the field-management reality that a robot is powered and configured well before it is allowed to move. Within the fault box, fault means total loss — the device is gone, emit safe zeros — while degraded means impaired but still acting: one motor of a pair lost, a reduced current budget, still pursuing its command.

The transitions are the same for every component, so the discipline is written once and inherited by motor, subsystem, estimator, and executive alike. A component that is configured but not enabled holds its parameters but commands nothing; a component in fault accepts commands but emits only safe ones. None of this is mechanism-specific.

Health is a field, not an exception

The first concrete requirement: a connected / health field lives in State.status, not in an exception path. The corpus already does this at the leaf without naming it — the swerve ModuleIO inputs carry driveConnected and turnConnected, and CTRE’s drive state carries FailedDaqs. The component model promotes that from a leaf convention to the universal rule: every component reports its health as part of the state it already exposes. A parent reads a child’s State.status the same way it reads the estimate, so “the elevator’s left motor is disconnected” propagates up the tree as ordinary state, available to the superstructure’s interlock logic, the telemetry log, and the driver dashboard through one path — not as an exception that some layer must remember to catch.

The null component is the fault state

The second requirement makes degradation structural rather than ad-hoc. The Elite Architecture already has the right object: the *IONull null-object (ch. 16), a do-nothing implementation whose methods are deliberately empty so a subsystem with disconnected hardware runs as a safe no-op instead of crashing. In the component model, the null implementation is the component in its fault lifecycle state: it accepts commands, emits safe/zero outgoing commands, and reports connected = false.

That reframing matters. Degradation stops being a special code path bolted on after the fact and becomes a lifecycle transition of the standard shape: a component detects a lost device, transitions running → fault, and its behavior in that state is simply “be the null component.” Because the null component satisfies the same contract as the real one, nothing above it needs a branch for the degraded case — the executive keeps issuing goals, the failed component keeps reporting connected = false and emitting zeros, and the rest of the robot keeps running. A robot that loses a non-critical mechanism mid-match degrades to operating without it instead of faulting the whole program.

Why the shape is the right home for it

Graceful degradation is hard to retrofit precisely because, in a baseline architecture, every subsystem handles failure its own way — or doesn’t. By giving every active thing the same faceplate, the same lifecycle, and the same State.status health field, the model gives degradation one definition that every component inherits. This is the structural hook the outside-robotics survey asked for: not a library to import, but a property of the shared shape. The discipline FRC skips becomes the default, because there is nowhere else for it to live.

The shape also has one more thing to prove — that it is genuinely the same factoring the broader field uses, not a coincidence. The next chapter cashes that in: the ROS bridge and language portability.