Inspiration
Every endurance athlete knows the horror story: you build a perfect interval workout in TrainingPeaks, export it to Zwift, and suddenly your heart rate targets vanish. You try to move it to a Wahoo head unit, and the power zones drift.
The world of sports data is a graveyard of incompatible body parts:
- FIT (Garmin): A compact, binary, unreadable stream of bytes.
- TCX (Training Center): An old, verbose XML format.
- ZWO (Zwift): A proprietary XML schema with limited capabilities.
I asked: What if I could stitch these dead, incompatible formats into a single, living super-format?
That’s how Kaiord was born. The name blends Kairos (Greek for "the right moment") and Ordo (Latin for "order")—bringing timely structure to chaos.
What it does
Kaiord is a "Frankenstein" engine that stitches together the sports data ecosystem. It works in three layers:
- The Spine (KRD Format): A canonical JSON schema that serves as the universal language. It supports 100% of the features from FIT, TCX, and ZWO combined.
- The Adapters (Core Library): A TypeScript library that ingests binary files (FIT) and text files (XML), converts them to KRD, and exports them back to any format with zero data loss.
- The Interface (SPA & CLI):
- CLI: For developers and data nerds to batch convert thousands of files in milliseconds.
- Web Editor: A "Vibe Coding" inspired editor where coaches can visually drag-and-drop intervals, and the file is instantly ready for Zwift or Garmin.
How I built it
I didn't just write code; I orchestrated an AI-augmented development lifecycle using Kiro.
1. Hexagonal Architecture (The Skeleton)
To prevent my monster from falling apart, I used a strict Hexagonal Architecture enforced by Steering Docs.
- Domain: Pure TypeScript logic. Zero dependencies.
- Ports: Interfaces like
FitReaderorTcxWriter. - Adapters: The dirty work of parsing binary buffers or scraping XML tags.
2. Spec-Driven & Test-Driven Development (The Brain)
Instead of the usual "chat-driven chaos" (where you ask an AI for code, it fails, and you spend 20 minutes debugging), I used Kiro's Spec-Driven approach.
Every feature started with three documents:
-
requirements.md: What the feature needs to do. -
design.md: How it will work (architecture). -
tasks.md: Step-by-step implementation plan.
Once the spec was approved, we followed a strict TDD loop enforced by steering/tdd.md:
- Red: Kiro writes the test first (e.g., "should convert FIT to KRD"). It fails.
- Green: Kiro writes the minimal code to pass the test.
- Refactor: We clean up the code while keeping tests green.
This "Think First, Code Later" mentality meant no "hallucinated" code. Kiro wasn't guessing; it was implementing a spec we had agreed upon.
3. Agent Hooks & MCP Automation
I didn't just type code; I built agents to manage the project.
- Vibe Coding handled the heavy lifting of the
adapters/layer. - Agent Hooks & MCP: I used a Kiro Hook to trigger the GitHub MCP Server, which reads the spec and opens a Pull Request with the perfect description in seconds.
- Automatic Guardians: Another hook watches
package.json. If dependencies change, it automatically verifies that documentation is up to date.
3. The Math of Resurrection (Round-Trip Safety)
To ensure the "stitched" monster didn't lose limbs during surgery, I implemented strict Round-Trip Validation. This means if I convert a file from FIT to KRD and back to FIT, the result must be mathematically equivalent to the original.
I enforced these "Laws of Robotics" using mathematical tolerances:
$$ \Delta Power \le 1 \text{ watt} $$ $$ \Delta Duration \le 1 \text{ second} $$ $$ \Delta HeartRate \le 1 \text{ bpm} $$
If a single byte shifts beyond these limits, the build fails. I combined AJV for structural validation and custom Vitest matchers to verify this signal integrity mathematically.
Challenges I ran into
The "Missing Limb" Problem Zwift's ZWO format physically cannot store Heart Rate zones in the same way a FIT file does. It was like trying to sew an arm onto a torso that had no shoulder.
- Solution: I extended the KRD schema to hold "shadow data". When converting to ZWO, we gracefully degrade. When converting back from KRD to FIT, we restore the full fidelity data.
Binary vs. Text Friction Debugging binary streams is painful. One wrong bit shifts the entire stream.
- Solution: I used Kiro's Vibe Coding to analyze hex dumps alongside the Garmin SDK specs, allowing the AI to spot bit-shifting errors that were invisible to the human eye.
Accomplishments that I'm proud of
- 📦 2 NPM Packages published (
@kaiord/core,@kaiord/cli) and 1 Live SPA (demo). - 🧪 86.5% Test Coverage across the entire monorepo.
- ⚡ <150ms conversion time for complex binary files.
- 👻 Kiroween Mode: A hidden "Frankenstein" theme in the editor (try the theme toggle!) and a secret ASCII banner in the CLI (
kaiord --kiro).
What I learned
- Agents need laws: Without my
AGENTS.mdand steering docs, the AI would have imported external libraries into my domain layer. Defining the "Laws of Robotics" (or architecture) upfront was crucial. - MCP is a superpower: Having Kiro talk directly to GitHub via MCP turned "chore work" (writing PR descriptions, linking issues) into an automated background process.
- Frankenstein is beautiful: Combining the speed of binary FIT with the readability of JSON KRD created something unexpectedly powerful—a format that is both machine-efficient and human-editable.
What's next for Kaiord
- Direct Integration: Connecting directly to TrainingPeaks and Garmin APIs to push structured workouts from the editor to the athlete's calendar.
- AI Workout Generator: Implementing a "Natural Language to KRD" agent. Users can describe a session ("3x10min at FTP with 5min rest"), and an LLM will generate the valid KRD structure via Structured Outputs, ready to be synced.
- Social Workouts: Enabling users to share workout libraries and training plans seamlessly, fostering a community of open-source training data.
Log in or sign up for Devpost to join the conversation.