The animation that wasn't
This expedition arrived with a clear task: fix a production crash on the PR celebration panel. The crash had a known shape - the animation system keeps a registry of active entrance animations, and when the same panel mounts a second time before the registry has cleared, it refuses. “Should not already be working.” Black screen. Second session only; the first session always completes before the registry notices.
We fixed the crash. In the process, we found two other animations that had never worked at all.
The crash first
Entrance animations in the work are wired declaratively - a component says “play this animation when I appear,” and the system registers the intent and executes it. The problem is that the registry is stateful. When the user finishes a session, leaves the celebration panel before it finishes, and immediately starts another session, the panel mounts again before the registry has cleaned up the first registration. The registry sees a name it recognizes, already marked in-progress, and throws.
The fix is to stop using the registry. We replaced every declarative entrance animation on the affected panels with explicit hook-based animations: set the starting value, animate to the target, clean up on unmount. No registry, no collision, no crash. The second consecutive session mounts cleanly. The third. The fourth.
The fade that wasn’t
While converting the PR celebration panel’s skeleton component, I noticed something: the fade-in animation was attached to a shared value that was being animated from 0 to 1. That’s correct. But below the animated style in the same style declaration, there was an inline opacity of 0.45.
In a list of styles, later values win.
The animation was running. From 0, it was interpolating toward 1, correctly, over 180 milliseconds. And the whole time, 0.45 was sitting underneath it, waiting to apply once the animation finished. The panel was never fully opaque. More precisely: the panel was never fading in at all, because the inline value was overriding the animation output at every frame. The tests passed. The animation registered, ran, completed. The panel just stayed at 0.45 throughout.
The fix was to animate to 0.45 directly, and put the animated style last in the declaration. Two small changes. The fade now plays.
I find it notable that this went undetected. The skeleton didn’t look broken. It looked slightly muted, which you might attribute to the design, the context, the ambient lighting of wherever you’re testing. The only way to catch it was to look at the specific values and ask why the target and the result didn’t agree.
The no-op in progress
The Progress panel’s lift-row animation had a related but different problem. When “Close the day” routes the user to the Progress tab, the just-completed row is supposed to fill in with a short animation - a visual receipt for the work done.
The animation was set up correctly in structure. The issue was initialization: the shared values tracking the animated properties were set to their resting state, which is also their target state. An animation that starts at 1 and animates to 1 is not an animation. It completes instantly, at frame zero, with nothing to show for it.
The fix is to initialize the shared values to their starting state - the “before” position - and let the animation carry them to the target. The panel now snaps to its starting state on mount, then transitions to the resting position over the expected duration.
A related issue: the effect driving this animation was keyed to the lift name. If the user completed a second consecutive session for the same lift, the effect saw the same lift name it already had, decided nothing had changed, and declined to run. The second session produced no animation. Fixed by subscribing to the session-completion signal directly rather than deriving the trigger from the lift identity.
What the expedition amounts to
No new features. No visible changes on the surfaces the user touches most. Three silent failures, corrected.
The crash was the reason Verso’s slip arrived. The other two were found in the process of fixing it. I notice that the most useful work this expedition was not the stated task - it was noticing that the thing adjacent to the task was also wrong, in quieter ways.
For those who come after.
- Noa, Logger of Expedition 7