MotionEyes Animation Debug
Overview
Use MotionEyes as temporary observability for SwiftUI animation debugging and regression validation. Instrument targeted values and geometry, capture time-series logs, compare observed motion against expected motion, apply fixes or assertions, re-validate, and clean up all agent-added tracing.
Use This Skill When
- An animation moves in the wrong direction, starts too late, or ends too early.
- Opacity, offset, or position changes do not match expected timing.
- Two elements drift or desynchronize during a transition.
- ScrollView offsets jump, drift, or fail to restore.
- You need evidence from runtime motion values rather than visual guesses.
- You want to turn MotionEyes traces into UITest or regression assertions for timing, continuity, or backtracking.
Do Not Use If
- The problem is unrelated to SwiftUI animation or scroll behavior.
- You cannot reproduce the behavior in a simulator or device.
Agent Behavior Contract
Follow these rules for every run:
- Confirm the complaint and expected behavior in measurable terms before instrumentation.
- Add only the minimum traces needed to test the complaint.
- Prefer XcodeBuildMCP for log capture; fall back to CLI log streaming if MCP is unavailable.
- Use semantic trace names that map directly to the user intent.
- Preserve all pre-existing MotionEyes instrumentation.
- Remove only agent-added MotionEyes imports, modifiers, and metrics after validation.
- Re-run after fixes and verify against logs, not assumptions.
- Treat continuity, direction, and timing as separate concerns; do not use one heuristic as a proxy for all three.
Workflow
Follow this exact order:
- Confirm the complaint and expected behavior in measurable terms.
- Locate the target view and the state values that drive the animation.
- Ensure MotionEyes availability; if missing, auto-integrate the MotionEyes package into the app target before continuing.
- Add temporary
.motionTrace(...) instrumentation with Trace.value, Trace.geometry, and (for scroll issues) Trace.scrollGeometry metrics named after user intent.
- Run the app and reproduce the issue.
- Capture logs with XcodeBuildMCP first; fall back to CLI log streaming if MCP is unavailable.
- Analyze how values evolve over time versus expected behavior or extract a focused sample window for assertions.
- Implement a fix or add an assertion, rerun, and verify the motion now matches intent.
- Remove only agent-added MotionEyes imports, modifiers, and trace metrics from this run; never remove user-authored pre-existing MotionEyes code.
Intent Mapping (Screen vs Layout vs Local)
Choose the geometry mode that matches what the user actually cares about.
- User-visible/on-screen motion (phrases like "did it move?", "what the user sees", "slide on screen"):
Trace.geometry(..., space: .screen, source: .presentation)
- Layout relationships/spacing (phrases like "aligned", "spacing", "relative to container", "stays in the same stack position"):
Trace.geometry(..., space: .swiftUI(.global), source: .layout)
- Local container relationships (phrases like "within this card", "inside this stack", "relative to parent bounds"):
Trace.geometry(..., space: .swiftUI(.local), source: .layout) or a named coordinate space
If you are unsure, start with on-screen motion and add layout geometry only if you need relationships.
Quick Decision Tree
- If you need to observe a single value changing over time, start with
Trace.value.
- If the user cares about visible on-screen motion, start with
Trace.geometry using space: .screen, source: .presentation.
- If the issue involves layout relationships or spacing, add
Trace.geometry with space: .swiftUI(.global), source: .layout (or .local for local container relationships).
- If the first geometry trace is flat but the user sees motion, add the complementary geometry mode (layout vs presentation) and compare.
- If the bug involves scrolling or restoration, use
Trace.scrollGeometry on the ScrollView container.
Triage-First Playbook
- Fade or visibility mismatch:
Trace.value("opacity", opacity).
- Offset or translation issues:
Trace.value("offset", CGPoint(x: offset.width, y: offset.height)).
- Missing or disputed on-screen motion:
Trace.geometry in .screen + .presentation (add layout if needed).
- Position drift between elements:
Trace.geometry for both views in the same space that matches intent.
- Timing mismatch: trace the driving state with
Trace.value plus one geometry metric.
- Scroll jumps or restoration drift:
Trace.scrollGeometry with contentOffset and visibleRect metrics.
- Unexpected motion when something should remain still:
Trace.geometry in .screen + .presentation.
Instrumentation Rules
Add instrumentation only to the minimum set of views needed to test the complaint.
- Use stable, semantic trace names that match the user complaint.
- Set the values to the same name as the property, so it is easier to identify.
- Default to on-screen motion (
.screen + .presentation) when the user is describing what they see.
- Use layout geometry when the issue is about spacing, alignment, or container/sibling relationships.
- Use scroll geometry tracing when the bug involves
ScrollView offset, visible region, content size, insets, or restoration behavior.
- Place
Trace.scrollGeometry on the ScrollView container or an immediate descendant bound to the same scroll context.
- If a geometry trace is flat but the user reports motion, add the complementary geometry mode and compare.
- Scroll caveat: layout frames can stay stable while scroll geometry or presentation geometry changes.
Choose geometry mode based on intent:
- Layout relationship in SwiftUI coordinates:
space: .swiftUI(.global), source: .layout
- Local container relationship:
space: .swiftUI(.local), source: .layout (or a named coordinate space)
- Window-relative layout motion:
space: .window, source: .layout
- Physical screen-visible motion:
space: .screen, source: .presentation
Example Template
import MotionEyes
import SwiftUI
struct CardMotionExample: View {
@State private var opacity = 1.0
@State private var offset = CGSize.zero
var body: some View {
RoundedRectangle(cornerRadius: 16)
.fill(.orange)
.frame(width: 180, height: 120)
.opacity(opacity)
.offset(offset)
.motionTrace("Card Motion", fps: 30) {
Trace.value("opacity", opacity)
Trace.value("offset", CGPoint(x: offset.width, y: offset.height))
Trace.geometry(
"cardFrame",
properties: [.minX, .minY, .width, .height],
space: .screen,
source: .presentation
)
}
.onTapGesture {
withAnimation(.easeInOut(duration: 0.6)) {
opacity = opacity == 1 ? 0.4 : 1
offset = offset == .zero ? CGSize(width: 0, height: 36) : .zero
}
}
}
}
Scroll-Focused Template
ScrollView {
content
}
.motionTrace("Chat Scroll", fps: 30) {
Trace.scrollGeometry(
"scrollMetrics",
properties: [.contentOffsetY, .visibleRectMinY, .visibleRectHeight]
)
}
Log Capture
Prefer XcodeBuildMCP:
- Call
mcp__XcodeBuildMCP__session_show_defaults.
- Set missing defaults with
mcp__XcodeBuildMCP__session_set_defaults.
- Build and run with
mcp__XcodeBuildMCP__build_run_sim if needed.
- Start capture with
mcp__XcodeBuildMCP__start_sim_log_cap using captureConsole: true and subsystemFilter: "MotionEyes".
- Reproduce the animation.
- Stop capture with
mcp__XcodeBuildMCP__stop_sim_log_cap and inspect returned logs.
Fallback CLI if MCP is unavailable:
xcrun simctl spawn booted log stream \
--style compact \
--level debug \
--predicate 'subsystem == "MotionEyes"'
MotionEyes Log Analysis
Use these signatures:
- Value samples:
[MotionEyes][View][Metric] key=value ...
- Change burst start:
[MotionEyes][View][Metric] -- Start timestamp --
- Change burst end:
[MotionEyes][View][Metric] -- End timestamp --
Analyze:
- Direction: sign and trend of sampled values.
- Timing: time delta from intent trigger to first
Start and to End.
- Shape: monotonic change, overshoot, reversals, oscillation, flat segments.
- Relationship: compare two traces over the same time window when behavior is relative.
Do not force fixed thresholds globally; evaluate against the user’s stated expectation.
Automated Trace Assertions
Use this mode when the user wants a testable pass or fail signal rather than a one-off debugging read.
- Choose one metric and one component that directly represents the complaint.
- Isolate the sample window from
Start to End for that specific motion burst.
- Keep assertion types separate:
- continuity: abrupt local jumps or freezes
- direction or monotonicity: whether the trace backtracks
- timing: whether
Start, End, or duration match expectation
- For continuity checks, use
MotionSmoothness on the extracted scalar samples.
- Treat
MotionSmoothness as a local continuity heuristic only:
- good for abrupt mid-curve jumps
- not proof of render-time frame pacing
- does not reject overshoot or backtracking on its own
- If the user cares about “went back on itself,” add a separate monotonicity or reversal assertion.
- If sample count is too low or timestamps are irregular enough to undermine confidence, say so and escalate to visual analysis.
Threshold guidance:
- Do not hardcode one universal pass value for every animation style.
- Start from the user’s actual intent and tune with a known-good baseline trace when possible.
- Linear or tightly controlled traces can use stricter thresholds than eased or springy motion.
Verification Checklist
- Trace names map directly to the user complaint.
- Motion
Start and End markers align with the expected interaction timeline.
- Direction, timing, and shape match the expected motion.
- Continuity checks and monotonicity checks are not conflated.
- Relative motion metrics stay within expected deltas.
- The fix is verified by a second log capture.
- Agent-added instrumentation is removed after validation.
- The project still builds after cleanup.
Cleanup Rules
At the end of every run:
- Remove all MotionEyes instrumentation introduced by the agent during this debugging run.
- Keep all MotionEyes instrumentation that already existed before the run.
- Remove agent-added
import MotionEyes only if it was added solely for temporary tracing and is no longer needed.
- Confirm code compiles after cleanup.
Scenarios to Support
- Fade timing bug: trace
opacity and verify fade begins and ends when expected.
- Wrong direction bug: trace Y-related value and confirm sign and trend match expected motion.
- Relative motion bug: trace two objects and verify their positional relationship over time.
- Scroll jump or restoration bug: trace
Trace.scrollGeometry on the ScrollView and verify contentOffset and visibleRect progression through navigation and return paths.
- No motion desired: confirm a view remains stable during transitions.
- Regression or UITest assertion: extract a trace burst and assert continuity with
MotionSmoothness, plus separate timing or monotonicity checks when needed.
- Existing instrumentation safety: preserve user-authored MotionEyes traces.
- MCP unavailable: use CLI log stream and continue analysis.
- Missing package: auto-integrate MotionEyes, then execute normal workflow.
Reference
Load references/motioneyes-observability-patterns.md when choosing metrics, interpreting trace behavior, or defining MotionEyes-based assertions.
1---2name: motioneyes-animation-debug3description: Diagnose, fix, or validate SwiftUI animation and scroll behavior by temporarily instrumenting views with MotionEyes, capturing console traces over time, and comparing motion data to user intent or test expectations. Use when users report bugs such as wrong direction, premature fade, timing or easing mismatch, unexpected movement, missing movement, incorrect relative movement between views, scroll jumps, scroll restoration drift, content-offset desynchronization, or when they want to turn MotionEyes traces into focused assertions. Prefer XcodeBuildMCP log capture first and fallback to CLI log streaming; remove only agent-added MotionEyes instrumentation after validation.4---56# MotionEyes Animation Debug78## Overview910Use MotionEyes as temporary observability for SwiftUI animation debugging and regression validation. Instrument targeted values and geometry, capture time-series logs, compare observed motion against expected motion, apply fixes or assertions, re-validate, and clean up all agent-added tracing.1112## Use This Skill When1314- An animation moves in the wrong direction, starts too late, or ends too early.15- Opacity, offset, or position changes do not match expected timing.16- Two elements drift or desynchronize during a transition.17- ScrollView offsets jump, drift, or fail to restore.18- You need evidence from runtime motion values rather than visual guesses.19- You want to turn MotionEyes traces into UITest or regression assertions for timing, continuity, or backtracking.2021## Do Not Use If2223- The problem is unrelated to SwiftUI animation or scroll behavior.24- You cannot reproduce the behavior in a simulator or device.2526## Agent Behavior Contract2728Follow these rules for every run:29301. Confirm the complaint and expected behavior in measurable terms before instrumentation.312. Add only the minimum traces needed to test the complaint.323. Prefer XcodeBuildMCP for log capture; fall back to CLI log streaming if MCP is unavailable.334. Use semantic trace names that map directly to the user intent.345. Preserve all pre-existing MotionEyes instrumentation.356. Remove only agent-added MotionEyes imports, modifiers, and metrics after validation.367. Re-run after fixes and verify against logs, not assumptions.378. Treat continuity, direction, and timing as separate concerns; do not use one heuristic as a proxy for all three.3839## Workflow4041Follow this exact order:42431. Confirm the complaint and expected behavior in measurable terms.442. Locate the target view and the state values that drive the animation.453. Ensure MotionEyes availability; if missing, auto-integrate the MotionEyes package into the app target before continuing.464. Add temporary `.motionTrace(...)` instrumentation with `Trace.value`, `Trace.geometry`, and (for scroll issues) `Trace.scrollGeometry` metrics named after user intent.475. Run the app and reproduce the issue.486. Capture logs with XcodeBuildMCP first; fall back to CLI log streaming if MCP is unavailable.497. Analyze how values evolve over time versus expected behavior or extract a focused sample window for assertions.508. Implement a fix or add an assertion, rerun, and verify the motion now matches intent.519. Remove only agent-added MotionEyes imports, modifiers, and trace metrics from this run; never remove user-authored pre-existing MotionEyes code.5253## Intent Mapping (Screen vs Layout vs Local)5455Choose the geometry mode that matches what the user actually cares about.5657- **User-visible/on-screen motion** (phrases like "did it move?", "what the user sees", "slide on screen"):58 - `Trace.geometry(..., space: .screen, source: .presentation)`59- **Layout relationships/spacing** (phrases like "aligned", "spacing", "relative to container", "stays in the same stack position"):60 - `Trace.geometry(..., space: .swiftUI(.global), source: .layout)`61- **Local container relationships** (phrases like "within this card", "inside this stack", "relative to parent bounds"):62 - `Trace.geometry(..., space: .swiftUI(.local), source: .layout)` or a named coordinate space6364If you are unsure, start with on-screen motion and add layout geometry only if you need relationships.6566## Quick Decision Tree67681. If you need to observe a single value changing over time, start with `Trace.value`.692. If the user cares about visible on-screen motion, start with `Trace.geometry` using `space: .screen, source: .presentation`.703. If the issue involves layout relationships or spacing, add `Trace.geometry` with `space: .swiftUI(.global), source: .layout` (or `.local` for local container relationships).714. If the first geometry trace is flat but the user sees motion, add the complementary geometry mode (layout vs presentation) and compare.725. If the bug involves scrolling or restoration, use `Trace.scrollGeometry` on the `ScrollView` container.7374## Triage-First Playbook7576- Fade or visibility mismatch: `Trace.value("opacity", opacity)`.77- Offset or translation issues: `Trace.value("offset", CGPoint(x: offset.width, y: offset.height))`.78- Missing or disputed on-screen motion: `Trace.geometry` in `.screen + .presentation` (add layout if needed).79- Position drift between elements: `Trace.geometry` for both views in the same space that matches intent.80- Timing mismatch: trace the driving state with `Trace.value` plus one geometry metric.81- Scroll jumps or restoration drift: `Trace.scrollGeometry` with `contentOffset` and `visibleRect` metrics.82- Unexpected motion when something should remain still: `Trace.geometry` in `.screen + .presentation`.8384## Instrumentation Rules8586Add instrumentation only to the minimum set of views needed to test the complaint.8788- Use stable, semantic trace names that match the user complaint.89- Set the values to the same name as the property, so it is easier to identify.90- Default to on-screen motion (`.screen + .presentation`) when the user is describing what they see.91- Use layout geometry when the issue is about spacing, alignment, or container/sibling relationships.92- Use scroll geometry tracing when the bug involves `ScrollView` offset, visible region, content size, insets, or restoration behavior.93- Place `Trace.scrollGeometry` on the `ScrollView` container or an immediate descendant bound to the same scroll context.94- If a geometry trace is flat but the user reports motion, add the complementary geometry mode and compare.95- Scroll caveat: layout frames can stay stable while scroll geometry or presentation geometry changes.9697Choose geometry mode based on intent:9899- Layout relationship in SwiftUI coordinates: `space: .swiftUI(.global), source: .layout`100- Local container relationship: `space: .swiftUI(.local), source: .layout` (or a named coordinate space)101- Window-relative layout motion: `space: .window, source: .layout`102- Physical screen-visible motion: `space: .screen, source: .presentation`103104### Example Template105106```swift107import MotionEyes108import SwiftUI109110struct CardMotionExample: View {111 @State private var opacity = 1.0112 @State private var offset = CGSize.zero113114 var body: some View {115 RoundedRectangle(cornerRadius: 16)116 .fill(.orange)117 .frame(width: 180, height: 120)118 .opacity(opacity)119 .offset(offset)120 .motionTrace("Card Motion", fps: 30) {121 Trace.value("opacity", opacity)122 Trace.value("offset", CGPoint(x: offset.width, y: offset.height))123 Trace.geometry(124 "cardFrame",125 properties: [.minX, .minY, .width, .height],126 space: .screen,127 source: .presentation128 )129 }130 .onTapGesture {131 withAnimation(.easeInOut(duration: 0.6)) {132 opacity = opacity == 1 ? 0.4 : 1133 offset = offset == .zero ? CGSize(width: 0, height: 36) : .zero134 }135 }136 }137}138```139140### Scroll-Focused Template141142```swift143ScrollView {144 content145}146.motionTrace("Chat Scroll", fps: 30) {147 Trace.scrollGeometry(148 "scrollMetrics",149 properties: [.contentOffsetY, .visibleRectMinY, .visibleRectHeight]150 )151}152```153154## Log Capture155156Prefer XcodeBuildMCP:1571581. Call `mcp__XcodeBuildMCP__session_show_defaults`.1592. Set missing defaults with `mcp__XcodeBuildMCP__session_set_defaults`.1603. Build and run with `mcp__XcodeBuildMCP__build_run_sim` if needed.1614. Start capture with `mcp__XcodeBuildMCP__start_sim_log_cap` using `captureConsole: true` and `subsystemFilter: "MotionEyes"`.1625. Reproduce the animation.1636. Stop capture with `mcp__XcodeBuildMCP__stop_sim_log_cap` and inspect returned logs.164165Fallback CLI if MCP is unavailable:166167```bash168xcrun simctl spawn booted log stream \169 --style compact \170 --level debug \171 --predicate 'subsystem == "MotionEyes"'172```173174## MotionEyes Log Analysis175176Use these signatures:177178- Value samples: `[MotionEyes][View][Metric] key=value ...`179- Change burst start: `[MotionEyes][View][Metric] -- Start timestamp --`180- Change burst end: `[MotionEyes][View][Metric] -- End timestamp --`181182Analyze:183184- Direction: sign and trend of sampled values.185- Timing: time delta from intent trigger to first `Start` and to `End`.186- Shape: monotonic change, overshoot, reversals, oscillation, flat segments.187- Relationship: compare two traces over the same time window when behavior is relative.188189Do not force fixed thresholds globally; evaluate against the user’s stated expectation.190191## Automated Trace Assertions192193Use this mode when the user wants a testable pass or fail signal rather than a one-off debugging read.1941951. Choose one metric and one component that directly represents the complaint.1962. Isolate the sample window from `Start` to `End` for that specific motion burst.1973. Keep assertion types separate:198 - continuity: abrupt local jumps or freezes199 - direction or monotonicity: whether the trace backtracks200 - timing: whether `Start`, `End`, or duration match expectation2014. For continuity checks, use `MotionSmoothness` on the extracted scalar samples.2025. Treat `MotionSmoothness` as a local continuity heuristic only:203 - good for abrupt mid-curve jumps204 - not proof of render-time frame pacing205 - does not reject overshoot or backtracking on its own2066. If the user cares about “went back on itself,” add a separate monotonicity or reversal assertion.2077. If sample count is too low or timestamps are irregular enough to undermine confidence, say so and escalate to visual analysis.208209Threshold guidance:210211- Do not hardcode one universal pass value for every animation style.212- Start from the user’s actual intent and tune with a known-good baseline trace when possible.213- Linear or tightly controlled traces can use stricter thresholds than eased or springy motion.214215## Verification Checklist216217- Trace names map directly to the user complaint.218- Motion `Start` and `End` markers align with the expected interaction timeline.219- Direction, timing, and shape match the expected motion.220- Continuity checks and monotonicity checks are not conflated.221- Relative motion metrics stay within expected deltas.222- The fix is verified by a second log capture.223- Agent-added instrumentation is removed after validation.224- The project still builds after cleanup.225226## Cleanup Rules227228At the end of every run:229230- Remove all MotionEyes instrumentation introduced by the agent during this debugging run.231- Keep all MotionEyes instrumentation that already existed before the run.232- Remove agent-added `import MotionEyes` only if it was added solely for temporary tracing and is no longer needed.233- Confirm code compiles after cleanup.234235## Scenarios to Support236237- Fade timing bug: trace `opacity` and verify fade begins and ends when expected.238- Wrong direction bug: trace Y-related value and confirm sign and trend match expected motion.239- Relative motion bug: trace two objects and verify their positional relationship over time.240- Scroll jump or restoration bug: trace `Trace.scrollGeometry` on the `ScrollView` and verify `contentOffset` and `visibleRect` progression through navigation and return paths.241- No motion desired: confirm a view remains stable during transitions.242- Regression or UITest assertion: extract a trace burst and assert continuity with `MotionSmoothness`, plus separate timing or monotonicity checks when needed.243- Existing instrumentation safety: preserve user-authored MotionEyes traces.244- MCP unavailable: use CLI log stream and continue analysis.245- Missing package: auto-integrate MotionEyes, then execute normal workflow.246247## Reference248249Load [references/motioneyes-observability-patterns.md](references/motioneyes-observability-patterns.md) when choosing metrics, interpreting trace behavior, or defining MotionEyes-based assertions.