Motion Accountability Platform — Architecture
``` Tier 3: Commitment Protocol (Swift + Supabase) ├── Declare intentions, verify against observed motion ├── Social feed: commitments met or visibly not └── Notification loop: 30-min push-up reminder → detection → confirmation
Full Public Reader
Motion Accountability Platform — Architecture
Crucible Output (Full Omega Chain, Stage 1)
### Vision
Always-on observation layer on top of MotionMix's existing pose/avatar pipeline. Not a fitness app feature, but a platform primitive that uses continuous motion capture to detect exercise reps, classify sleep/wake state, and verify public commitments.
### Philosophy
"Not direct features per se, but building on known observations that we want to highlight or improve or disincentivize." The system watches, classifies, and surfaces, rather than prescribing.
---
Architecture: Three-Tier Observation Stack
Tier 3: Commitment Protocol (Swift + Supabase)
├── Declare intentions, verify against observed motion
├── Social feed: commitments met or visibly not
└── Notification loop: 30-min push-up reminder → detection → confirmation
Tier 2: Bout Segmentation (SAN Claims + Temporal)
├── Claim vectors classify motion regime (exercise/rest/sleep)
├── Exercise bout boundaries from periodicity + energy transitions
└── Auto-capture triggers: rep completion, wake event, commitment met
Tier 1: Exercise Detection (Geometric Joint-Angle State Machines)
├── Joint angle computation from 27-bone skeleton at 30Hz
├── Per-exercise state machines (push-up, squat, etc.)
├── Sleep/wake EMA classifier (hip height + body energy)
└── ~500 FLOPs/frame, zero ML, pure Rust---
Tier 1: cc-brain/src/accountability/
Module Structure
src/accountability/
mod.rs — AccountabilityEngine + FFI (8 symbols)
joint_angle.rs — Joint angle computation, bone index mapping
exercise.rs — ExerciseClassifier with per-exercise state machines
sleep.rs — SleepWakeDetector (EMA hip height + energy)
rep_counter.rs — Generic rep counter (peak detection on angle signal)
types.rs — ExerciseType, RepEvent, SleepState, AccountabilitySnapshotCore Types
pub enum ExerciseType {
PushUp,
Squat,
Plank, // static hold, not rep-based
Burpee, // compound: squat + push-up + jump
Unknown, // detected periodicity but unclassified
}
pub enum SleepState {
AwakeActive, // moving, hip > 0.7 standing height
AwakeStill, // standing/sitting, low energy
Resting, // lying, < 5 minutes
Sleeping, // lying, > 5 minutes sustained
}
pub struct RepEvent {
exercise: ExerciseType,
timestamp_ms: u64,
quality: f32, // 0-1 based on ROM completion
duration_ms: u32, // time for this rep
}
pub struct AccountabilitySnapshot {
current_exercise: Option<ExerciseType>,
rep_count: u32,
current_rep_phase: RepPhase, // Up, Down, Hold, Transition
sleep_state: SleepState,
bout_active: bool,
bout_duration_ms: u64,
session_reps: Vec<RepEvent>, // reps this session
}Push-Up State Machine
Bone indices (Mocopi 27):
LEFT_SHOULDER = 7, LEFT_ELBOW = 8 (inferred), LEFT_HAND = 9
RIGHT_SHOULDER = 11, RIGHT_ELBOW = 12, RIGHT_HAND = 13
HIPS = 0, LEFT_FOOT = 16, RIGHT_FOOT = 20
Angles:
elbow_angle = angle(shoulder, elbow, hand) // averaged L+R
torso_angle = angle(shoulder_mid, hip, feet_mid) vs horizontal
State machine:
IDLE → DOWN: torso < 25deg from horizontal AND elbow < 110deg
DOWN → UP: elbow > 160deg AND torso still < 25deg
UP → COUNTED: rep++, quality = min(elbow_range, torso_stability)
Hysteresis: 3 consecutive frames (100ms) before state transition.
EMA filter: alpha = 0.3 on raw angles to smooth noise.Squat State Machine
Bone indices (Mocopi 27):
HIPS = 0
LEFT_UPPER_LEG = 14, LEFT_KNEE = 15, LEFT_ANKLE/FOOT = 16
RIGHT_UPPER_LEG = 17, RIGHT_KNEE = 18, RIGHT_ANKLE/FOOT = 20
Angles:
knee_angle = angle(hip, knee, ankle) // averaged L+R
State machine:
STANDING: knee > 160deg
DESCENDING: knee decreasing, < 160deg
BOTTOM: knee < 100deg (parallel) or < 70deg (deep)
ASCENDING: knee increasing from bottom
COUNTED: knee > 155deg → rep++, depth_quality = min_knee_angle
Quality flags:
parallel (90-100) = 1.0
deep (<80) = 1.0
partial (100-120) = 0.5 (flag as incomplete)Sleep/Wake Classifier
Inputs (from existing PoseMetrics):
hip_height = skeleton.world_position(0)[1] // root bone Y
body_energy = sum(bone angular velocities) / N // existing field
head_up_dot = head_bone_quat.up() . world_up // gravity alignment
EMA window: 30 seconds (900 frames at 30Hz), alpha = 1.0/900.0
Classification:
AwakeActive: ema_height_ratio > 0.7 AND ema_energy > 0.05
AwakeStill: ema_height_ratio > 0.7 AND ema_energy < 0.05
Resting: ema_height_ratio < 0.4 AND ema_energy < 0.02 AND duration < 5min
Sleeping: ema_height_ratio < 0.4 AND ema_energy < 0.02 AND duration > 5min
Calibration: standing_hip_height set from first 5 seconds of camera activation.Generic Rep Counter
Signal: primary joint angle for the exercise (EMA-filtered)
Method: Peak-valley detection with minimum separation of 15 frames (0.5s)
fn detect_rep(angle: f32, history: &mut RingBuffer<f32>) -> Option<RepEvent> {
// 1. Add to ring buffer
// 2. If local minimum found (valley) after a peak:
// - peak_to_valley = ROM for this rep
// - time between peaks = rep duration
// - quality = peak_to_valley / expected_ROM
// 3. Return RepEvent if valid rep detected
}---
Tier 2: Bout Segmentation (integrates with existing SAN)
Connecting to Claim Bridge
The claim_bridge.rs already detects:
- Echo (periodicity > 0.8): rhythmic repetitive motion = exercise
- Oscillation (6+ curvature sign changes): bouncing/reps
- Dwell (speed < 0.02 for 10 frames): pause between sets
- Transition (jerk spike > 2sigma): rep boundary
New integration:
// In AccountabilityEngine::step()
let claims = claim_bridge.detect(latent);
let is_exercise_bout = claims.echo.active && claims.oscillation.active;
let is_rest = claims.dwell.active && !claims.transition.active;
let bout_boundary = claims.transition.triggered && !was_in_bout;Exercise Bout Detection
Tier 2 windows: 5-second sliding windows (150 frames)
window_energy_mean > 0.1 AND repetition_detected → EXERCISE
window_energy_mean < 0.03 → REST
sustained REST > 30s after EXERCISE → BOUT_COMPLETE → trigger auto-captureAuto-Capture Integration
Events that trigger clip capture (via existing SANTrajectoryLogger):
1. Rep completion → 3-second clip centered on rep
2. Exercise bout complete → summary clip (first + last rep)
3. Wake-up event (Sleeping → AwakeActive transition)
4. Commitment verification moment
---
Tier 3: Commitment Protocol (Swift + Supabase)
Data Model (Supabase)
-- Public commitments
CREATE TABLE commitments (
id UUID PRIMARY KEY,
user_id UUID REFERENCES auth.users,
commitment_type TEXT, -- 'exercise_interval', 'wake_time', 'daily_reps'
target JSONB, -- {"exercise": "push_up", "interval_min": 30, "reps": 10}
declared_at TIMESTAMPTZ,
expires_at TIMESTAMPTZ,
status TEXT DEFAULT 'active', -- active, met, missed, expired
verified_at TIMESTAMPTZ,
verification_data JSONB -- {"reps_detected": 12, "clip_url": "..."}
);
-- Observed exercise sessions
CREATE TABLE exercise_sessions (
id UUID PRIMARY KEY,
user_id UUID REFERENCES auth.users,
exercise_type TEXT,
rep_count INT,
avg_quality FLOAT,
duration_ms INT,
started_at TIMESTAMPTZ,
ended_at TIMESTAMPTZ,
clip_url TEXT,
commitment_id UUID REFERENCES commitments
);
-- Sleep/wake events
CREATE TABLE sleep_events (
id UUID PRIMARY KEY,
user_id UUID REFERENCES auth.users,
event_type TEXT, -- 'sleep_start', 'wake_up'
detected_at TIMESTAMPTZ,
confidence FLOAT,
commitment_id UUID REFERENCES commitments
);Notification Loop (existing infrastructure)
1. 30-min timer fires → push notification "Time for push-ups"
2. User starts exercising (camera already on)
3. Tier 1 detects push-ups → counts reps
4. Rep count >= target → commitment verified
5. Auto-capture clips the session
6. Feed updated: "Mohamed completed 12 push-ups (target: 10)"
7. Timer resets---
FFI Surface (8 new symbols)
// Lifecycle
AccountabilityEngine* accountability_create(uint32_t num_bones);
void accountability_destroy(AccountabilityEngine* ptr);
// Per-frame update (call at 30Hz with bone positions)
void accountability_update(AccountabilityEngine* ptr, const float* bone_positions, uint32_t num_bones);
// State queries
uint32_t accountability_get_rep_count(const AccountabilityEngine* ptr);
uint32_t accountability_get_exercise_type(const AccountabilityEngine* ptr); // enum as u32
uint32_t accountability_get_sleep_state(const AccountabilityEngine* ptr); // enum as u32
float accountability_get_rep_quality(const AccountabilityEngine* ptr); // latest rep quality
// Event polling
uint32_t accountability_poll_events(const AccountabilityEngine* ptr, AccountabilityEventFFI* out, uint32_t max_events);---
Integration Points
| System | How It Connects |
|---|---|
| Avatar Pipeline | Provides 27-bone positions at 30Hz via `avatar_get_bone_positions` |
| SAN Claims | Bout segmentation uses Echo + Oscillation + Dwell signals |
| PoseMetrics | Body energy, bounce, hip Y already computed |
| Auto-Capture | Exercise bout/rep/wake events trigger clip recording |
| Notifications | iOS local notifications for interval reminders |
| Supabase | Commitments, sessions, sleep events persisted |
| Feed | Commitment verification results posted to social feed |
---
Build Order
### Wave 1: Core Detection (Rust, cc-brain)
- `joint_angle.rs` — angle computation + bone index constants
- `exercise.rs` — push-up + squat state machines
- `rep_counter.rs` — generic peak-valley rep counter
- `sleep.rs` — EMA sleep/wake classifier
- `types.rs` — all types
- `mod.rs` — AccountabilityEngine + FFI
- Tests: ~20 unit tests covering state machines, angles, sleep transitions
### Wave 2: Integration (Rust + Swift)
- Wire FFI into `echelon.h` and `ffi.rs`
- Rebuild `libechelon_ios.a` with accountability symbols
- Swift `AccountabilityBridge.swift` wrapping FFI
- Connect to existing `PoseService` bone data flow
### Wave 3: Auto-Capture + Notifications (Swift)
- Rep/bout/wake events trigger SANTrajectoryLogger clips
- Local notification scheduling (30-min push-up interval)
- Notification → exercise detection → confirmation loop
### Wave 4: Commitment Protocol (Swift + Supabase)
- Supabase tables + RLS
- Commitment CRUD in Swift
- Verification engine (match exercise sessions to active commitments)
- Feed integration
### Wave 5: Polish + Deploy
- Calibration flow (stand still for 5 seconds)
- Exercise form feedback (quality score overlay)
- Session summary view
- Deploy to all iPhones
---
Key Design Decisions
1. No neural network for initial 5-exercise classifier. Pure geometry at ~500 FLOPs/frame. Reserve MLPs for future "detect any exercise" expansion.
2. Event-driven, not polling. Rep events fire only when state transitions occur. Battery-efficient.
3. Builds on existing infrastructure. No new sensor pipelines. Uses avatar bone positions + SAN claims + PoseMetrics already computed every frame.
4. Observation-layer philosophy. The system watches and classifies, it doesn't prescribe. No gamification, no streaks, no badges. Just: did you do it or not.
5. 30-second EMA for sleep prevents false triggers from brief lying down. 5-minute sustained threshold for sleep classification.
6. Hysteresis on all state machines (3 frames minimum) prevents jitter at classification boundaries.
Promotion Decision
Promote into a technical note or architecture paper with implementation anchors.
Source Anchor
Comp-Core/core/audio-media/cc-echelon/crates/cc-brain/MOTION_ACCOUNTABILITY_ARCHITECTURE.md
Detected Structure
Method · Evaluation · References · Code Anchors · Architecture