metric lifecycle - bitcobblers/wod-wiki GitHub Wiki
The central design idea of WOD Wiki is: everything is a metric. A workout plan, a tracked result, and a derived insight are all expressed as Metric objects. The only difference is their origin and ownership layer.
A Metric has:
interface IMetric {
type: MetricType | string; // e.g. 'rep', 'duration', 'effort', 'hint'
value?: unknown; // the numeric/string/object value
origin: MetricOrigin; // who created it
unit?: string; // kg, m, lb, etc.
action?: 'suppress'; // hides this metric type
sourceBlockKey?: string; // runtime block that owns it
timestamp?: Date; // when it was recorded
metadata?: Record<string, unknown>;
name?: string;
}| Origin | Meaning |
|---|---|
parser |
Created from source text during parse |
dialect |
Added by a dialect policy |
compiler |
Synthesized by the JIT compiler |
runtime / execution / tracked
|
Generated while the clock runs |
user / collected
|
Entered by the athlete during or after the workout |
user-plan |
A pre-run choice or override (e.g., resolving a choice group) |
analyzed / analyzed-estimated
|
Derived by analytics processors |
When several metrics of the same type exist on one statement/block, the ownership resolver picks one winner:
parser → dialect → user-plan → runtime → user-entry
(low) (high)
Higher layers shadow lower layers. A suppressor metric hides all metrics of its type.
Markdown source
│
▼
┌─────────────┐ parser metrics ┌─────────────────┐
│ Parse │ ─────────────────▶ │ CodeStatement │
│ (Lezer) │ │ MetricContainer │
└─────────────┘ └─────────────────┘
│
▼
┌─────────────┐ dialect metrics ┌─────────────────┐
│ Dialect │ ─────────────────▶ │ CodeStatement │
│ Stack │ │ MetricContainer │
└─────────────┘ └─────────────────┘
│
▼
┌─────────────┐ compiler metrics ┌───────────────┐
│ JIT Compile │ ───────────────────▶ │ RuntimeBlock │
│ │ │ + Behaviors │
└─────────────┘ └───────────────┘
│
▼
┌─────────────┐ runtime/user metrics ┌─────────────────┐
│ Run on │ ──────────────────────▶ │ OutputStatement │
│ Clock │ │ MetricContainer │
└─────────────┘ └─────────────────┘
│
▼
┌─────────────┐ analyzed metrics ┌─────────────────┐
│ Analytics │ ───────────────────▶ │ OutputStatement │
│ Engine │ │ MetricContainer │
└─────────────┘ └─────────────────┘
The Lezer grammar turns source text into a CST. syntax-parser.ts maps CST nodes to typed SyntaxPrimitives and builds an indentation tree. semantic-classifier.ts turns primitives into IMetrics.
Examples:
-
5:00→Duration(300000) -
10→Rep(10) -
Push Ups→Effort("Push Ups")
The Dialect Stack fuses numbers with units, recognizes protocol keywords, and emits hints.
Examples:
-
400m Run→Distance(400, m)+Effort("Run") -
AMRAPkeyword →Hint("workout.amrap")
The JIT compiler matches IRuntimeBlockStrategy implementations against each statement group. Strategies add Behaviors to a shared BlockBuilder. Behaviors are capabilities such as timers, rounds, sound cues, and reporting.
Compiler output is a tree of IRuntimeBlocks ready for the stack.
ScriptRuntime pushes blocks onto a RuntimeStack. Each block receives lifecycle calls:
mount()next()unmount()dispose()
As the athlete presses Next, finishes a round, or a timer expires, blocks emit OutputStatements carrying runtime metrics (elapsed time, completed reps, actual load).
After (or during) execution, analytics processors derive insight metrics:
-
volume= reps × load -
pace= distance / elapsed -
power= work / time -
tis= Training Intensity Score (composite)
These are written as analyzed metrics into the same MetricContainer stream.
MetricContainer is the typed collection that rides on CodeStatement, IRuntimeBlock, and OutputStatement. It provides:
-
get(type)— highest-precedence metric of a type -
getByType(type)— all metrics of a type, sorted by precedence -
resolve(filter?)— apply ownership resolution -
add(...metrics),remove(...),merge(...)
- Metrics are never overwritten — each stage adds new metrics at its own origin.
- Ownership resolution decides display — the highest visible layer wins.
-
Hints are metrics —
MetricType.Hintflows through the same channel but is excluded from display. -
Suppressors hide by type — a
Metricwithaction: 'suppress'hides all metrics of that type. -
Choices collapse before runtime — unresolved
Choicemetrics are resolved touser-planmetrics before the first block compiles.
-
05-architecture.md— pipeline architecture -
08-analytics.md— analytics derivation