Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 | // Value types for AI generation contracts.
//
// Mirrors `spec/ai-generation.allium`. The three component contracts
// (CycleIntentGeneration, BlockIntentGeneration, WorkoutGeneration)
// share these as their inputs, durable artefacts, and outcomes.
//
// Persistence note: MesocycleIntent and BlockIntent are persisted as
// `mesocycles.intent` / `blocks.intent` (jsonb) and round-trip through
// the database. PriorWorkoutSummary and WorkoutGenerationContext are
// TypeScript-only types — they are assembled per-call by the platform's
// AI integration layer to invoke the Harness, and never persisted.
import type { MovementPattern } from './enums';
import type { PrescribedExercise } from './index';
// ── Intent artefacts (durable; written to mesocycles.intent / blocks.intent) ──
/**
* Coach-voice direction for a mesocycle. Persisted as `mesocycles.intent`.
*
* Spec: `value MesocycleIntent` in spec/ai-generation.allium.
*/
export interface MesocycleIntent {
/** Coach-voice prose, athlete-readable. Captures the cycle's overall direction in plain language. */
narrative: string;
/**
* Movement patterns this cycle is biased toward. May overlap with previous
* cycles for continuity, or shift if the athlete is moving between focus
* areas. Empty list is valid (a "balanced" cycle).
*/
emphasis_patterns: MovementPattern[];
}
/**
* Coach-voice direction for a block, plus per-workout pattern allocation
* and block-wide volume targets. Persisted as `blocks.intent`.
*
* Spec: `value BlockIntent` in spec/ai-generation.allium.
*/
export interface BlockIntent {
/**
* Coach-voice prose for the block. Single paragraph; should reference the
* block's role in the cycle (build, develop, deload, etc.).
*/
block_focus: string;
/**
* Per-workout pattern allocation. Length must equal the number of training
* days in the block (see BlockIntentGeneration.@invariant
* WorkoutEmphasisCardinality).
*/
workout_emphasis: WorkoutEmphasis[];
/**
* Block-wide volume targets. The workout-detail harness reads this to know
* how many workouts in the block should hit each pattern, which is essential
* for least-worst replanning when a workout is missed.
*/
pattern_volume_targets: PatternVolumeTarget[];
}
/**
* Per-workout slot allocation inside a BlockIntent.
*
* Spec: `value WorkoutEmphasis` in spec/ai-generation.allium.
*/
export interface WorkoutEmphasis {
/**
* 1-indexed position in the block. Maps to the workout slot at that index
* in the block's training_days ordering.
*/
day_in_block: number;
/** Patterns this workout leads with (at least one). */
primary_patterns: MovementPattern[];
/** Patterns this workout also touches at moderate volume. May be empty. */
secondary_patterns: MovementPattern[];
/**
* Coach voice, e.g. "heavy squat day, accessory pull". Athlete-readable;
* used as the placeholder for a not-yet-detailed workout.
*/
intent: string;
}
/**
* Block-wide pattern volume target inside a BlockIntent.
*
* Spec: `value PatternVolumeTarget` in spec/ai-generation.allium.
*/
export interface PatternVolumeTarget {
pattern: MovementPattern;
/**
* Target number of workouts in the block that emphasise this pattern
* (counts a workout emphasising the pattern as primary OR secondary).
*/
workouts_count: number;
}
// ── Workout-detail context bundle (per-call; never persisted) ──
/**
* Identity reference for a workout slot used inside a PriorWorkoutSummary.
* Kept structural and minimal: the harness only needs enough to disambiguate
* the slot when composing the corrective prompt and ordering history.
*/
export interface WorkoutRef {
id: string;
block_id: string;
scheduled_date: string;
day_in_block: number;
}
/**
* Brief summary of a previously-prescribed-or-completed workout in the same
* block. Used by WorkoutGeneration to plan within-block variety and to drive
* least-worst replanning when prior slots have been missed.
*
* Spec: `value PriorWorkoutSummary` in spec/ai-generation.allium.
*
* NOT persisted — assembled per-call by the integration layer.
*/
export interface PriorWorkoutSummary {
workout: WorkoutRef;
/** Whether this workout has been logged or only prescribed. */
status: 'prescribed_only' | 'logged';
/** Pattern allocation that was prescribed (for variety planning). */
prescribed_patterns: MovementPattern[];
/** Brief outcome signal (logged workouts only). Null otherwise. */
actual_volume_summary: string | null;
}
/**
* Identity reference for an athlete used inside a WorkoutGenerationContext.
* Structural and minimal: the integration layer fully resolves athlete state
* (profile, goals, constraints) into sibling fields rather than expecting
* the harness to dereference further.
*/
export interface AthleteRef {
id: string;
}
/**
* Identity reference for a mesocycle used inside a WorkoutGenerationContext.
* Carries `cycle_number` because the cycle/block intent prompts branch on it
* per the first-cycle bootstrap resolution (see ai-generation.allium Q1).
*/
export interface MesocycleRef {
id: string;
cycle_number: number;
}
/**
* Identity reference for a block used inside a WorkoutGenerationContext.
*/
export interface BlockRef {
id: string;
mesocycle_id: string;
block_number: number;
}
/**
* Identity reference for the workout slot being generated.
*/
export interface WorkoutSlotRef {
id: string;
block_id: string;
scheduled_date: string;
day_in_block: number;
}
/**
* Identity reference for a training location.
*/
export interface TrainingLocationRef {
id: string;
name: string;
}
/**
* Identity reference for an equipment type.
*/
export interface EquipmentTypeRef {
id: string;
name: string;
}
/**
* Identity reference for an exercise the athlete is familiar with.
*/
export interface ExerciseRef {
id: string;
name: string;
}
/**
* Identity reference for an athlete constraint.
*/
export interface AthleteConstraintRef {
id: string;
description: string;
}
/**
* Brief reference for a post-workout review, surfaced in
* recent_review_feedback.
*/
export interface PostWorkoutReviewRef {
id: string;
workout_id: string;
feedback: string;
created_at: string;
}
/**
* Reference for a disruption affecting the slot being generated.
*/
export interface DisruptionRef {
id: string;
kind: string;
start_date: string;
end_date: string;
}
/**
* Typed context bundle passed to WorkoutGeneration.generate(). Caller-
* assembled — see WorkoutGeneration.@invariant ContextBundleAssembledByCaller
* and @invariant ContextBundleFreshness.
*
* Spec: `value WorkoutGenerationContext` in spec/ai-generation.allium.
*
* NOT persisted — assembled per-call by the integration layer for each
* workout-detail invocation.
*/
export interface WorkoutGenerationContext {
// Identity
athlete: AthleteRef;
/** The slot we are generating detail for. */
workout: WorkoutSlotRef;
/** workout.block, denormalised for clarity. */
block: BlockRef;
/** block.mesocycle, denormalised. */
mesocycle: MesocycleRef;
// Slot parameters (already chosen by upstream block scheduling)
session_type: import('./enums').SessionType;
time_cap_minutes: number;
location: TrainingLocationRef;
is_deload: boolean;
// Block parameters (rep range, rest target — already on block)
rep_range_low: number;
rep_range_high: number;
rest_seconds_target: number;
// Intent cascade
cycle_intent: MesocycleIntent;
block_intent: BlockIntent;
/** The BlockIntent.workout_emphasis entry for this slot. */
workout_emphasis: WorkoutEmphasis;
// Athlete state
available_equipment: EquipmentTypeRef[];
familiar_exercises: ExerciseRef[];
constraints: AthleteConstraintRef[];
is_sparse_profile: boolean;
// History (drives within-block coherence and within-cycle adaptation)
/** Already-prescribed-or-completed prior workouts in the same block. */
prior_workouts_in_block: PriorWorkoutSummary[];
/** Last K sets per pattern across recent workouts. */
recent_set_logs: import('./index').SetLog[];
/** Last N post-workout reviews. */
recent_review_feedback: PostWorkoutReviewRef[];
/** Disruptions whose effective window covers the slot's scheduled_date. */
active_disruptions: DisruptionRef[];
// Retry context (populated by the harness on attempt 2+)
previous_violations: ValidationViolation[];
}
// ── Validation, plan, outcomes ──
/**
* A single rule violation produced by validate_session or by the platform-
* side validation that wraps WorkoutGeneration's inner loop.
*
* Spec: `value ValidationViolation` in spec/ai-generation.allium.
*/
export interface ValidationViolation {
/** Matches the validation rules in programming/Workout validation. */
rule: 'volume_concentration' | 'time_overrun' | 'unknown_exercise';
/**
* Free-form message used to compose the next-attempt prompt. For
* unknown_exercise this lists the offending exercise IDs.
*/
message: string;
}
/**
* Validation result for a generated workout. Always present on a successful
* outcome; absent on a refused outcome.
*
* Spec: `value ValidationResult` in spec/ai-generation.allium.
*/
export interface ValidationResult {
valid: boolean;
violations: ValidationViolation[];
}
/**
* The submit_workout_plan tool's input — the final, model-produced workout
* plan that the platform persists on success.
*
* Spec: `value WorkoutPlan` in spec/ai-generation.allium.
*/
export interface WorkoutPlan {
prescribed_exercises: PrescribedExercise[];
}
/**
* Why the harness returned without producing a usable plan. Discriminated
* by `kind`; the seven kinds correspond exactly to RefusalReason.kind in
* spec/ai-generation.allium.
*
* Spec: `value RefusalReason` in spec/ai-generation.allium.
*/
export interface RefusalReason {
kind:
/**
* A required field on the context bundle is missing or null.
*/
| 'context_bundle_incomplete'
/**
* The slot's hard constraints cannot be satisfied (e.g. equipment empty
* AND no bodyweight exercises in the library for any of the block's
* required patterns).
*/
| 'hard_constraint_unsatisfiable'
/**
* Validation retries exhausted without producing a passing plan (see
* WorkoutGeneration.@invariant Convergence).
*/
| 'validation_unconverged'
/**
* The model loop reached its iteration cap without calling the submit
* tool.
*/
| 'model_abandoned'
/**
* The model returned output that could not be parsed against
* submit_workout_plan's input schema, even after a corrective turn.
*/
| 'malformed_output'
/**
* Upstream provider failure (rate-limit cascade, network error mid-loop,
* provider 5xx). Distinguishable from the above because the platform
* may treat it differently (e.g. retry later vs replan now).
*/
| 'transient_upstream_failure'
/**
* The harness ran past its hard latency budget before producing a
* result. Returned in lieu of timing out the calling rule.
*/
| 'wall_clock_timeout';
/**
* Machine-readable diagnostic string — typically the underlying error
* message, the violation list, or a short trace. Not athlete-visible
* (failures are not athlete-observable per the AI service boundary).
*/
detail: string;
}
/**
* Outcome of a WorkoutGeneration.generate() call. Discriminated by
* `succeeded`: when true, `plan` and `validation` are present; when false,
* `refusal` is present.
*
* Spec: `value WorkoutGenerationOutcome` in spec/ai-generation.allium.
*/
export type WorkoutGenerationOutcome =
| { succeeded: true; plan: WorkoutPlan; validation: ValidationResult }
| { succeeded: false; refusal: RefusalReason };
/**
* Outcome of a CycleIntentGeneration.generate() call. Discriminated by
* `succeeded`.
*
* Spec: `value CycleIntentOutcome` in spec/ai-generation.allium.
*/
export type CycleIntentOutcome =
| { succeeded: true; intent: MesocycleIntent }
| { succeeded: false; refusal: RefusalReason };
/**
* Outcome of a BlockIntentGeneration.generate() call. Discriminated by
* `succeeded`.
*
* Spec: `value BlockIntentOutcome` in spec/ai-generation.allium.
*/
export type BlockIntentOutcome =
| { succeeded: true; intent: BlockIntent }
| { succeeded: false; refusal: RefusalReason };
|