@sunsteel/contracts 0.63.0 → 0.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -24,1324 +24,1538 @@ interface PaginatedResponse<T> {
24
24
  nextCursor?: string;
25
25
  }
26
26
 
27
- interface SetLog {
28
- id: string;
29
- sessionId: string;
30
- routineExerciseId: string;
31
- exerciseId: string;
32
- setNumber: number;
33
- reps: number;
34
- weight?: number | null;
35
- rpe?: number | null;
36
- isCompleted: boolean;
37
- completedAt?: IsoDateString | null;
38
- createdAt: IsoDateString;
39
- updatedAt: IsoDateString;
40
- }
41
- interface WorkoutSession {
42
- id: string;
43
- userId: string;
44
- routineId: string;
45
- routineDayId: string;
46
- status: WorkoutSessionStatus;
47
- startedAt: IsoDateString;
48
- endedAt?: IsoDateString | null;
49
- durationSec?: number | null;
50
- notes?: string | null;
51
- lastActivityAt?: IsoDateString | null;
52
- setLogs?: SetLog[];
53
- /**
54
- * LIVE-11: slots performed with a different exercise in this session. The
55
- * routine day (snapshot) keeps the original prescription; set logs for a
56
- * substituted slot carry the substitute's `exerciseId`.
57
- */
58
- exerciseSubstitutions?: SessionExerciseSubstitution[];
59
- /**
60
- * LIVE-16: what the owner noted about one exercise in this workout. The
61
- * routine's own note (`routineDay.exercises[].note`) is the standing
62
- * instruction and is never written from a session.
63
- */
64
- exerciseNotes?: SessionExerciseNote[];
65
- reused?: boolean;
66
- routine?: {
67
- id: string;
68
- name: string;
69
- description?: string | null;
70
- };
71
- routineDay?: {
72
- id: string;
73
- name?: string | null;
74
- order?: number;
75
- /** Null on a ROTATION routine day (ROUT-11). */
76
- dayOfWeek?: number | null;
77
- exercises: Array<{
78
- id: string;
79
- order: number;
80
- restSeconds?: number | null;
81
- note?: string | null;
82
- progressionScheme: ProgressionScheme;
83
- minWeightIncrement: number;
84
- exercise: {
85
- id: string;
86
- name: string;
87
- primaryMuscles: MuscleGroup[];
88
- secondaryMuscles?: MuscleGroup[];
89
- };
90
- sets: Array<{
91
- id: string;
92
- setNumber: number;
93
- repType: RepType;
94
- reps?: number | null;
95
- minReps?: number | null;
96
- maxReps?: number | null;
97
- weight?: number | null;
98
- rir?: number | null;
99
- }>;
100
- }>;
101
- };
102
- }
103
- /** Completed set from the latest earlier execution of the same routine day. */
104
- interface PreviousSetPerformance {
105
- routineExerciseId: string;
106
- exerciseId: string;
107
- setNumber: number;
108
- reps: number;
109
- weight?: number | null;
110
- rpe?: number | null;
111
- }
112
- /** Stable successful response of GET /workouts/sessions/:id/previous-performance. */
113
- interface PreviousPerformanceResponse {
114
- sessionId: string;
115
- endedAt: IsoDateString;
116
- sets: PreviousSetPerformance[];
117
- }
118
- interface StartWorkoutRequest {
119
- routineId: string;
120
- routineDayId: string;
121
- notes?: string;
27
+ /**
28
+ * Per-routine visibility. The account-level `PROF-06` routines rule is the
29
+ * upper bound: a routine marked `PUBLIC` inside an account whose routines are
30
+ * `FOLLOWERS` is visible to followers only, the same way `SOC-04` bounds
31
+ * activity by the profile section it comes from.
32
+ */
33
+ declare const ROUTINE_VISIBILITY_VALUES: readonly ["PRIVATE", "FOLLOWERS", "PUBLIC"];
34
+ type RoutineVisibility = (typeof ROUTINE_VISIBILITY_VALUES)[number];
35
+ /** One routine keeps at most this many active links. */
36
+ declare const ROUTINE_SHARE_MAX_ACTIVE_LINKS = 10;
37
+ /** PUT /routines/:id/visibility */
38
+ interface UpdateRoutineVisibilityRequest {
39
+ visibility: RoutineVisibility;
122
40
  }
123
- interface StartWorkoutResponse {
41
+ /** An active share link, visible only to the routine's owner. */
42
+ interface RoutineShare {
124
43
  id: string;
125
44
  routineId: string;
126
- routineDayId: string;
127
- status: WorkoutSessionStatus;
128
- startedAt: IsoDateString;
129
- endedAt?: IsoDateString | null;
130
- reused: boolean;
131
- }
132
- type FinishStatus = Extract<WorkoutSessionStatus, 'COMPLETED' | 'ABORTED'>;
133
- interface FinishWorkoutRequest {
134
- status: FinishStatus;
135
- notes?: string;
136
- }
137
- type ProgressionRule = 'ALL_SETS_REACHED_TARGET' | 'SET_REACHED_TARGET';
138
- interface ProgressionSetChange {
139
- setNumber: number;
140
- targetReps: number;
141
- performedReps: number;
142
- previousWeightKg: number;
143
- newWeightKg: number;
144
- }
145
- /** An automatic prescription update produced while completing a session. */
146
- interface ProgressionChange {
147
- routineExerciseId: string;
148
- exerciseId: string;
149
- exerciseName: string;
150
- progressionScheme: Extract<ProgressionScheme, 'DOUBLE_PROGRESSION' | 'DYNAMIC_DOUBLE_PROGRESSION'>;
151
- rule: ProgressionRule;
152
- minWeightIncrementKg: number;
153
- sets: ProgressionSetChange[];
45
+ /** Unguessable identifier used in the public `/shared/routines/:token` URL. */
46
+ token: string;
47
+ createdAt: IsoDateString;
154
48
  }
155
- /** Stable successful response of PATCH /workouts/sessions/:id/finish. */
156
- interface FinishWorkoutResponse {
157
- session: WorkoutSession;
158
- progressionChanges: ProgressionChange[];
159
- recap: WorkoutSessionRecap | null;
49
+ /** Stable successful response of GET /routines/:id/shares. */
50
+ interface RoutineShareListResponse {
51
+ items: RoutineShare[];
160
52
  }
161
- interface UpsertSetLogRequest {
162
- routineExerciseId: string;
163
- exerciseId: string;
164
- setNumber: number;
165
- reps: number;
166
- weight?: number;
167
- rpe?: number;
168
- isCompleted?: boolean;
53
+ interface SharedRoutineOwner {
54
+ username: string;
55
+ name: string;
56
+ lastName?: string | null;
57
+ avatarUrl?: string | null;
169
58
  }
170
59
  /**
171
- * LIVE-11: an exercise performed in place of one routine slot for a single
172
- * session. The substitute's muscles are captured when the swap is made, so
173
- * later catalog changes cannot rewrite what the session trained.
60
+ * How a reader reached a routine. A link is the owner's explicit consent for
61
+ * that one routine and ignores visibility; a visibility read is governed by
62
+ * both the routine's own rule and the account's.
174
63
  */
175
- interface SessionExerciseSubstitution {
176
- /** The replaced slot, as in `routineDay.exercises[].id`. */
177
- routineExerciseId: string;
178
- exercise: {
179
- id: string;
180
- name: string;
181
- primaryMuscles: MuscleGroup[];
182
- secondaryMuscles: MuscleGroup[];
183
- };
184
- substitutedAt: IsoDateString;
185
- }
186
- declare const SESSION_NOTE_MAX_LENGTH = 2000;
187
- declare const SESSION_EXERCISE_NOTE_MAX_LENGTH = 500;
188
- /** One exercise's note for one workout, keyed by its routine slot. */
189
- interface SessionExerciseNote {
190
- /** The slot, as in `routineDay.exercises[].id`. */
191
- routineExerciseId: string;
192
- note: string;
64
+ declare const SHARED_ROUTINE_SOURCES: readonly ["LINK", "VISIBILITY"];
65
+ type SharedRoutineSource = (typeof SHARED_ROUTINE_SOURCES)[number];
66
+ /**
67
+ * Stable successful response of the unauthenticated
68
+ * GET /shared/routines/:token, and of the authenticated member read.
69
+ */
70
+ interface SharedRoutine {
71
+ /** Kept so a future clone (`ROUT-05`) can record what it came from. */
72
+ routineId: string;
73
+ setup: RoutineVersionSetup;
74
+ owner: SharedRoutineOwner;
75
+ source: SharedRoutineSource;
76
+ /** When the routine was last changed, not when it was shared. */
77
+ updatedAt: IsoDateString;
193
78
  }
194
79
  /**
195
- * Body of PUT /workouts/sessions/:id/notes. Partial: an omitted field keeps
196
- * what is stored, `null` or an empty string clears it. Exercise notes merge
197
- * by slot, so two notes saved moments apart cannot overwrite each other.
80
+ * ROUT-06. Where a cloned routine came from. It is recorded on the clone and
81
+ * never on the source, so a routine cannot learn who copied it. It is served
82
+ * only to a viewer who could read the source anyway: lineage must not become
83
+ * a way to discover that a private routine exists.
198
84
  */
199
- interface UpdateSessionNotesRequest {
200
- notes?: string | null;
201
- exerciseNotes?: Array<{
202
- routineExerciseId: string;
203
- note: string | null;
204
- }>;
85
+ interface RoutineLineage {
86
+ /** The routine this one was cloned from, when the viewer may read it. */
87
+ sourceRoutineId: string | null;
88
+ /** The original author's public identity, when they still have one. */
89
+ author: SharedRoutineOwner | null;
90
+ /**
91
+ * True when the source exists but this viewer may not read it, or its author
92
+ * is gone. The clone still says it was cloned; it just cannot say from what.
93
+ */
94
+ isSourceHidden: boolean;
95
+ clonedAt: IsoDateString;
205
96
  }
206
- interface UpdateSessionNotesResponse {
207
- notes: string | null;
208
- exerciseNotes: SessionExerciseNote[];
97
+ /** A routine in a member's visible list, without loading its whole setup. */
98
+ interface SharedRoutineSummary {
99
+ routineId: string;
100
+ name: string;
101
+ description: string | null;
102
+ scheduleMode: RoutineScheduleMode;
103
+ dayCount: number;
104
+ exerciseCount: number;
105
+ updatedAt: IsoDateString;
209
106
  }
210
- /** Body of PUT /workouts/sessions/:id/exercises/:routineExerciseId/substitution. */
211
- interface SubstituteSessionExerciseRequest {
212
- exerciseId: string;
213
- /** Also use this exercise in the routine from the next session on. */
214
- applyToRoutine?: boolean;
107
+ /** Stable successful response of GET /users/:identifier/routines. */
108
+ interface MemberRoutinesResponse {
109
+ routines: SharedRoutineSummary[];
215
110
  }
216
111
  /**
217
- * Stable successful response of the substitution PUT and DELETE. A swap is
218
- * refused once the slot has a completed set; incomplete drafts for the slot
219
- * are cleared because their values belonged to the other exercise.
112
+ * POST /routines/clones. Exactly one source, matching the two ways a routine
113
+ * can be read: `token` for a private link, `routineId` for one the viewer may
114
+ * already see. Sending both, or neither, is refused.
220
115
  */
221
- interface SubstituteSessionExerciseResponse {
222
- session: WorkoutSession;
223
- /** True only when `applyToRoutine` was asked for and the routine still has the slot. */
224
- routineUpdated: boolean;
225
- }
226
- type PersonalRecordKind = 'WEIGHT' | 'REPS' | 'VOLUME' | 'ESTIMATED_1RM';
227
- /** A record frontier crossed by the set mutation that produced this response. */
228
- interface EarnedPersonalRecord {
229
- kind: PersonalRecordKind;
230
- exerciseId: string;
231
- exerciseName: string;
232
- /** Canonical kg for every kind except REPS, where this is a repetition count. */
233
- value: number;
234
- /** Missing when this is the first completed result for that record kind. */
235
- previousBest?: number | null;
236
- }
237
- /** Stable successful response of PUT /workouts/sessions/:id/set-logs. */
238
- interface UpsertSetLogResponse {
239
- setLog: SetLog;
240
- earnedRecords: EarnedPersonalRecord[];
116
+ interface CloneRoutineRequest {
117
+ /** The `/shared/routines/:token` link the reader followed. */
118
+ token?: string;
119
+ /** A routine the viewer may read under `ROUT-04` visibility. */
120
+ routineId?: string;
241
121
  }
242
- /** Final record frontier earned by one completed session. */
243
- interface SessionRecapRecord extends EarnedPersonalRecord {
122
+ /** The refusals a clone states by name rather than as a bare 4xx. */
123
+ declare const CLONE_ROUTINE_REFUSALS: {
124
+ /** Neither `token` nor `routineId`, or both at once. */
125
+ readonly SOURCE_REQUIRED: "SOURCE_REQUIRED";
126
+ /** An exercise the routine programs is no longer in the catalog. */
127
+ readonly UNKNOWN_EXERCISE: "UNKNOWN_EXERCISE";
128
+ };
129
+ type CloneRoutineRefusal = (typeof CLONE_ROUTINE_REFUSALS)[keyof typeof CLONE_ROUTINE_REFUSALS];
130
+
131
+ /**
132
+ * ROUT-11: a WEEKLY routine ties each day to a weekday; a ROTATION routine
133
+ * runs its days in `order`, each after the last completed one, whatever the
134
+ * weekday.
135
+ */
136
+ declare const ROUTINE_SCHEDULE_MODES: readonly ["WEEKLY", "ROTATION"];
137
+ type RoutineScheduleMode = (typeof ROUTINE_SCHEDULE_MODES)[number];
138
+ /** At most seven days in either mode. */
139
+ declare const ROUTINE_DAYS_MAX = 7;
140
+ declare const ROUTINE_DAY_NAME_MAX = 40;
141
+ /**
142
+ * How a routine day is named everywhere, including history snapshots taken
143
+ * before ROUT-11: its own name, else its weekday, else its rotation letter
144
+ * ("Day A" for order 0).
145
+ */
146
+ declare function routineDayLabel(day: {
147
+ name?: string | null;
148
+ dayOfWeek?: number | null;
149
+ order?: number | null;
150
+ }): string;
151
+ interface RoutineSet {
244
152
  setNumber: number;
245
- achievedAt: IsoDateString;
153
+ repType: RepType;
154
+ reps?: number | null;
155
+ minReps?: number | null;
156
+ maxReps?: number | null;
157
+ weight?: number | null;
158
+ rir?: number | null;
246
159
  }
247
- /** Metrics from the latest earlier execution of the same routine day. */
248
- interface PreviousSessionRecap {
249
- sessionId: string;
250
- endedAt: IsoDateString;
251
- durationSec: number;
252
- totalVolumeKg: number;
253
- completedSets: number;
254
- durationDeltaSec: number;
255
- volumeDeltaKg: number;
256
- completedSetsDelta: number;
160
+ interface RoutineExercise {
161
+ id: string;
162
+ order: number;
163
+ restSeconds: number;
164
+ note?: string | null;
165
+ progressionScheme: ProgressionScheme;
166
+ minWeightIncrement: number;
167
+ exercise: {
168
+ id: string;
169
+ name: string;
170
+ primaryMuscles?: MuscleGroup[];
171
+ secondaryMuscles?: MuscleGroup[];
172
+ };
173
+ sets: RoutineSet[];
257
174
  }
258
- /** Stable successful response of GET /workouts/sessions/:id/recap. */
259
- interface WorkoutSessionRecap {
260
- sessionId: string;
261
- routineName: string;
262
- dayName?: string | null;
263
- startedAt: IsoDateString;
264
- endedAt: IsoDateString;
265
- durationSec: number;
266
- totalVolumeKg: number;
267
- completedSets: number;
268
- notes?: string | null;
269
- /** LIVE-16: exercise notes of this workout, named, in the day's order. */
270
- exerciseNotes?: SessionRecapExerciseNote[];
271
- records: SessionRecapRecord[];
272
- progressionChanges: ProgressionChange[];
273
- previousSession: PreviousSessionRecap | null;
175
+ interface CreateRoutineExerciseInput {
176
+ exerciseId: string;
177
+ order?: number;
178
+ restSeconds: number;
179
+ note?: string;
180
+ progressionScheme: ProgressionScheme;
181
+ minWeightIncrement: number;
182
+ sets: RoutineSet[];
274
183
  }
275
- interface SessionRecapExerciseNote {
276
- routineExerciseId: string;
277
- exerciseName: string;
278
- note: string;
184
+ interface RoutineDay {
185
+ id: string;
186
+ /** 0=Sun..6=Sat on a WEEKLY routine; null on a ROTATION routine. */
187
+ dayOfWeek: number | null;
188
+ /** Optional label such as "Push" or "Upper A" (see `routineDayLabel`). */
189
+ name: string | null;
190
+ /** Rotation sequence, 0-based; also the display order. */
191
+ order: number;
192
+ exercises: RoutineExercise[];
279
193
  }
280
- /** Parts of a completed session's recap the owner may choose to share. */
281
- declare const SESSION_SHARE_FIELDS: readonly ["duration", "volume", "completedSets", "records", "progression", "notes"];
282
- type SessionShareField = (typeof SESSION_SHARE_FIELDS)[number];
283
- /** Pre-selected when creating a link; notes and prescriptions are opt-in. */
284
- declare const SESSION_SHARE_DEFAULT_FIELDS: SessionShareField[];
285
- declare const SESSION_SHARE_MAX_ACTIVE_LINKS = 10;
286
- interface CreateSessionShareRequest {
287
- fields: SessionShareField[];
194
+ interface CreateRoutineDayInput {
195
+ /** Required and unique on a WEEKLY routine; omitted or null on a ROTATION. */
196
+ dayOfWeek?: number | null;
197
+ name?: string | null;
198
+ order?: number;
199
+ exercises: CreateRoutineExerciseInput[];
288
200
  }
289
- /** An active share link, visible only to the session owner. */
290
- interface SessionShare {
201
+ interface Routine {
291
202
  id: string;
292
- sessionId: string;
293
- /** Unguessable identifier used in the public `/shared/sessions/:token` URL. */
294
- token: string;
295
- fields: SessionShareField[];
296
- createdAt: IsoDateString;
203
+ userId: string;
204
+ name: string;
205
+ description?: string | null;
206
+ isPeriodized: boolean;
207
+ isFavorite: boolean;
208
+ isCompleted: boolean;
209
+ scheduleMode: RoutineScheduleMode;
297
210
  /**
298
- * TRUST-04: a moderator has hidden the shared workout, so the link stops
299
- * resolving for everyone but its owner. The link is not revoked and the
300
- * session is untouched; the owner is told because a live-looking link that
301
- * opens for nobody is worse than one that says why.
211
+ * ROTATION only: the day after the one of the last completed session (an
212
+ * aborted session does not advance it), or the first day before any.
213
+ */
214
+ nextRotationDayId: string | null;
215
+ /**
216
+ * SCHED-07: weekdays (0=Sun..6=Sat) this weekly routine rests on by plan,
217
+ * never one of its training weekdays; always empty on a ROTATION routine.
218
+ */
219
+ restDays: number[];
220
+ /**
221
+ * SCHED-06: weekdays (0=Sun..6=Sat) a ROTATION routine trains on, sorted; the
222
+ * schedule places its days on them in order. Empty means any day, without
223
+ * dates; always empty on a WEEKLY routine.
224
+ */
225
+ rotationWeekdays: number[];
226
+ /**
227
+ * ROUT-04: who may read this routine, bounded by the account-level
228
+ * `PROF-06` routines rule. Owner-only field; it never appears in a shared
229
+ * read, where visibility is the reason the reader is there.
230
+ */
231
+ visibility: RoutineVisibility;
232
+ /**
233
+ * ROUT-06: present only on a routine that was cloned, and only with what
234
+ * this viewer is allowed to know about its source.
235
+ */
236
+ lineage?: RoutineLineage | null;
237
+ /**
238
+ * ROUT-07: owner-declared, both optional. They are claims about the
239
+ * programme that only its author can make, which is why they are stored
240
+ * here rather than derived from the author's `PROF-05` training identity.
241
+ */
242
+ goal?: TrainingGoal | null;
243
+ experienceLevel?: TrainingExperienceLevel | null;
244
+ /**
245
+ * TRUST-04: a moderator has hidden this routine from everyone but its
246
+ * owner. Owner-only field, and true on the owner's own read alone: a
247
+ * routine nobody else can open while its own visibility still reads
248
+ * "Public" would be a control with nothing behind it.
302
249
  */
303
250
  isHiddenByModeration?: boolean;
251
+ /** The baseline days. A block's days are in `trainingBlocks`, never here. */
252
+ days: RoutineDay[];
253
+ /**
254
+ * ROUT-15: every current block revision as a plan, past, active and future,
255
+ * ordered by start date. Owner-only; absent from any shared read. Resolve
256
+ * what a date trains with `resolveRoutinePlan`, never by reading this and
257
+ * `days` separately.
258
+ */
259
+ trainingBlocks?: RoutineTrainingBlockPlan[];
260
+ createdAt: IsoDateString;
261
+ updatedAt: IsoDateString;
304
262
  }
305
- /** Stable successful response of GET /workouts/sessions/:id/shares. */
306
- interface SessionShareListResponse {
307
- items: SessionShare[];
308
- }
309
- interface SharedSessionOwner {
310
- username: string;
263
+ interface CreateRoutineRequest {
311
264
  name: string;
312
- lastName?: string | null;
313
- avatarUrl?: string | null;
265
+ description?: string;
266
+ /** ROUT-07; omitted means undeclared, which is not the same as a default. */
267
+ goal?: TrainingGoal | null;
268
+ experienceLevel?: TrainingExperienceLevel | null;
269
+ isPeriodized: boolean;
270
+ /** Defaults to WEEKLY. Changing it on update requires `days`. */
271
+ scheduleMode?: RoutineScheduleMode;
272
+ /**
273
+ * Weekly routines only. Omitted on update keeps the stored rest days, minus
274
+ * any that became training weekdays.
275
+ */
276
+ restDays?: number[];
277
+ /**
278
+ * Rotation routines only. Omitted on update keeps the stored weekdays;
279
+ * switching to WEEKLY clears them.
280
+ */
281
+ rotationWeekdays?: number[];
282
+ days: CreateRoutineDayInput[];
314
283
  }
284
+ type UpdateRoutineRequest = Partial<CreateRoutineRequest>;
285
+ /** A routine keeps at most this many versions; saving past it is refused. */
286
+ declare const ROUTINE_VERSIONS_MAX = 20;
287
+ declare const ROUTINE_VERSION_NAME_MAX = 60;
315
288
  /**
316
- * Stable successful response of the unauthenticated
317
- * GET /shared/sessions/:token. Only the fields named in `fields` are present;
318
- * everything else is omitted rather than nulled. Weights are canonical kg and
319
- * `weightUnit` is the owner's display preference.
289
+ * SAVED is an intentional save; BEFORE_RESTORE is the setup a restore
290
+ * replaced, saved automatically so the restore can be undone.
320
291
  */
321
- interface SharedSessionRecap {
322
- fields: SessionShareField[];
323
- owner: SharedSessionOwner;
324
- weightUnit: WeightUnit;
325
- routineName: string;
326
- dayName?: string | null;
327
- endedAt: IsoDateString;
328
- durationSec?: number;
329
- totalVolumeKg?: number;
330
- completedSets?: number;
331
- records?: SessionRecapRecord[];
332
- progressionChanges?: ProgressionChange[];
333
- notes?: string | null;
334
- /** LIVE-16: shared with the `notes` field. */
335
- exerciseNotes?: SessionRecapExerciseNote[];
336
- }
337
- interface WorkoutSessionSummary {
338
- id: string;
339
- status: WorkoutSessionStatus;
340
- startedAt: IsoDateString;
341
- endedAt?: IsoDateString | null;
342
- durationSec?: number | null;
343
- totalVolume?: number | null;
344
- totalSets?: number | null;
345
- totalExercises?: number | null;
346
- notes?: string | null;
347
- routine: {
292
+ declare const ROUTINE_VERSION_KINDS: readonly ["SAVED", "BEFORE_RESTORE"];
293
+ type RoutineVersionKind = (typeof ROUTINE_VERSION_KINDS)[number];
294
+ interface RoutineVersionExercise {
295
+ /** The catalog exercise and its name when the version was saved. */
296
+ exercise: {
348
297
  id: string;
349
298
  name: string;
350
- dayName?: string | null;
351
299
  };
300
+ order: number;
301
+ restSeconds: number;
302
+ note: string | null;
303
+ progressionScheme: ProgressionScheme;
304
+ minWeightIncrement: number;
305
+ sets: RoutineSet[];
352
306
  }
353
- interface ListSessionsParams {
354
- status?: WorkoutSessionStatus;
355
- routineId?: string;
356
- from?: IsoDateString;
357
- to?: IsoDateString;
358
- q?: string;
359
- cursor?: string;
360
- limit?: number;
361
- sort?: 'finishedAt:desc' | 'finishedAt:asc' | 'startedAt:desc' | 'startedAt:asc';
307
+ interface RoutineVersionDay {
308
+ dayOfWeek: number | null;
309
+ name: string | null;
310
+ order: number;
311
+ exercises: RoutineVersionExercise[];
362
312
  }
363
- type WorkoutSessionListResponse = PaginatedResponse<WorkoutSessionSummary>;
364
- interface WorkoutStatsQuery {
365
- weekStart: IsoDateString;
366
- weekEnd: IsoDateString;
367
- timeZone: string;
313
+ /** Everything a routine edit can change, as it was when the version was saved. */
314
+ interface RoutineVersionSetup {
315
+ name: string;
316
+ description: string | null;
317
+ scheduleMode: RoutineScheduleMode;
318
+ restDays: number[];
319
+ /** SCHED-06; absent in versions saved before it, which means none. */
320
+ rotationWeekdays?: number[];
321
+ days: RoutineVersionDay[];
368
322
  }
369
- interface WorkoutStatsResponse {
370
- totalCompleted: number;
371
- completionRate: number;
372
- weeklyWorkoutsCount: number;
373
- activeDaysThisWeek: number;
323
+ interface RoutineVersion {
324
+ id: string;
325
+ routineId: string;
326
+ /** 1, 2, 3… per routine, never reused after a deletion. */
327
+ number: number;
328
+ name: string | null;
329
+ kind: RoutineVersionKind;
330
+ /** BEFORE_RESTORE only: the number of the version that was restored. */
331
+ restoredVersionNumber: number | null;
332
+ createdAt: IsoDateString;
333
+ setup: RoutineVersionSetup;
374
334
  }
375
-
376
- declare const TRAINING_EVENT_TYPES: readonly ["SESSION_COMPLETED", "PERSONAL_RECORD", "PROGRESSION_CHANGED", "STREAK_MILESTONE", "ACHIEVEMENT_UNLOCKED"];
377
- type TrainingEventType = (typeof TRAINING_EVENT_TYPES)[number];
378
- /** Stable successful response of GET /workouts/progress. */
379
- interface WorkoutProgressQuery {
380
- timeZone: string;
335
+ /** `GET /routines/:id/versions`, newest first. */
336
+ interface RoutineVersionsResponse {
337
+ versions: RoutineVersion[];
338
+ max: number;
381
339
  }
382
- interface PersonalRecordEntry {
383
- exerciseId: string;
384
- exerciseName: string;
385
- weight: number;
386
- reps: number;
387
- estimated1rm: number;
388
- achievedAt: string;
340
+ /** `POST /routines/:id/versions` */
341
+ interface CreateRoutineVersionRequest {
342
+ name?: string | null;
389
343
  }
390
- interface RecentActivityEntry {
391
- sessionId: string;
344
+ /** `POST /routines/:id/versions/:versionId/restore` */
345
+ interface RestoreRoutineVersionResponse {
346
+ routine: Routine;
347
+ /** The replaced setup, saved as a BEFORE_RESTORE version. */
348
+ savedVersion: RoutineVersion;
349
+ }
350
+
351
+ /** A local calendar date, YYYY-MM-DD. */
352
+ type CalendarDate = string;
353
+ /**
354
+ * The schedule-instance model: per-date overrides of a routine's plan. MOVE
355
+ * puts one weekly occurrence on another date (SCHED-04); SKIP marks it
356
+ * skipped on purpose (SCHED-05). Overrides are kept by calendar date, not by
357
+ * routine day, because routine edits replace the days.
358
+ */
359
+ declare const SCHEDULE_OVERRIDE_KINDS: readonly ["MOVE", "SKIP"];
360
+ type ScheduleOverrideKind = (typeof SCHEDULE_OVERRIDE_KINDS)[number];
361
+ /** A move goes at most this many days before or after the planned date. */
362
+ declare const SCHEDULE_MOVE_MAX_DAYS = 6;
363
+ /**
364
+ * SCHED-05: a skip can explain a planned day up to this many days back;
365
+ * ahead, any planned day can be skipped.
366
+ */
367
+ declare const SCHEDULE_SKIP_PAST_DAYS = 6;
368
+ /** Overrides are read for at most this many days at once. */
369
+ declare const SCHEDULE_OVERRIDES_MAX_RANGE_DAYS = 62;
370
+ interface ScheduleOverride {
371
+ id: string;
392
372
  routineId: string;
393
- routineName: string;
394
- dayName: string;
395
- startedAt: string;
396
- endedAt: string | null;
397
- durationSec: number | null;
398
- totalVolumeKg: number;
399
- completedSets: number;
373
+ /** The planned date this override changes. */
374
+ date: CalendarDate;
375
+ kind: ScheduleOverrideKind;
376
+ /** MOVE: the date the occurrence now falls on; null for SKIP. */
377
+ toDate: CalendarDate | null;
378
+ createdAt: IsoDateString;
400
379
  }
401
- interface WorkoutProgressResponse {
402
- totalVolumeKg: number;
403
- currentStreakDays: number;
404
- bestStreakDays: number;
405
- personalRecords: PersonalRecordEntry[];
406
- recentActivity: RecentActivityEntry[];
380
+ /**
381
+ * `GET /schedule/overrides?from&to`: the owner's overrides whose date or
382
+ * target falls in the range, both ends included.
383
+ */
384
+ interface ScheduleOverridesQuery {
385
+ from: CalendarDate;
386
+ to: CalendarDate;
407
387
  }
408
- interface ExerciseStrengthTrendQuery {
409
- exerciseId?: string;
410
- from?: string;
411
- to?: string;
388
+ interface ScheduleOverridesResponse {
389
+ overrides: ScheduleOverride[];
412
390
  }
413
- interface ExerciseStrengthSummary {
414
- exerciseId: string;
415
- exerciseName: string;
416
- weightKg: number;
417
- reps: number;
418
- estimated1rmKg: number;
419
- achievedAt: string;
391
+ /** `PUT /schedule/overrides/move`: moves one occurrence, or moves it again. */
392
+ interface MoveOccurrenceRequest {
393
+ routineId: string;
394
+ date: CalendarDate;
395
+ toDate: CalendarDate;
420
396
  }
421
- interface ExerciseStrengthTrendPoint {
422
- sessionId: string;
423
- achievedAt: string;
424
- weightKg: number;
425
- reps: number;
426
- estimated1rmKg: number;
397
+ /**
398
+ * `PUT /schedule/overrides/skip`: marks one occurrence skipped. A moved
399
+ * occurrence is skipped as a whole; undoing is `DELETE` like a move.
400
+ */
401
+ interface SkipOccurrenceRequest {
402
+ routineId: string;
403
+ date: CalendarDate;
427
404
  }
428
- /** Stable successful response of GET /workouts/progress/strength. */
429
- interface ExerciseStrengthTrendResponse {
430
- exercises: ExerciseStrengthSummary[];
431
- selectedExercise: ExerciseStrengthSummary | null;
432
- range: {
433
- from: string | null;
434
- to: string;
435
- };
436
- baseline: ExerciseStrengthTrendPoint | null;
437
- points: ExerciseStrengthTrendPoint[];
438
- truncated: boolean;
405
+
406
+ /** One routine may have this many current, non-overlapping planned blocks. */
407
+ declare const ROUTINE_TRAINING_BLOCKS_MAX = 24;
408
+ /** A started block keeps at most this many immutable revisions. */
409
+ declare const ROUTINE_TRAINING_BLOCK_REVISIONS_MAX = 20;
410
+ declare const ROUTINE_TRAINING_BLOCK_NAME_MAX = 60;
411
+ declare const ROUTINE_TRAINING_BLOCK_STATES: readonly ["FUTURE", "ACTIVE", "COMPLETE"];
412
+ type RoutineTrainingBlockState = (typeof ROUTINE_TRAINING_BLOCK_STATES)[number];
413
+ declare const ROUTINE_TRAINING_BLOCK_SOURCE_KINDS: readonly ["CURRENT_ROUTINE", "SAVED_VERSION"];
414
+ type RoutineTrainingBlockSourceKind = (typeof ROUTINE_TRAINING_BLOCK_SOURCE_KINDS)[number];
415
+ /**
416
+ * The origin is snapshotted so a later routine edit or ROUT-08 version
417
+ * deletion cannot change what this block says it was authored from.
418
+ */
419
+ interface RoutineTrainingBlockSource {
420
+ kind: RoutineTrainingBlockSourceKind;
421
+ versionId: string | null;
422
+ versionNumber: number | null;
423
+ versionName: string | null;
439
424
  }
440
- interface ExercisePerformanceHistoryQuery {
441
- exerciseId?: string;
442
- from?: string;
443
- to?: string;
444
- cursor?: string;
445
- limit?: number;
425
+ /** One immutable revision of a named, dated setup. */
426
+ interface RoutineTrainingBlockRevision {
427
+ id: string;
428
+ routineId: string;
429
+ /** Stable across every replacement of the same logical block. */
430
+ seriesId: string;
431
+ /** 1, 2, 3… inside the series. */
432
+ revision: number;
433
+ name: string;
434
+ startDate: CalendarDate;
435
+ endDate: CalendarDate;
436
+ setup: RoutineVersionSetup;
437
+ source: RoutineTrainingBlockSource;
438
+ createdAt: IsoDateString;
439
+ /** Null only on the current revision. */
440
+ supersededAt: IsoDateString | null;
446
441
  }
447
- interface ExercisePerformanceSummary {
448
- exerciseId: string;
449
- exerciseName: string;
450
- lastPerformedAt: string;
451
- hasStrengthTrend: boolean;
442
+ /** A current block in the routine timeline. State is relative to `today`. */
443
+ interface RoutineTrainingBlock extends Omit<RoutineTrainingBlockRevision, 'supersededAt'> {
444
+ state: RoutineTrainingBlockState;
445
+ revisionCount: number;
452
446
  }
453
- interface ExercisePerformanceSet {
454
- routineExerciseId: string;
455
- setNumber: number;
456
- reps: number;
457
- weightKg?: number | null;
458
- rpe?: number | null;
447
+ /** `GET /routines/:id/training-blocks`, ordered by start date. */
448
+ interface RoutineTrainingBlocksResponse {
449
+ blocks: RoutineTrainingBlock[];
450
+ max: number;
451
+ revisionMax: number;
452
+ /** The owner's local date used to derive every state in this response. */
453
+ today: CalendarDate;
459
454
  }
460
- interface ExercisePerformancePrescriptionSet {
461
- setNumber: number;
462
- repType: RepType;
463
- reps?: number | null;
464
- minReps?: number | null;
465
- maxReps?: number | null;
466
- weightKg?: number | null;
467
- rir?: number | null;
455
+ /** `GET /routines/:id/training-blocks/:blockId/revisions`, newest first. */
456
+ interface RoutineTrainingBlockRevisionsResponse {
457
+ seriesId: string;
458
+ revisions: RoutineTrainingBlockRevision[];
459
+ max: number;
468
460
  }
469
- interface ExercisePerformancePrescription {
470
- routineExerciseId: string;
471
- restSeconds?: number | null;
472
- note?: string | null;
473
- progressionScheme: ProgressionScheme;
474
- minWeightIncrementKg: number;
475
- sets: ExercisePerformancePrescriptionSet[];
476
- }
477
- interface ExercisePerformanceSession {
478
- sessionId: string;
479
- status: Extract<WorkoutSessionStatus, 'COMPLETED' | 'ABORTED'>;
480
- routineName: string;
481
- dayName?: string | null;
482
- startedAt: string;
483
- endedAt: string;
484
- durationSec?: number | null;
485
- sessionNotes?: string | null;
486
- sets: ExercisePerformanceSet[];
487
- prescriptions: ExercisePerformancePrescription[];
488
- progressionChanges: ProgressionChange[];
489
- }
490
- /** Stable successful response of GET /workouts/progress/performance. */
491
- interface ExercisePerformanceHistoryResponse {
492
- exercises: ExercisePerformanceSummary[];
493
- selectedExercise: ExercisePerformanceSummary | null;
494
- items: ExercisePerformanceSession[];
495
- nextCursor?: string;
496
- }
497
- interface MuscleGroupHeatmapQuery {
498
- timeZone: string;
499
- /** Number of Monday-based calendar weeks to return, including this week. */
500
- weeks?: number;
501
- }
502
- interface MuscleGroupHeatmapValue {
503
- muscle: MuscleGroup;
504
- /** Completed-set equivalents: primary muscles count 1, secondary count 0.5. */
505
- weightedSets: number;
506
- }
507
- interface MuscleGroupHeatmapWeek {
508
- weekStart: string;
509
- isCurrentWeek: boolean;
510
- totalWeightedSets: number;
511
- muscles: MuscleGroupHeatmapValue[];
512
- }
513
- /** Stable successful response of GET /workouts/progress/muscles. */
514
- interface MuscleGroupHeatmapResponse {
515
- timeZone: string;
516
- weeks: MuscleGroupHeatmapWeek[];
517
- peakWeightedSets: number;
518
- }
519
- interface VolumeTrendQuery {
520
- timeZone: string;
521
- /** Number of Monday-based calendar weeks to return, including this week. */
522
- weeks?: number;
523
- }
524
- interface VolumeTrendPoint {
525
- weekStart: string;
526
- isCurrentWeek: boolean;
527
- /** External-load volume in canonical kilograms: weight × repetitions. */
528
- volumeKg: number;
529
- completedSets: number;
461
+ /**
462
+ * `POST /routines/:id/training-blocks` and
463
+ * `PUT /routines/:id/training-blocks/:blockId`.
464
+ * Null or omission captures the routine as it exists at request time.
465
+ */
466
+ interface UpsertRoutineTrainingBlockRequest {
467
+ name: string;
468
+ startDate: CalendarDate;
469
+ endDate: CalendarDate;
470
+ sourceVersionId?: string | null;
530
471
  }
531
- interface VolumeTrendSeries {
472
+ /**
473
+ * A current block as a plan the schedule, the dashboard and a session start
474
+ * use. Its days are the block's own working copy, with ids, kept apart from
475
+ * the routine's baseline `days`: a session of a block day logs against them
476
+ * and progression advances them, never the baseline, which resumes unchanged
477
+ * once the block ends. A new revision starts again from its authored setup.
478
+ */
479
+ interface RoutineTrainingBlockPlan {
480
+ /** The current revision's id. */
532
481
  id: string;
482
+ seriesId: string;
483
+ revision: number;
533
484
  name: string;
534
- totalVolumeKg: number;
535
- totalCompletedSets: number;
536
- points: VolumeTrendPoint[];
537
- }
538
- interface MuscleVolumeTrendSeries extends VolumeTrendSeries {
539
- id: MuscleGroup;
540
- }
541
- /** Stable successful response of GET /workouts/progress/volume. */
542
- interface VolumeTrendResponse {
543
- timeZone: string;
544
- weeks: number;
545
- overall: VolumeTrendPoint[];
546
- muscles: MuscleVolumeTrendSeries[];
547
- routines: VolumeTrendSeries[];
548
- exercises: VolumeTrendSeries[];
485
+ startDate: CalendarDate;
486
+ endDate: CalendarDate;
487
+ scheduleMode: RoutineScheduleMode;
488
+ restDays: number[];
489
+ rotationWeekdays: number[];
490
+ /**
491
+ * ROTATION only: the day after the last completed session of this block
492
+ * (any revision), or its first day before any. Never a baseline day.
493
+ */
494
+ nextRotationDayId: string | null;
495
+ days: RoutineDay[];
549
496
  }
550
- interface SessionComparisonQuery {
551
- routineDayId?: string;
497
+ /** ROUT-15: the block a session trained, copied onto the session itself. */
498
+ interface SessionTrainingBlock {
499
+ /** The revision trained. */
500
+ id: string;
501
+ seriesId: string;
502
+ revision: number;
503
+ name: string;
552
504
  }
553
- interface SessionComparisonRoutineDay {
554
- routineDayId: string;
555
- routineId: string;
556
- routineName: string;
557
- dayName?: string | null;
558
- lastCompletedAt: IsoDateString;
559
- completedSessionCount: number;
505
+ type DatedBlock = {
506
+ startDate: CalendarDate;
507
+ endDate: CalendarDate;
508
+ };
509
+ /** Both ends are inclusive; dates compare as `YYYY-MM-DD` strings. */
510
+ declare function trainingBlockCovers(block: DatedBlock, date: CalendarDate): boolean;
511
+ /**
512
+ * The one active block for a date, or null. Blocks of one routine never
513
+ * overlap (ROUT-09), so the first cover is the only one.
514
+ */
515
+ declare function resolveActiveTrainingBlock<T extends DatedBlock>(blocks: readonly T[] | null | undefined, date: CalendarDate): T | null;
516
+ /** What a routine plans for one date: its active block, else its baseline. */
517
+ interface ResolvedRoutinePlan<D> {
518
+ block: SessionTrainingBlock & DatedBlock | null;
519
+ scheduleMode: RoutineScheduleMode;
520
+ restDays: number[];
521
+ rotationWeekdays: number[];
522
+ nextRotationDayId: string | null;
523
+ days: D[];
560
524
  }
561
- interface SessionComparisonSet {
525
+ interface RoutinePlanSource<D> {
526
+ scheduleMode: RoutineScheduleMode;
527
+ restDays: number[];
528
+ rotationWeekdays?: number[] | null;
529
+ nextRotationDayId?: string | null;
530
+ days: D[];
531
+ trainingBlocks?: ReadonlyArray<DatedBlock & SessionTrainingBlock & {
532
+ scheduleMode: RoutineScheduleMode;
533
+ restDays: number[];
534
+ rotationWeekdays: number[];
535
+ nextRotationDayId: string | null;
536
+ days: D[];
537
+ }> | null;
538
+ }
539
+ /**
540
+ * ROUT-15's single resolver. Every reader that decides what a routine trains
541
+ * on a date - the schedule, the dashboard, a session start, reminders and the
542
+ * partner schedule, on the frontend and the backend alike - asks this, so
543
+ * weekly and rotation routines, and the baseline and a block, never disagree.
544
+ */
545
+ declare function resolveRoutinePlan<D>(routine: RoutinePlanSource<D>, date: CalendarDate): ResolvedRoutinePlan<D>;
546
+
547
+ interface SetLog {
548
+ id: string;
549
+ sessionId: string;
562
550
  routineExerciseId: string;
551
+ exerciseId: string;
563
552
  setNumber: number;
564
553
  reps: number;
565
- weightKg?: number | null;
554
+ weight?: number | null;
566
555
  rpe?: number | null;
556
+ isCompleted: boolean;
557
+ completedAt?: IsoDateString | null;
558
+ createdAt: IsoDateString;
559
+ updatedAt: IsoDateString;
567
560
  }
568
- interface SessionComparisonExercise {
561
+ interface WorkoutSession {
562
+ id: string;
563
+ userId: string;
564
+ routineId: string;
565
+ routineDayId: string;
566
+ status: WorkoutSessionStatus;
567
+ startedAt: IsoDateString;
568
+ endedAt?: IsoDateString | null;
569
+ durationSec?: number | null;
570
+ notes?: string | null;
571
+ lastActivityAt?: IsoDateString | null;
572
+ setLogs?: SetLog[];
573
+ /**
574
+ * LIVE-11: slots performed with a different exercise in this session. The
575
+ * routine day (snapshot) keeps the original prescription; set logs for a
576
+ * substituted slot carry the substitute's `exerciseId`.
577
+ */
578
+ exerciseSubstitutions?: SessionExerciseSubstitution[];
579
+ /**
580
+ * LIVE-16: what the owner noted about one exercise in this workout. The
581
+ * routine's own note (`routineDay.exercises[].note`) is the standing
582
+ * instruction and is never written from a session.
583
+ */
584
+ exerciseNotes?: SessionExerciseNote[];
585
+ reused?: boolean;
586
+ /**
587
+ * ROUT-15: the training block this session trained, or null when it
588
+ * trained the routine's baseline. Copied at start, so a later revision or
589
+ * the block's end never rewrites what the session was.
590
+ */
591
+ trainingBlock?: SessionTrainingBlock | null;
592
+ routine?: {
593
+ id: string;
594
+ name: string;
595
+ description?: string | null;
596
+ };
597
+ routineDay?: {
598
+ id: string;
599
+ name?: string | null;
600
+ order?: number;
601
+ /** Null on a ROTATION routine day (ROUT-11). */
602
+ dayOfWeek?: number | null;
603
+ exercises: Array<{
604
+ id: string;
605
+ order: number;
606
+ restSeconds?: number | null;
607
+ note?: string | null;
608
+ progressionScheme: ProgressionScheme;
609
+ minWeightIncrement: number;
610
+ exercise: {
611
+ id: string;
612
+ name: string;
613
+ primaryMuscles: MuscleGroup[];
614
+ secondaryMuscles?: MuscleGroup[];
615
+ };
616
+ sets: Array<{
617
+ id: string;
618
+ setNumber: number;
619
+ repType: RepType;
620
+ reps?: number | null;
621
+ minReps?: number | null;
622
+ maxReps?: number | null;
623
+ weight?: number | null;
624
+ rir?: number | null;
625
+ }>;
626
+ }>;
627
+ };
628
+ }
629
+ /** Completed set from the latest earlier execution of the same routine day. */
630
+ interface PreviousSetPerformance {
569
631
  routineExerciseId: string;
570
632
  exerciseId: string;
571
- exerciseName: string;
572
- order: number;
573
- sets: SessionComparisonSet[];
633
+ setNumber: number;
634
+ reps: number;
635
+ weight?: number | null;
636
+ rpe?: number | null;
574
637
  }
575
- interface SessionComparisonSession {
638
+ /** Stable successful response of GET /workouts/sessions/:id/previous-performance. */
639
+ interface PreviousPerformanceResponse {
576
640
  sessionId: string;
577
- routineDayId: string;
578
- routineName: string;
579
- dayName?: string | null;
580
- startedAt: IsoDateString;
581
641
  endedAt: IsoDateString;
582
- durationSec: number;
583
- totalVolumeKg: number;
584
- completedSets: number;
585
- notes?: string | null;
586
- exercises: SessionComparisonExercise[];
642
+ sets: PreviousSetPerformance[];
587
643
  }
588
- /** Stable successful response of GET /workouts/progress/session-comparison. */
589
- interface SessionComparisonResponse {
590
- routineDays: SessionComparisonRoutineDay[];
591
- selectedRoutineDay: SessionComparisonRoutineDay | null;
592
- latestSession: SessionComparisonSession | null;
593
- previousSession: SessionComparisonSession | null;
644
+ interface StartWorkoutRequest {
645
+ routineId: string;
646
+ routineDayId: string;
647
+ notes?: string;
594
648
  }
595
- declare const PROGRESS_TIMELINE_EVENT_TYPES: readonly ["PERSONAL_RECORD", "PROGRESSION_CHANGED"];
596
- type ProgressTimelineEventType = (typeof PROGRESS_TIMELINE_EVENT_TYPES)[number];
597
- interface ProgressTimelineQuery {
598
- type?: ProgressTimelineEventType;
599
- /** Only events for one catalog exercise (EXER-01). */
600
- exerciseId?: string;
601
- cursor?: string;
602
- limit?: number;
649
+ interface StartWorkoutResponse {
650
+ id: string;
651
+ routineId: string;
652
+ routineDayId: string;
653
+ status: WorkoutSessionStatus;
654
+ startedAt: IsoDateString;
655
+ endedAt?: IsoDateString | null;
656
+ reused: boolean;
603
657
  }
604
- interface ProgressTimelineSessionContext {
605
- sessionId: string;
606
- routineName: string;
607
- dayName?: string | null;
658
+ type FinishStatus = Extract<WorkoutSessionStatus, 'COMPLETED' | 'ABORTED'>;
659
+ interface FinishWorkoutRequest {
660
+ status: FinishStatus;
661
+ notes?: string;
608
662
  }
609
- interface ProgressTimelineRecordPerformance {
610
- weightKg: number;
611
- reps: number;
612
- estimated1rmKg: number;
613
- }
614
- type ProgressTimelineRecordReason = 'FIRST_RECORDED_BEST' | 'HEAVIER_LOAD' | 'MORE_REPS_AT_SAME_LOAD';
615
- interface ProgressTimelinePersonalRecordItem {
616
- eventId: string;
617
- type: 'PERSONAL_RECORD';
618
- occurredAt: IsoDateString;
619
- session: ProgressTimelineSessionContext;
620
- exerciseId: string;
621
- exerciseName: string;
622
- current: ProgressTimelineRecordPerformance;
623
- previous: ProgressTimelineRecordPerformance | null;
624
- reason: ProgressTimelineRecordReason;
663
+ type ProgressionRule = 'ALL_SETS_REACHED_TARGET' | 'SET_REACHED_TARGET';
664
+ interface ProgressionSetChange {
665
+ setNumber: number;
666
+ targetReps: number;
667
+ performedReps: number;
668
+ previousWeightKg: number;
669
+ newWeightKg: number;
625
670
  }
626
- interface ProgressTimelineProgressionItem {
627
- eventId: string;
628
- type: 'PROGRESSION_CHANGED';
629
- occurredAt: IsoDateString;
630
- session: ProgressTimelineSessionContext;
671
+ /** An automatic prescription update produced while completing a session. */
672
+ interface ProgressionChange {
673
+ routineExerciseId: string;
631
674
  exerciseId: string;
632
675
  exerciseName: string;
633
- change: ProgressionChange;
676
+ progressionScheme: Extract<ProgressionScheme, 'DOUBLE_PROGRESSION' | 'DYNAMIC_DOUBLE_PROGRESSION'>;
677
+ rule: ProgressionRule;
678
+ minWeightIncrementKg: number;
679
+ sets: ProgressionSetChange[];
634
680
  }
635
- type ProgressTimelineItem = ProgressTimelinePersonalRecordItem | ProgressTimelineProgressionItem;
636
- /** Stable successful response of GET /workouts/progress/timeline. */
637
- interface ProgressTimelineResponse {
638
- items: ProgressTimelineItem[];
639
- nextCursor?: string;
681
+ /** Stable successful response of PATCH /workouts/sessions/:id/finish. */
682
+ interface FinishWorkoutResponse {
683
+ session: WorkoutSession;
684
+ progressionChanges: ProgressionChange[];
685
+ recap: WorkoutSessionRecap | null;
640
686
  }
641
- /** Immutable prescription captured before any sets or progression. */
642
- interface WorkoutSessionSnapshotV1 {
643
- schemaVersion: 1;
644
- sessionId: string;
645
- sourceRoutineId: string;
646
- sourceRoutineDayId: string;
647
- capturedAt: string;
648
- provenance: 'CAPTURED' | 'APPROXIMATED';
649
- notes: string | null;
650
- routine: NonNullable<WorkoutSession['routine']>;
651
- routineDay: NonNullable<WorkoutSession['routineDay']>;
687
+ interface UpsertSetLogRequest {
688
+ routineExerciseId: string;
689
+ exerciseId: string;
690
+ setNumber: number;
691
+ reps: number;
692
+ weight?: number;
693
+ rpe?: number;
694
+ isCompleted?: boolean;
652
695
  }
653
- interface SetAccountTimeZoneRequest {
654
- timeZone: string;
655
- /** Bootstrap must not replace a zone registered by another device. */
656
- onlyIfUnset?: boolean;
696
+ /**
697
+ * LIVE-11: an exercise performed in place of one routine slot for a single
698
+ * session. The substitute's muscles are captured when the swap is made, so
699
+ * later catalog changes cannot rewrite what the session trained.
700
+ */
701
+ interface SessionExerciseSubstitution {
702
+ /** The replaced slot, as in `routineDay.exercises[].id`. */
703
+ routineExerciseId: string;
704
+ exercise: {
705
+ id: string;
706
+ name: string;
707
+ primaryMuscles: MuscleGroup[];
708
+ secondaryMuscles: MuscleGroup[];
709
+ };
710
+ substitutedAt: IsoDateString;
657
711
  }
658
- interface WorkoutAnalyticsStatus {
659
- timeZone: string | null;
660
- requestedTimeZone: string | null;
661
- state: 'UNINITIALIZED' | 'BUILDING' | 'READY' | 'FAILED';
662
- generationId: string | null;
712
+ declare const SESSION_NOTE_MAX_LENGTH = 2000;
713
+ declare const SESSION_EXERCISE_NOTE_MAX_LENGTH = 500;
714
+ /** One exercise's note for one workout, keyed by its routine slot. */
715
+ interface SessionExerciseNote {
716
+ /** The slot, as in `routineDay.exercises[].id`. */
717
+ routineExerciseId: string;
718
+ note: string;
663
719
  }
664
720
  /**
665
- * Thresholds behind every plateau. A lift is flagged only when all hold: at
666
- * least the account's minimum sessions (`PLATEAU_MIN_SESSIONS` by default)
667
- * of terminal sessions with completed loaded sets of it since its current
668
- * best (the session that set the best excluded, and counting only inside the
669
- * look-back window), the best is at least
670
- * `PLATEAU_MIN_DAYS_SINCE_BEST` days old, and the lift was trained within the
671
- * last `PLATEAU_RECENT_DAYS` days. They describe numbers, never a cause.
721
+ * Body of PUT /workouts/sessions/:id/notes. Partial: an omitted field keeps
722
+ * what is stored, `null` or an empty string clears it. Exercise notes merge
723
+ * by slot, so two notes saved moments apart cannot overwrite each other.
672
724
  */
673
- declare const PLATEAU_WINDOW_DAYS = 56;
674
- declare const PLATEAU_MIN_SESSIONS = 4;
675
- declare const PLATEAU_MIN_DAYS_SINCE_BEST = 21;
676
- declare const PLATEAU_RECENT_DAYS = 21;
725
+ interface UpdateSessionNotesRequest {
726
+ notes?: string | null;
727
+ exerciseNotes?: Array<{
728
+ routineExerciseId: string;
729
+ note: string | null;
730
+ }>;
731
+ }
732
+ interface UpdateSessionNotesResponse {
733
+ notes: string | null;
734
+ exerciseNotes: SessionExerciseNote[];
735
+ }
736
+ /** Body of PUT /workouts/sessions/:id/exercises/:routineExerciseId/substitution. */
737
+ interface SubstituteSessionExerciseRequest {
738
+ exerciseId: string;
739
+ /** Also use this exercise in the routine from the next session on. */
740
+ applyToRoutine?: boolean;
741
+ }
677
742
  /**
678
- * PREF-05: each account chooses its minimum sessions within these bounds;
679
- * `PLATEAU_MIN_SESSIONS` is the default. The other thresholds stay fixed.
743
+ * Stable successful response of the substitution PUT and DELETE. A swap is
744
+ * refused once the slot has a completed set; incomplete drafts for the slot
745
+ * are cleared because their values belonged to the other exercise.
680
746
  */
681
- declare const PLATEAU_MIN_SESSIONS_MIN = 3;
682
- declare const PLATEAU_MIN_SESSIONS_MAX = 8;
683
- /** Body and response of PUT /users/preferences/plateaus. */
684
- interface PlateauPreferences {
685
- minSessions: number;
686
- }
687
- interface PlateauSet {
688
- weightKg: number;
689
- reps: number;
690
- estimated1rmKg: number;
747
+ interface SubstituteSessionExerciseResponse {
748
+ session: WorkoutSession;
749
+ /** True only when `applyToRoutine` was asked for and the routine still has the slot. */
750
+ routineUpdated: boolean;
691
751
  }
692
- interface ExercisePlateau {
752
+ type PersonalRecordKind = 'WEIGHT' | 'REPS' | 'VOLUME' | 'ESTIMATED_1RM';
753
+ /** A record frontier crossed by the set mutation that produced this response. */
754
+ interface EarnedPersonalRecord {
755
+ kind: PersonalRecordKind;
693
756
  exerciseId: string;
694
757
  exerciseName: string;
695
- /** The persisted best-set frontier. */
696
- best: PlateauSet & {
697
- achievedAt: IsoDateString;
698
- };
699
- /** Sessions were counted from the later of the best and the window start. */
700
- countedSince: IsoDateString;
701
- sessionsWithoutNewBest: number;
702
- /** The set since then with the highest estimated 1RM (latest on ties). */
703
- closest: PlateauSet & {
704
- performedAt: IsoDateString;
705
- };
706
- /** closest.estimated1rmKg / best.estimated1rmKg. */
707
- closestRatio: number;
708
- lastPerformedAt: IsoDateString;
709
- }
710
- /** Stable successful response of GET /workouts/progress/plateaus. */
711
- interface PlateausResponse {
712
- asOf: IsoDateString;
713
- thresholds: {
714
- windowDays: number;
715
- /** The account's PREF-05 choice, within the bounds above. */
716
- minSessions: number;
717
- minDaysSinceBest: number;
718
- recentDays: number;
719
- };
720
- /** Lifts with a best set that were trained within the recent days. */
721
- checkedExercises: number;
722
- /** Longest-running first: most sessions without a new best. */
723
- plateaus: ExercisePlateau[];
758
+ /** Canonical kg for every kind except REPS, where this is a repetition count. */
759
+ value: number;
760
+ /** Missing when this is the first completed result for that record kind. */
761
+ previousBest?: number | null;
724
762
  }
725
-
726
- declare const ACHIEVEMENT_CATEGORIES: readonly ["SESSIONS", "SETS", "VOLUME_KG", "RECORDS", "STREAK_DAYS"];
727
- type AchievementCategory = (typeof ACHIEVEMENT_CATEGORIES)[number];
728
- interface AchievementDefinition {
729
- id: string;
730
- category: AchievementCategory;
731
- threshold: number;
732
- title: string;
733
- description: string;
763
+ /** Stable successful response of PUT /workouts/sessions/:id/set-logs. */
764
+ interface UpsertSetLogResponse {
765
+ setLog: SetLog;
766
+ earnedRecords: EarnedPersonalRecord[];
734
767
  }
735
- /** Stable milestone catalog shared by the writer and every presentation. */
736
- declare const ACHIEVEMENT_DEFINITIONS: readonly AchievementDefinition[];
737
- interface AchievementUnlockedEventPayload extends AchievementDefinition {
738
- schemaVersion: 1;
739
- backfilled: boolean;
768
+ /** Final record frontier earned by one completed session. */
769
+ interface SessionRecapRecord extends EarnedPersonalRecord {
770
+ setNumber: number;
771
+ achievedAt: IsoDateString;
740
772
  }
741
- interface StreakMilestoneEventPayload {
742
- schemaVersion: 1;
743
- achievementId: string;
744
- streakDays: number;
745
- backfilled: boolean;
773
+ /** Metrics from the latest earlier execution of the same routine day. */
774
+ interface PreviousSessionRecap {
775
+ sessionId: string;
776
+ endedAt: IsoDateString;
777
+ durationSec: number;
778
+ totalVolumeKg: number;
779
+ completedSets: number;
780
+ durationDeltaSec: number;
781
+ volumeDeltaKg: number;
782
+ completedSetsDelta: number;
746
783
  }
747
- interface EarnedAchievement extends AchievementDefinition {
748
- eventId: string;
749
- unlockedAt: IsoDateString;
750
- sourceSessionId: string | null;
751
- /** True when existing verified history was recognized after ACH-01 shipped. */
752
- backfilled: boolean;
784
+ /** Stable successful response of GET /workouts/sessions/:id/recap. */
785
+ interface WorkoutSessionRecap {
786
+ sessionId: string;
787
+ routineName: string;
788
+ dayName?: string | null;
789
+ startedAt: IsoDateString;
790
+ endedAt: IsoDateString;
791
+ durationSec: number;
792
+ totalVolumeKg: number;
793
+ completedSets: number;
794
+ notes?: string | null;
795
+ /** LIVE-16: exercise notes of this workout, named, in the day's order. */
796
+ exerciseNotes?: SessionRecapExerciseNote[];
797
+ records: SessionRecapRecord[];
798
+ progressionChanges: ProgressionChange[];
799
+ previousSession: PreviousSessionRecap | null;
753
800
  }
754
- interface RenaissanceRankDefinition {
755
- id: 'INITIATE' | 'APPRENTICE' | 'ARTISAN' | 'MAESTRO' | 'VIRTUOSO' | 'LAUREATE';
756
- title: string;
757
- description: string;
758
- minimumSessions: number;
759
- minimumActiveWeeks: number;
801
+ interface SessionRecapExerciseNote {
802
+ routineExerciseId: string;
803
+ exerciseName: string;
804
+ note: string;
760
805
  }
761
- /**
762
- * Stable rank ladder. Both requirements are intentionally based on attendance:
763
- * completed sessions reward participation, while distinct active weeks reward
764
- * consistency without encouraging consecutive-day training.
765
- */
766
- declare const RENAISSANCE_RANK_DEFINITIONS: readonly [{
767
- readonly id: "INITIATE";
768
- readonly title: "Initiate";
769
- readonly description: "Your place in the training ledger begins here.";
770
- readonly minimumSessions: 0;
771
- readonly minimumActiveWeeks: 0;
772
- }, {
773
- readonly id: "APPRENTICE";
774
- readonly title: "Apprentice";
775
- readonly description: "Learning the craft through regular practice.";
776
- readonly minimumSessions: 5;
777
- readonly minimumActiveWeeks: 3;
778
- }, {
779
- readonly id: "ARTISAN";
780
- readonly title: "Artisan";
781
- readonly description: "Building a dependable training practice.";
782
- readonly minimumSessions: 15;
783
- readonly minimumActiveWeeks: 8;
784
- }, {
785
- readonly id: "MAESTRO";
786
- readonly title: "Maestro";
787
- readonly description: "Sustaining purposeful work across many weeks.";
788
- readonly minimumSessions: 30;
789
- readonly minimumActiveWeeks: 16;
790
- }, {
791
- readonly id: "VIRTUOSO";
792
- readonly title: "Virtuoso";
793
- readonly description: "Showing enduring discipline through repeated seasons.";
794
- readonly minimumSessions: 60;
795
- readonly minimumActiveWeeks: 32;
796
- }, {
797
- readonly id: "LAUREATE";
798
- readonly title: "Laureate";
799
- readonly description: "A lasting training practice recorded in the ledger.";
800
- readonly minimumSessions: 100;
801
- readonly minimumActiveWeeks: 52;
802
- }];
803
- interface RenaissanceRankProgress {
804
- currentRank: RenaissanceRankDefinition;
805
- nextRank: RenaissanceRankDefinition | null;
806
- completedSessions: number;
807
- activeWeeks: number;
808
- sessionsRemaining: number;
809
- activeWeeksRemaining: number;
806
+ /** Parts of a completed session's recap the owner may choose to share. */
807
+ declare const SESSION_SHARE_FIELDS: readonly ["duration", "volume", "completedSets", "records", "progression", "notes"];
808
+ type SessionShareField = (typeof SESSION_SHARE_FIELDS)[number];
809
+ /** Pre-selected when creating a link; notes and prescriptions are opt-in. */
810
+ declare const SESSION_SHARE_DEFAULT_FIELDS: SessionShareField[];
811
+ declare const SESSION_SHARE_MAX_ACTIVE_LINKS = 10;
812
+ interface CreateSessionShareRequest {
813
+ fields: SessionShareField[];
810
814
  }
811
- /**
812
- * Current verified total and the next fixed catalog milestone for one category.
813
- * A null next milestone means the category's finite catalog is complete.
814
- */
815
- interface AchievementCategoryProgress {
816
- category: AchievementCategory;
817
- currentValue: number;
818
- nextMilestone: AchievementDefinition | null;
819
- remaining: number;
815
+ /** An active share link, visible only to the session owner. */
816
+ interface SessionShare {
817
+ id: string;
818
+ sessionId: string;
819
+ /** Unguessable identifier used in the public `/shared/sessions/:token` URL. */
820
+ token: string;
821
+ fields: SessionShareField[];
822
+ createdAt: IsoDateString;
823
+ /**
824
+ * TRUST-04: a moderator has hidden the shared workout, so the link stops
825
+ * resolving for everyone but its owner. The link is not revoked and the
826
+ * session is untouched; the owner is told because a live-looking link that
827
+ * opens for nobody is worse than one that says why.
828
+ */
829
+ isHiddenByModeration?: boolean;
830
+ }
831
+ /** Stable successful response of GET /workouts/sessions/:id/shares. */
832
+ interface SessionShareListResponse {
833
+ items: SessionShare[];
834
+ }
835
+ interface SharedSessionOwner {
836
+ username: string;
837
+ name: string;
838
+ lastName?: string | null;
839
+ avatarUrl?: string | null;
820
840
  }
821
841
  /**
822
- * ACH-05 recognises a return only after the athlete has rebuilt a small,
823
- * recovery-compatible pattern. The break is counted as full local calendar
824
- * days without a completed session; repeated sessions on one date count once.
842
+ * Stable successful response of the unauthenticated
843
+ * GET /shared/sessions/:token. Only the fields named in `fields` are present;
844
+ * everything else is omitted rather than nulled. Weights are canonical kg and
845
+ * `weightUnit` is the owner's display preference.
825
846
  */
826
- declare const COMEBACK_MIN_INACTIVE_DAYS = 14;
827
- declare const COMEBACK_REQUIRED_ACTIVE_DAYS = 3;
828
- declare const COMEBACK_WINDOW_DAYS = 14;
829
- declare const COMEBACK_SESSION_LOOKBACK = 500;
830
- declare const COMEBACK_RECOGNITION_LIMIT = 20;
831
- interface ComebackRecognition {
847
+ interface SharedSessionRecap {
848
+ fields: SessionShareField[];
849
+ owner: SharedSessionOwner;
850
+ weightUnit: WeightUnit;
851
+ routineName: string;
852
+ dayName?: string | null;
853
+ endedAt: IsoDateString;
854
+ durationSec?: number;
855
+ totalVolumeKg?: number;
856
+ completedSets?: number;
857
+ records?: SessionRecapRecord[];
858
+ progressionChanges?: ProgressionChange[];
859
+ notes?: string | null;
860
+ /** LIVE-16: shared with the `notes` field. */
861
+ exerciseNotes?: SessionRecapExerciseNote[];
862
+ }
863
+ interface WorkoutSessionSummary {
832
864
  id: string;
833
- inactiveDays: number;
834
- returnedAt: IsoDateString;
835
- recognizedAt: IsoDateString;
836
- sourceSessionId: string;
837
- activeDays: number;
838
- /** Inclusive local-calendar span from the return through recognition. */
839
- windowDays: number;
865
+ status: WorkoutSessionStatus;
866
+ startedAt: IsoDateString;
867
+ endedAt?: IsoDateString | null;
868
+ durationSec?: number | null;
869
+ totalVolume?: number | null;
870
+ totalSets?: number | null;
871
+ totalExercises?: number | null;
872
+ notes?: string | null;
873
+ routine: {
874
+ id: string;
875
+ name: string;
876
+ dayName?: string | null;
877
+ /** ROUT-15: the block trained, when it was one. */
878
+ trainingBlockName?: string | null;
879
+ };
880
+ }
881
+ interface ListSessionsParams {
882
+ status?: WorkoutSessionStatus;
883
+ routineId?: string;
884
+ from?: IsoDateString;
885
+ to?: IsoDateString;
886
+ q?: string;
887
+ cursor?: string;
888
+ limit?: number;
889
+ sort?: 'finishedAt:desc' | 'finishedAt:asc' | 'startedAt:desc' | 'startedAt:asc';
890
+ }
891
+ type WorkoutSessionListResponse = PaginatedResponse<WorkoutSessionSummary>;
892
+ interface WorkoutStatsQuery {
893
+ weekStart: IsoDateString;
894
+ weekEnd: IsoDateString;
895
+ timeZone: string;
896
+ }
897
+ interface WorkoutStatsResponse {
898
+ totalCompleted: number;
899
+ completionRate: number;
900
+ weeklyWorkoutsCount: number;
901
+ activeDaysThisWeek: number;
902
+ }
903
+
904
+ declare const TRAINING_EVENT_TYPES: readonly ["SESSION_COMPLETED", "PERSONAL_RECORD", "PROGRESSION_CHANGED", "STREAK_MILESTONE", "ACHIEVEMENT_UNLOCKED"];
905
+ type TrainingEventType = (typeof TRAINING_EVENT_TYPES)[number];
906
+ /** Stable successful response of GET /workouts/progress. */
907
+ interface WorkoutProgressQuery {
908
+ timeZone: string;
909
+ }
910
+ interface PersonalRecordEntry {
911
+ exerciseId: string;
912
+ exerciseName: string;
913
+ weight: number;
914
+ reps: number;
915
+ estimated1rm: number;
916
+ achievedAt: string;
917
+ }
918
+ interface RecentActivityEntry {
919
+ sessionId: string;
920
+ routineId: string;
921
+ routineName: string;
922
+ dayName: string;
923
+ startedAt: string;
924
+ endedAt: string | null;
925
+ durationSec: number | null;
926
+ totalVolumeKg: number;
927
+ completedSets: number;
928
+ }
929
+ interface WorkoutProgressResponse {
930
+ totalVolumeKg: number;
931
+ currentStreakDays: number;
932
+ bestStreakDays: number;
933
+ personalRecords: PersonalRecordEntry[];
934
+ recentActivity: RecentActivityEntry[];
935
+ }
936
+ interface ExerciseStrengthTrendQuery {
937
+ exerciseId?: string;
938
+ from?: string;
939
+ to?: string;
940
+ }
941
+ interface ExerciseStrengthSummary {
942
+ exerciseId: string;
943
+ exerciseName: string;
944
+ weightKg: number;
945
+ reps: number;
946
+ estimated1rmKg: number;
947
+ achievedAt: string;
948
+ }
949
+ interface ExerciseStrengthTrendPoint {
950
+ sessionId: string;
951
+ achievedAt: string;
952
+ weightKg: number;
953
+ reps: number;
954
+ estimated1rmKg: number;
955
+ }
956
+ /** Stable successful response of GET /workouts/progress/strength. */
957
+ interface ExerciseStrengthTrendResponse {
958
+ exercises: ExerciseStrengthSummary[];
959
+ selectedExercise: ExerciseStrengthSummary | null;
960
+ range: {
961
+ from: string | null;
962
+ to: string;
963
+ };
964
+ baseline: ExerciseStrengthTrendPoint | null;
965
+ points: ExerciseStrengthTrendPoint[];
966
+ truncated: boolean;
967
+ }
968
+ interface ExercisePerformanceHistoryQuery {
969
+ exerciseId?: string;
970
+ from?: string;
971
+ to?: string;
972
+ cursor?: string;
973
+ limit?: number;
974
+ }
975
+ interface ExercisePerformanceSummary {
976
+ exerciseId: string;
977
+ exerciseName: string;
978
+ lastPerformedAt: string;
979
+ hasStrengthTrend: boolean;
980
+ }
981
+ interface ExercisePerformanceSet {
982
+ routineExerciseId: string;
983
+ setNumber: number;
984
+ reps: number;
985
+ weightKg?: number | null;
986
+ rpe?: number | null;
987
+ }
988
+ interface ExercisePerformancePrescriptionSet {
989
+ setNumber: number;
990
+ repType: RepType;
991
+ reps?: number | null;
992
+ minReps?: number | null;
993
+ maxReps?: number | null;
994
+ weightKg?: number | null;
995
+ rir?: number | null;
996
+ }
997
+ interface ExercisePerformancePrescription {
998
+ routineExerciseId: string;
999
+ restSeconds?: number | null;
1000
+ note?: string | null;
1001
+ progressionScheme: ProgressionScheme;
1002
+ minWeightIncrementKg: number;
1003
+ sets: ExercisePerformancePrescriptionSet[];
1004
+ }
1005
+ interface ExercisePerformanceSession {
1006
+ sessionId: string;
1007
+ status: Extract<WorkoutSessionStatus, 'COMPLETED' | 'ABORTED'>;
1008
+ routineName: string;
1009
+ dayName?: string | null;
1010
+ startedAt: string;
1011
+ endedAt: string;
1012
+ durationSec?: number | null;
1013
+ sessionNotes?: string | null;
1014
+ sets: ExercisePerformanceSet[];
1015
+ prescriptions: ExercisePerformancePrescription[];
1016
+ progressionChanges: ProgressionChange[];
1017
+ }
1018
+ /** Stable successful response of GET /workouts/progress/performance. */
1019
+ interface ExercisePerformanceHistoryResponse {
1020
+ exercises: ExercisePerformanceSummary[];
1021
+ selectedExercise: ExercisePerformanceSummary | null;
1022
+ items: ExercisePerformanceSession[];
1023
+ nextCursor?: string;
1024
+ }
1025
+ interface MuscleGroupHeatmapQuery {
1026
+ timeZone: string;
1027
+ /** Number of Monday-based calendar weeks to return, including this week. */
1028
+ weeks?: number;
1029
+ }
1030
+ interface MuscleGroupHeatmapValue {
1031
+ muscle: MuscleGroup;
1032
+ /** Completed-set equivalents: primary muscles count 1, secondary count 0.5. */
1033
+ weightedSets: number;
1034
+ }
1035
+ interface MuscleGroupHeatmapWeek {
1036
+ weekStart: string;
1037
+ isCurrentWeek: boolean;
1038
+ totalWeightedSets: number;
1039
+ muscles: MuscleGroupHeatmapValue[];
1040
+ }
1041
+ /** Stable successful response of GET /workouts/progress/muscles. */
1042
+ interface MuscleGroupHeatmapResponse {
1043
+ timeZone: string;
1044
+ weeks: MuscleGroupHeatmapWeek[];
1045
+ peakWeightedSets: number;
840
1046
  }
841
- interface ComebackRecognitionSummary {
842
- minimumInactiveDays: number;
843
- requiredActiveDays: number;
844
- windowDays: number;
845
- /** Most recent first, capped by COMEBACK_RECOGNITION_LIMIT. */
846
- recognitions: ComebackRecognition[];
847
- /** True when older completed-session events fell outside the bounded scan. */
848
- historyTruncated: boolean;
1047
+ interface VolumeTrendQuery {
1048
+ timeZone: string;
1049
+ /** Number of Monday-based calendar weeks to return, including this week. */
1050
+ weeks?: number;
849
1051
  }
850
- /** Stable successful response of GET /achievements. */
851
- interface AchievementsResponse {
852
- analyticsReady: boolean;
853
- earnedCount: number;
854
- availableCount: number;
855
- achievements: EarnedAchievement[];
856
- rank: RenaissanceRankProgress | null;
857
- milestoneProgress: AchievementCategoryProgress[];
858
- comeback: ComebackRecognitionSummary | null;
1052
+ interface VolumeTrendPoint {
1053
+ weekStart: string;
1054
+ isCurrentWeek: boolean;
1055
+ /** External-load volume in canonical kilograms: weight × repetitions. */
1056
+ volumeKg: number;
1057
+ completedSets: number;
859
1058
  }
860
-
861
- /**
862
- * A block is **symmetric**. It removes any follow in both directions and
863
- * prevents a new one either way; neither account appears in the other's
864
- * search, suggestions, relationship lists or profile reads. Hiding only the
865
- * blocked account from the blocker would leave the blocker visible to them,
866
- * which is the half nobody asks for.
867
- *
868
- * It is **not retroactive over what was already handed out**: a `SOC-07`
869
- * session link and a `ROUT-04` routine link carry no viewer identity, so a
870
- * block cannot withdraw one. Revoking the link is that control.
871
- */
872
- interface BlockedMember {
873
- /** The blocked account's identity, as a search result carries it. */
874
- member: UserSearchResponse;
875
- blockedAt: IsoDateString;
1059
+ interface VolumeTrendSeries {
1060
+ id: string;
1061
+ name: string;
1062
+ totalVolumeKg: number;
1063
+ totalCompletedSets: number;
1064
+ points: VolumeTrendPoint[];
876
1065
  }
877
- /** Stable successful response of GET /users/me/blocks. */
878
- interface BlockedMembersResponse {
879
- blocks: BlockedMember[];
1066
+ interface MuscleVolumeTrendSeries extends VolumeTrendSeries {
1067
+ id: MuscleGroup;
880
1068
  }
881
- /** One account blocks at most this many others. */
882
- declare const BLOCKED_MEMBERS_MAX = 500;
883
- /** What a viewer may do about one profile, so the UI never offers a no-op. */
884
- interface MemberModerationState {
885
- /** The viewer has blocked this member. */
886
- isBlocked: boolean;
1069
+ /** Stable successful response of GET /workouts/progress/volume. */
1070
+ interface VolumeTrendResponse {
1071
+ timeZone: string;
1072
+ weeks: number;
1073
+ overall: VolumeTrendPoint[];
1074
+ muscles: MuscleVolumeTrendSeries[];
1075
+ routines: VolumeTrendSeries[];
1076
+ exercises: VolumeTrendSeries[];
887
1077
  }
888
- declare const REPORT_SUBJECT_KINDS: readonly ["MEMBER", "ROUTINE", "SESSION", "COMMENT"];
889
- type ReportSubjectKind = (typeof REPORT_SUBJECT_KINDS)[number];
890
- /**
891
- * Why something was reported. The list is short and fixed on purpose: a free
892
- * text field would collect personal data `TRUST-04` has nowhere to put yet.
893
- */
894
- declare const REPORT_REASONS: readonly ["SPAM", "HARASSMENT", "IMPERSONATION", "UNSAFE_ADVICE", "SEXUAL_CONTENT", "OTHER"];
895
- type ReportReason = (typeof REPORT_REASONS)[number];
896
- declare const REPORT_DETAILS_MAX_LENGTH = 500;
897
- /** One account files at most this many reports a day. */
898
- declare const REPORTS_PER_DAY_MAX = 20;
899
- /** POST /reports */
900
- interface CreateReportRequest {
901
- subjectKind: ReportSubjectKind;
902
- /** A member id or username, a routine id, or a session share token. */
903
- subjectId: string;
904
- reason: ReportReason;
905
- /** Optional context from the reporter, never required. */
906
- details?: string | null;
1078
+ interface SessionComparisonQuery {
1079
+ routineDayId?: string;
907
1080
  }
908
- /**
909
- * Stable successful response of POST /reports. It confirms the report was
910
- * recorded and says nothing about review, because nothing reviews it yet.
911
- */
912
- interface CreateReportResponse {
913
- id: string;
914
- createdAt: IsoDateString;
1081
+ interface SessionComparisonRoutineDay {
1082
+ routineDayId: string;
1083
+ routineId: string;
1084
+ routineName: string;
1085
+ dayName?: string | null;
1086
+ lastCompletedAt: IsoDateString;
1087
+ completedSessionCount: number;
915
1088
  }
916
- /** Whether the signed-in account may open the queue at all. */
917
- interface ModeratorState {
918
- isModerator: boolean;
1089
+ interface SessionComparisonSet {
1090
+ routineExerciseId: string;
1091
+ setNumber: number;
1092
+ reps: number;
1093
+ weightKg?: number | null;
1094
+ rpe?: number | null;
919
1095
  }
920
- declare const REPORT_STATUSES: readonly ["OPEN", "DISMISSED", "ACTIONED"];
921
- type ReportStatus = (typeof REPORT_STATUSES)[number];
922
- /**
923
- * Every moderator power, each one a record. `VIEW_SUBJECT` is here for the
924
- * same reason the others are: it is a thing a moderator did to somebody's
925
- * content, and it is the only one that leaves no other trace.
926
- */
927
- declare const MODERATION_ACTION_KINDS: readonly ["VIEW_SUBJECT", "DISMISS_REPORT", "HIDE_SUBJECT", "RESTORE_SUBJECT"];
928
- type ModerationActionKind = (typeof MODERATION_ACTION_KINDS)[number];
929
- declare const MODERATION_NOTE_MAX_LENGTH = 500;
930
- declare const MODERATION_QUEUE_PAGE_SIZE = 20;
931
- declare const MODERATION_HISTORY_PAGE_SIZE = 20;
932
- /**
933
- * What the reviewer may actually learn about a reported subject, resolved
934
- * server-side through the rules that already govern the subject. A reviewer
935
- * who may not read it is told so; the queue never fills the gap in.
936
- */
937
- interface ReportSubjectPreview {
938
- kind: ReportSubjectKind;
939
- /** As the reporter named it: a member id or username, a routine id, a token. */
940
- id: string;
941
- /**
942
- * The member id, routine id or session-share token this resolves to, which
943
- * is what a review action is recorded against. Null when the subject is
944
- * gone.
945
- */
946
- resolvedId: string | null;
947
- /** Present only when the reviewer may read the subject. */
948
- title: string | null;
949
- /** The subject's owner, when the reviewer may read the subject. */
950
- owner: UserSearchResponse | null;
951
- /** The subject no longer exists; only dismissing is left. */
952
- isMissing: boolean;
953
- /**
954
- * The shipped `PROF-06`/`PROF-10` rules deny this reviewer this subject.
955
- * It can still be hidden or dismissed on the strength of the report; it
956
- * cannot be read, and no moderator flag changes that.
957
- */
958
- isWithheld: boolean;
959
- /** A `HIDE_SUBJECT` is currently in force. */
960
- isHidden: boolean;
1096
+ interface SessionComparisonExercise {
1097
+ routineExerciseId: string;
1098
+ exerciseId: string;
1099
+ exerciseName: string;
1100
+ order: number;
1101
+ sets: SessionComparisonSet[];
961
1102
  }
962
- /** One row of the queue. */
963
- interface ModerationReport {
964
- id: string;
965
- status: ReportStatus;
966
- reason: ReportReason;
967
- details: string | null;
968
- createdAt: IsoDateString;
969
- reporter: UserSearchResponse;
970
- subject: ReportSubjectPreview;
971
- /** Other reports still open about the same subject, this one excluded. */
972
- otherOpenReports: number;
973
- resolvedAt: IsoDateString | null;
1103
+ interface SessionComparisonSession {
1104
+ sessionId: string;
1105
+ routineDayId: string;
1106
+ routineName: string;
1107
+ dayName?: string | null;
1108
+ startedAt: IsoDateString;
1109
+ endedAt: IsoDateString;
1110
+ durationSec: number;
1111
+ totalVolumeKg: number;
1112
+ completedSets: number;
1113
+ notes?: string | null;
1114
+ exercises: SessionComparisonExercise[];
974
1115
  }
975
- interface ModerationQueueQuery {
976
- /** Omitted means `OPEN`, which is the queue's reason to exist. */
977
- status?: ReportStatus;
1116
+ /** Stable successful response of GET /workouts/progress/session-comparison. */
1117
+ interface SessionComparisonResponse {
1118
+ routineDays: SessionComparisonRoutineDay[];
1119
+ selectedRoutineDay: SessionComparisonRoutineDay | null;
1120
+ latestSession: SessionComparisonSession | null;
1121
+ previousSession: SessionComparisonSession | null;
1122
+ }
1123
+ declare const PROGRESS_TIMELINE_EVENT_TYPES: readonly ["PERSONAL_RECORD", "PROGRESSION_CHANGED"];
1124
+ type ProgressTimelineEventType = (typeof PROGRESS_TIMELINE_EVENT_TYPES)[number];
1125
+ interface ProgressTimelineQuery {
1126
+ type?: ProgressTimelineEventType;
1127
+ /** Only events for one catalog exercise (EXER-01). */
1128
+ exerciseId?: string;
978
1129
  cursor?: string;
979
1130
  limit?: number;
980
1131
  }
981
- /** Stable successful response of GET /moderation/queue. */
982
- interface ModerationQueueResponse {
983
- reports: ModerationReport[];
984
- nextCursor: string | null;
985
- /** Open reports in total, independent of the page or the filter. */
986
- openCount: number;
1132
+ interface ProgressTimelineSessionContext {
1133
+ sessionId: string;
1134
+ routineName: string;
1135
+ dayName?: string | null;
987
1136
  }
988
- /**
989
- * One enforcement record. It is written once and never updated or deleted —
990
- * including when a hide is undone, which appends a `RESTORE_SUBJECT` record
991
- * rather than editing the `HIDE_SUBJECT` one.
992
- */
993
- interface ModerationActionRecord {
994
- id: string;
995
- kind: ModerationActionKind;
996
- moderator: UserSearchResponse;
997
- /** The report the action was taken from, when it was taken from one. */
998
- reportId: string | null;
999
- subjectKind: ReportSubjectKind;
1000
- subjectId: string;
1001
- note: string | null;
1002
- createdAt: IsoDateString;
1137
+ interface ProgressTimelineRecordPerformance {
1138
+ weightKg: number;
1139
+ reps: number;
1140
+ estimated1rmKg: number;
1003
1141
  }
1004
- interface ModerationHistoryQuery {
1005
- cursor?: string;
1006
- limit?: number;
1007
- /** Narrow to one subject's records, newest first. */
1008
- subjectKind?: ReportSubjectKind;
1009
- subjectId?: string;
1142
+ type ProgressTimelineRecordReason = 'FIRST_RECORDED_BEST' | 'HEAVIER_LOAD' | 'MORE_REPS_AT_SAME_LOAD';
1143
+ interface ProgressTimelinePersonalRecordItem {
1144
+ eventId: string;
1145
+ type: 'PERSONAL_RECORD';
1146
+ occurredAt: IsoDateString;
1147
+ session: ProgressTimelineSessionContext;
1148
+ exerciseId: string;
1149
+ exerciseName: string;
1150
+ current: ProgressTimelineRecordPerformance;
1151
+ previous: ProgressTimelineRecordPerformance | null;
1152
+ reason: ProgressTimelineRecordReason;
1153
+ }
1154
+ interface ProgressTimelineProgressionItem {
1155
+ eventId: string;
1156
+ type: 'PROGRESSION_CHANGED';
1157
+ occurredAt: IsoDateString;
1158
+ session: ProgressTimelineSessionContext;
1159
+ exerciseId: string;
1160
+ exerciseName: string;
1161
+ change: ProgressionChange;
1010
1162
  }
1011
- /** Stable successful response of GET /moderation/actions. */
1012
- interface ModerationHistoryResponse {
1013
- actions: ModerationActionRecord[];
1014
- nextCursor: string | null;
1163
+ type ProgressTimelineItem = ProgressTimelinePersonalRecordItem | ProgressTimelineProgressionItem;
1164
+ /** Stable successful response of GET /workouts/progress/timeline. */
1165
+ interface ProgressTimelineResponse {
1166
+ items: ProgressTimelineItem[];
1167
+ nextCursor?: string;
1015
1168
  }
1016
- /**
1017
- * POST /moderation/reports/:id/dismiss, /hide and /restore. The note is the
1018
- * reviewer's own record of why; it is never shown to the reporter or to the
1019
- * subject's owner, because neither is promised an explanation.
1020
- */
1021
- interface ReviewReportRequest {
1022
- note?: string | null;
1169
+ /** Immutable prescription captured before any sets or progression. */
1170
+ interface WorkoutSessionSnapshotV1 {
1171
+ schemaVersion: 1;
1172
+ sessionId: string;
1173
+ sourceRoutineId: string;
1174
+ sourceRoutineDayId: string;
1175
+ capturedAt: string;
1176
+ provenance: 'CAPTURED' | 'APPROXIMATED';
1177
+ notes: string | null;
1178
+ routine: NonNullable<WorkoutSession['routine']>;
1179
+ routineDay: NonNullable<WorkoutSession['routineDay']>;
1180
+ /** ROUT-15; absent on snapshots captured before blocks executed. */
1181
+ trainingBlock?: WorkoutSession['trainingBlock'];
1023
1182
  }
1024
- /** Stable successful response of every review action, including a view. */
1025
- interface ReviewReportResponse {
1026
- report: ModerationReport;
1027
- action: ModerationActionRecord;
1183
+ interface SetAccountTimeZoneRequest {
1184
+ timeZone: string;
1185
+ /** Bootstrap must not replace a zone registered by another device. */
1186
+ onlyIfUnset?: boolean;
1187
+ }
1188
+ interface WorkoutAnalyticsStatus {
1189
+ timeZone: string | null;
1190
+ requestedTimeZone: string | null;
1191
+ state: 'UNINITIALIZED' | 'BUILDING' | 'READY' | 'FAILED';
1192
+ generationId: string | null;
1028
1193
  }
1029
-
1030
1194
  /**
1031
- * ROUT-11: a WEEKLY routine ties each day to a weekday; a ROTATION routine
1032
- * runs its days in `order`, each after the last completed one, whatever the
1033
- * weekday.
1195
+ * Thresholds behind every plateau. A lift is flagged only when all hold: at
1196
+ * least the account's minimum sessions (`PLATEAU_MIN_SESSIONS` by default)
1197
+ * of terminal sessions with completed loaded sets of it since its current
1198
+ * best (the session that set the best excluded, and counting only inside the
1199
+ * look-back window), the best is at least
1200
+ * `PLATEAU_MIN_DAYS_SINCE_BEST` days old, and the lift was trained within the
1201
+ * last `PLATEAU_RECENT_DAYS` days. They describe numbers, never a cause.
1034
1202
  */
1035
- declare const ROUTINE_SCHEDULE_MODES: readonly ["WEEKLY", "ROTATION"];
1036
- type RoutineScheduleMode = (typeof ROUTINE_SCHEDULE_MODES)[number];
1037
- /** At most seven days in either mode. */
1038
- declare const ROUTINE_DAYS_MAX = 7;
1039
- declare const ROUTINE_DAY_NAME_MAX = 40;
1203
+ declare const PLATEAU_WINDOW_DAYS = 56;
1204
+ declare const PLATEAU_MIN_SESSIONS = 4;
1205
+ declare const PLATEAU_MIN_DAYS_SINCE_BEST = 21;
1206
+ declare const PLATEAU_RECENT_DAYS = 21;
1040
1207
  /**
1041
- * How a routine day is named everywhere, including history snapshots taken
1042
- * before ROUT-11: its own name, else its weekday, else its rotation letter
1043
- * ("Day A" for order 0).
1208
+ * PREF-05: each account chooses its minimum sessions within these bounds;
1209
+ * `PLATEAU_MIN_SESSIONS` is the default. The other thresholds stay fixed.
1044
1210
  */
1045
- declare function routineDayLabel(day: {
1046
- name?: string | null;
1047
- dayOfWeek?: number | null;
1048
- order?: number | null;
1049
- }): string;
1050
- interface RoutineSet {
1051
- setNumber: number;
1052
- repType: RepType;
1053
- reps?: number | null;
1054
- minReps?: number | null;
1055
- maxReps?: number | null;
1056
- weight?: number | null;
1057
- rir?: number | null;
1211
+ declare const PLATEAU_MIN_SESSIONS_MIN = 3;
1212
+ declare const PLATEAU_MIN_SESSIONS_MAX = 8;
1213
+ /** Body and response of PUT /users/preferences/plateaus. */
1214
+ interface PlateauPreferences {
1215
+ minSessions: number;
1058
1216
  }
1059
- interface RoutineExercise {
1060
- id: string;
1061
- order: number;
1062
- restSeconds: number;
1063
- note?: string | null;
1064
- progressionScheme: ProgressionScheme;
1065
- minWeightIncrement: number;
1066
- exercise: {
1067
- id: string;
1068
- name: string;
1069
- primaryMuscles?: MuscleGroup[];
1070
- secondaryMuscles?: MuscleGroup[];
1071
- };
1072
- sets: RoutineSet[];
1217
+ interface PlateauSet {
1218
+ weightKg: number;
1219
+ reps: number;
1220
+ estimated1rmKg: number;
1073
1221
  }
1074
- interface CreateRoutineExerciseInput {
1222
+ interface ExercisePlateau {
1075
1223
  exerciseId: string;
1076
- order?: number;
1077
- restSeconds: number;
1078
- note?: string;
1079
- progressionScheme: ProgressionScheme;
1080
- minWeightIncrement: number;
1081
- sets: RoutineSet[];
1224
+ exerciseName: string;
1225
+ /** The persisted best-set frontier. */
1226
+ best: PlateauSet & {
1227
+ achievedAt: IsoDateString;
1228
+ };
1229
+ /** Sessions were counted from the later of the best and the window start. */
1230
+ countedSince: IsoDateString;
1231
+ sessionsWithoutNewBest: number;
1232
+ /** The set since then with the highest estimated 1RM (latest on ties). */
1233
+ closest: PlateauSet & {
1234
+ performedAt: IsoDateString;
1235
+ };
1236
+ /** closest.estimated1rmKg / best.estimated1rmKg. */
1237
+ closestRatio: number;
1238
+ lastPerformedAt: IsoDateString;
1082
1239
  }
1083
- interface RoutineDay {
1240
+ /** Stable successful response of GET /workouts/progress/plateaus. */
1241
+ interface PlateausResponse {
1242
+ asOf: IsoDateString;
1243
+ thresholds: {
1244
+ windowDays: number;
1245
+ /** The account's PREF-05 choice, within the bounds above. */
1246
+ minSessions: number;
1247
+ minDaysSinceBest: number;
1248
+ recentDays: number;
1249
+ };
1250
+ /** Lifts with a best set that were trained within the recent days. */
1251
+ checkedExercises: number;
1252
+ /** Longest-running first: most sessions without a new best. */
1253
+ plateaus: ExercisePlateau[];
1254
+ }
1255
+
1256
+ declare const ACHIEVEMENT_CATEGORIES: readonly ["SESSIONS", "SETS", "VOLUME_KG", "RECORDS", "STREAK_DAYS"];
1257
+ type AchievementCategory = (typeof ACHIEVEMENT_CATEGORIES)[number];
1258
+ interface AchievementDefinition {
1084
1259
  id: string;
1085
- /** 0=Sun..6=Sat on a WEEKLY routine; null on a ROTATION routine. */
1086
- dayOfWeek: number | null;
1087
- /** Optional label such as "Push" or "Upper A" (see `routineDayLabel`). */
1088
- name: string | null;
1089
- /** Rotation sequence, 0-based; also the display order. */
1090
- order: number;
1091
- exercises: RoutineExercise[];
1260
+ category: AchievementCategory;
1261
+ threshold: number;
1262
+ title: string;
1263
+ description: string;
1092
1264
  }
1093
- interface CreateRoutineDayInput {
1094
- /** Required and unique on a WEEKLY routine; omitted or null on a ROTATION. */
1095
- dayOfWeek?: number | null;
1096
- name?: string | null;
1097
- order?: number;
1098
- exercises: CreateRoutineExerciseInput[];
1265
+ /** Stable milestone catalog shared by the writer and every presentation. */
1266
+ declare const ACHIEVEMENT_DEFINITIONS: readonly AchievementDefinition[];
1267
+ interface AchievementUnlockedEventPayload extends AchievementDefinition {
1268
+ schemaVersion: 1;
1269
+ backfilled: boolean;
1099
1270
  }
1100
- interface Routine {
1271
+ interface StreakMilestoneEventPayload {
1272
+ schemaVersion: 1;
1273
+ achievementId: string;
1274
+ streakDays: number;
1275
+ backfilled: boolean;
1276
+ }
1277
+ interface EarnedAchievement extends AchievementDefinition {
1278
+ eventId: string;
1279
+ unlockedAt: IsoDateString;
1280
+ sourceSessionId: string | null;
1281
+ /** True when existing verified history was recognized after ACH-01 shipped. */
1282
+ backfilled: boolean;
1283
+ }
1284
+ interface RenaissanceRankDefinition {
1285
+ id: 'INITIATE' | 'APPRENTICE' | 'ARTISAN' | 'MAESTRO' | 'VIRTUOSO' | 'LAUREATE';
1286
+ title: string;
1287
+ description: string;
1288
+ minimumSessions: number;
1289
+ minimumActiveWeeks: number;
1290
+ }
1291
+ /**
1292
+ * Stable rank ladder. Both requirements are intentionally based on attendance:
1293
+ * completed sessions reward participation, while distinct active weeks reward
1294
+ * consistency without encouraging consecutive-day training.
1295
+ */
1296
+ declare const RENAISSANCE_RANK_DEFINITIONS: readonly [{
1297
+ readonly id: "INITIATE";
1298
+ readonly title: "Initiate";
1299
+ readonly description: "Your place in the training ledger begins here.";
1300
+ readonly minimumSessions: 0;
1301
+ readonly minimumActiveWeeks: 0;
1302
+ }, {
1303
+ readonly id: "APPRENTICE";
1304
+ readonly title: "Apprentice";
1305
+ readonly description: "Learning the craft through regular practice.";
1306
+ readonly minimumSessions: 5;
1307
+ readonly minimumActiveWeeks: 3;
1308
+ }, {
1309
+ readonly id: "ARTISAN";
1310
+ readonly title: "Artisan";
1311
+ readonly description: "Building a dependable training practice.";
1312
+ readonly minimumSessions: 15;
1313
+ readonly minimumActiveWeeks: 8;
1314
+ }, {
1315
+ readonly id: "MAESTRO";
1316
+ readonly title: "Maestro";
1317
+ readonly description: "Sustaining purposeful work across many weeks.";
1318
+ readonly minimumSessions: 30;
1319
+ readonly minimumActiveWeeks: 16;
1320
+ }, {
1321
+ readonly id: "VIRTUOSO";
1322
+ readonly title: "Virtuoso";
1323
+ readonly description: "Showing enduring discipline through repeated seasons.";
1324
+ readonly minimumSessions: 60;
1325
+ readonly minimumActiveWeeks: 32;
1326
+ }, {
1327
+ readonly id: "LAUREATE";
1328
+ readonly title: "Laureate";
1329
+ readonly description: "A lasting training practice recorded in the ledger.";
1330
+ readonly minimumSessions: 100;
1331
+ readonly minimumActiveWeeks: 52;
1332
+ }];
1333
+ interface RenaissanceRankProgress {
1334
+ currentRank: RenaissanceRankDefinition;
1335
+ nextRank: RenaissanceRankDefinition | null;
1336
+ completedSessions: number;
1337
+ activeWeeks: number;
1338
+ sessionsRemaining: number;
1339
+ activeWeeksRemaining: number;
1340
+ }
1341
+ /**
1342
+ * Current verified total and the next fixed catalog milestone for one category.
1343
+ * A null next milestone means the category's finite catalog is complete.
1344
+ */
1345
+ interface AchievementCategoryProgress {
1346
+ category: AchievementCategory;
1347
+ currentValue: number;
1348
+ nextMilestone: AchievementDefinition | null;
1349
+ remaining: number;
1350
+ }
1351
+ /**
1352
+ * ACH-05 recognises a return only after the athlete has rebuilt a small,
1353
+ * recovery-compatible pattern. The break is counted as full local calendar
1354
+ * days without a completed session; repeated sessions on one date count once.
1355
+ */
1356
+ declare const COMEBACK_MIN_INACTIVE_DAYS = 14;
1357
+ declare const COMEBACK_REQUIRED_ACTIVE_DAYS = 3;
1358
+ declare const COMEBACK_WINDOW_DAYS = 14;
1359
+ declare const COMEBACK_SESSION_LOOKBACK = 500;
1360
+ declare const COMEBACK_RECOGNITION_LIMIT = 20;
1361
+ interface ComebackRecognition {
1101
1362
  id: string;
1102
- userId: string;
1103
- name: string;
1104
- description?: string | null;
1105
- isPeriodized: boolean;
1106
- isFavorite: boolean;
1107
- isCompleted: boolean;
1108
- scheduleMode: RoutineScheduleMode;
1109
- /**
1110
- * ROTATION only: the day after the one of the last completed session (an
1111
- * aborted session does not advance it), or the first day before any.
1112
- */
1113
- nextRotationDayId: string | null;
1114
- /**
1115
- * SCHED-07: weekdays (0=Sun..6=Sat) this weekly routine rests on by plan,
1116
- * never one of its training weekdays; always empty on a ROTATION routine.
1117
- */
1118
- restDays: number[];
1119
- /**
1120
- * SCHED-06: weekdays (0=Sun..6=Sat) a ROTATION routine trains on, sorted; the
1121
- * schedule places its days on them in order. Empty means any day, without
1122
- * dates; always empty on a WEEKLY routine.
1123
- */
1124
- rotationWeekdays: number[];
1125
- /**
1126
- * ROUT-04: who may read this routine, bounded by the account-level
1127
- * `PROF-06` routines rule. Owner-only field; it never appears in a shared
1128
- * read, where visibility is the reason the reader is there.
1129
- */
1130
- visibility: RoutineVisibility;
1131
- /**
1132
- * ROUT-06: present only on a routine that was cloned, and only with what
1133
- * this viewer is allowed to know about its source.
1134
- */
1135
- lineage?: RoutineLineage | null;
1136
- /**
1137
- * ROUT-07: owner-declared, both optional. They are claims about the
1138
- * programme that only its author can make, which is why they are stored
1139
- * here rather than derived from the author's `PROF-05` training identity.
1140
- */
1141
- goal?: TrainingGoal | null;
1142
- experienceLevel?: TrainingExperienceLevel | null;
1143
- /**
1144
- * TRUST-04: a moderator has hidden this routine from everyone but its
1145
- * owner. Owner-only field, and true on the owner's own read alone: a
1146
- * routine nobody else can open while its own visibility still reads
1147
- * "Public" would be a control with nothing behind it.
1148
- */
1149
- isHiddenByModeration?: boolean;
1150
- days: RoutineDay[];
1151
- createdAt: IsoDateString;
1152
- updatedAt: IsoDateString;
1363
+ inactiveDays: number;
1364
+ returnedAt: IsoDateString;
1365
+ recognizedAt: IsoDateString;
1366
+ sourceSessionId: string;
1367
+ activeDays: number;
1368
+ /** Inclusive local-calendar span from the return through recognition. */
1369
+ windowDays: number;
1153
1370
  }
1154
- interface CreateRoutineRequest {
1155
- name: string;
1156
- description?: string;
1157
- /** ROUT-07; omitted means undeclared, which is not the same as a default. */
1158
- goal?: TrainingGoal | null;
1159
- experienceLevel?: TrainingExperienceLevel | null;
1160
- isPeriodized: boolean;
1161
- /** Defaults to WEEKLY. Changing it on update requires `days`. */
1162
- scheduleMode?: RoutineScheduleMode;
1163
- /**
1164
- * Weekly routines only. Omitted on update keeps the stored rest days, minus
1165
- * any that became training weekdays.
1166
- */
1167
- restDays?: number[];
1168
- /**
1169
- * Rotation routines only. Omitted on update keeps the stored weekdays;
1170
- * switching to WEEKLY clears them.
1171
- */
1172
- rotationWeekdays?: number[];
1173
- days: CreateRoutineDayInput[];
1371
+ interface ComebackRecognitionSummary {
1372
+ minimumInactiveDays: number;
1373
+ requiredActiveDays: number;
1374
+ windowDays: number;
1375
+ /** Most recent first, capped by COMEBACK_RECOGNITION_LIMIT. */
1376
+ recognitions: ComebackRecognition[];
1377
+ /** True when older completed-session events fell outside the bounded scan. */
1378
+ historyTruncated: boolean;
1174
1379
  }
1175
- type UpdateRoutineRequest = Partial<CreateRoutineRequest>;
1176
- /** A routine keeps at most this many versions; saving past it is refused. */
1177
- declare const ROUTINE_VERSIONS_MAX = 20;
1178
- declare const ROUTINE_VERSION_NAME_MAX = 60;
1380
+ /** Stable successful response of GET /achievements. */
1381
+ interface AchievementsResponse {
1382
+ analyticsReady: boolean;
1383
+ earnedCount: number;
1384
+ availableCount: number;
1385
+ achievements: EarnedAchievement[];
1386
+ rank: RenaissanceRankProgress | null;
1387
+ milestoneProgress: AchievementCategoryProgress[];
1388
+ comeback: ComebackRecognitionSummary | null;
1389
+ }
1390
+
1179
1391
  /**
1180
- * SAVED is an intentional save; BEFORE_RESTORE is the setup a restore
1181
- * replaced, saved automatically so the restore can be undone.
1392
+ * A block is **symmetric**. It removes any follow in both directions and
1393
+ * prevents a new one either way; neither account appears in the other's
1394
+ * search, suggestions, relationship lists or profile reads. Hiding only the
1395
+ * blocked account from the blocker would leave the blocker visible to them,
1396
+ * which is the half nobody asks for.
1397
+ *
1398
+ * It is **not retroactive over what was already handed out**: a `SOC-07`
1399
+ * session link and a `ROUT-04` routine link carry no viewer identity, so a
1400
+ * block cannot withdraw one. Revoking the link is that control.
1182
1401
  */
1183
- declare const ROUTINE_VERSION_KINDS: readonly ["SAVED", "BEFORE_RESTORE"];
1184
- type RoutineVersionKind = (typeof ROUTINE_VERSION_KINDS)[number];
1185
- interface RoutineVersionExercise {
1186
- /** The catalog exercise and its name when the version was saved. */
1187
- exercise: {
1188
- id: string;
1189
- name: string;
1190
- };
1191
- order: number;
1192
- restSeconds: number;
1193
- note: string | null;
1194
- progressionScheme: ProgressionScheme;
1195
- minWeightIncrement: number;
1196
- sets: RoutineSet[];
1402
+ interface BlockedMember {
1403
+ /** The blocked account's identity, as a search result carries it. */
1404
+ member: UserSearchResponse;
1405
+ blockedAt: IsoDateString;
1197
1406
  }
1198
- interface RoutineVersionDay {
1199
- dayOfWeek: number | null;
1200
- name: string | null;
1201
- order: number;
1202
- exercises: RoutineVersionExercise[];
1407
+ /** Stable successful response of GET /users/me/blocks. */
1408
+ interface BlockedMembersResponse {
1409
+ blocks: BlockedMember[];
1203
1410
  }
1204
- /** Everything a routine edit can change, as it was when the version was saved. */
1205
- interface RoutineVersionSetup {
1206
- name: string;
1207
- description: string | null;
1208
- scheduleMode: RoutineScheduleMode;
1209
- restDays: number[];
1210
- /** SCHED-06; absent in versions saved before it, which means none. */
1211
- rotationWeekdays?: number[];
1212
- days: RoutineVersionDay[];
1411
+ /** One account blocks at most this many others. */
1412
+ declare const BLOCKED_MEMBERS_MAX = 500;
1413
+ /** What a viewer may do about one profile, so the UI never offers a no-op. */
1414
+ interface MemberModerationState {
1415
+ /** The viewer has blocked this member. */
1416
+ isBlocked: boolean;
1213
1417
  }
1214
- interface RoutineVersion {
1418
+ declare const REPORT_SUBJECT_KINDS: readonly ["MEMBER", "ROUTINE", "SESSION", "COMMENT"];
1419
+ type ReportSubjectKind = (typeof REPORT_SUBJECT_KINDS)[number];
1420
+ /**
1421
+ * Why something was reported. The list is short and fixed on purpose: a free
1422
+ * text field would collect personal data `TRUST-04` has nowhere to put yet.
1423
+ */
1424
+ declare const REPORT_REASONS: readonly ["SPAM", "HARASSMENT", "IMPERSONATION", "UNSAFE_ADVICE", "SEXUAL_CONTENT", "OTHER"];
1425
+ type ReportReason = (typeof REPORT_REASONS)[number];
1426
+ declare const REPORT_DETAILS_MAX_LENGTH = 500;
1427
+ /** One account files at most this many reports a day. */
1428
+ declare const REPORTS_PER_DAY_MAX = 20;
1429
+ /** POST /reports */
1430
+ interface CreateReportRequest {
1431
+ subjectKind: ReportSubjectKind;
1432
+ /** A member id or username, a routine id, or a session share token. */
1433
+ subjectId: string;
1434
+ reason: ReportReason;
1435
+ /** Optional context from the reporter, never required. */
1436
+ details?: string | null;
1437
+ }
1438
+ /**
1439
+ * Stable successful response of POST /reports. It confirms the report was
1440
+ * recorded and says nothing about review, because nothing reviews it yet.
1441
+ */
1442
+ interface CreateReportResponse {
1215
1443
  id: string;
1216
- routineId: string;
1217
- /** 1, 2, 3… per routine, never reused after a deletion. */
1218
- number: number;
1219
- name: string | null;
1220
- kind: RoutineVersionKind;
1221
- /** BEFORE_RESTORE only: the number of the version that was restored. */
1222
- restoredVersionNumber: number | null;
1223
1444
  createdAt: IsoDateString;
1224
- setup: RoutineVersionSetup;
1225
1445
  }
1226
- /** `GET /routines/:id/versions`, newest first. */
1227
- interface RoutineVersionsResponse {
1228
- versions: RoutineVersion[];
1229
- max: number;
1230
- }
1231
- /** `POST /routines/:id/versions` */
1232
- interface CreateRoutineVersionRequest {
1233
- name?: string | null;
1234
- }
1235
- /** `POST /routines/:id/versions/:versionId/restore` */
1236
- interface RestoreRoutineVersionResponse {
1237
- routine: Routine;
1238
- /** The replaced setup, saved as a BEFORE_RESTORE version. */
1239
- savedVersion: RoutineVersion;
1446
+ /** Whether the signed-in account may open the queue at all. */
1447
+ interface ModeratorState {
1448
+ isModerator: boolean;
1240
1449
  }
1241
-
1450
+ declare const REPORT_STATUSES: readonly ["OPEN", "DISMISSED", "ACTIONED"];
1451
+ type ReportStatus = (typeof REPORT_STATUSES)[number];
1242
1452
  /**
1243
- * Per-routine visibility. The account-level `PROF-06` routines rule is the
1244
- * upper bound: a routine marked `PUBLIC` inside an account whose routines are
1245
- * `FOLLOWERS` is visible to followers only, the same way `SOC-04` bounds
1246
- * activity by the profile section it comes from.
1453
+ * Every moderator power, each one a record. `VIEW_SUBJECT` is here for the
1454
+ * same reason the others are: it is a thing a moderator did to somebody's
1455
+ * content, and it is the only one that leaves no other trace.
1247
1456
  */
1248
- declare const ROUTINE_VISIBILITY_VALUES: readonly ["PRIVATE", "FOLLOWERS", "PUBLIC"];
1249
- type RoutineVisibility = (typeof ROUTINE_VISIBILITY_VALUES)[number];
1250
- /** One routine keeps at most this many active links. */
1251
- declare const ROUTINE_SHARE_MAX_ACTIVE_LINKS = 10;
1252
- /** PUT /routines/:id/visibility */
1253
- interface UpdateRoutineVisibilityRequest {
1254
- visibility: RoutineVisibility;
1457
+ declare const MODERATION_ACTION_KINDS: readonly ["VIEW_SUBJECT", "DISMISS_REPORT", "HIDE_SUBJECT", "RESTORE_SUBJECT"];
1458
+ type ModerationActionKind = (typeof MODERATION_ACTION_KINDS)[number];
1459
+ declare const MODERATION_NOTE_MAX_LENGTH = 500;
1460
+ declare const MODERATION_QUEUE_PAGE_SIZE = 20;
1461
+ declare const MODERATION_HISTORY_PAGE_SIZE = 20;
1462
+ /**
1463
+ * What the reviewer may actually learn about a reported subject, resolved
1464
+ * server-side through the rules that already govern the subject. A reviewer
1465
+ * who may not read it is told so; the queue never fills the gap in.
1466
+ */
1467
+ interface ReportSubjectPreview {
1468
+ kind: ReportSubjectKind;
1469
+ /** As the reporter named it: a member id or username, a routine id, a token. */
1470
+ id: string;
1471
+ /**
1472
+ * The member id, routine id or session-share token this resolves to, which
1473
+ * is what a review action is recorded against. Null when the subject is
1474
+ * gone.
1475
+ */
1476
+ resolvedId: string | null;
1477
+ /** Present only when the reviewer may read the subject. */
1478
+ title: string | null;
1479
+ /** The subject's owner, when the reviewer may read the subject. */
1480
+ owner: UserSearchResponse | null;
1481
+ /** The subject no longer exists; only dismissing is left. */
1482
+ isMissing: boolean;
1483
+ /**
1484
+ * The shipped `PROF-06`/`PROF-10` rules deny this reviewer this subject.
1485
+ * It can still be hidden or dismissed on the strength of the report; it
1486
+ * cannot be read, and no moderator flag changes that.
1487
+ */
1488
+ isWithheld: boolean;
1489
+ /** A `HIDE_SUBJECT` is currently in force. */
1490
+ isHidden: boolean;
1255
1491
  }
1256
- /** An active share link, visible only to the routine's owner. */
1257
- interface RoutineShare {
1492
+ /** One row of the queue. */
1493
+ interface ModerationReport {
1258
1494
  id: string;
1259
- routineId: string;
1260
- /** Unguessable identifier used in the public `/shared/routines/:token` URL. */
1261
- token: string;
1495
+ status: ReportStatus;
1496
+ reason: ReportReason;
1497
+ details: string | null;
1262
1498
  createdAt: IsoDateString;
1499
+ reporter: UserSearchResponse;
1500
+ subject: ReportSubjectPreview;
1501
+ /** Other reports still open about the same subject, this one excluded. */
1502
+ otherOpenReports: number;
1503
+ resolvedAt: IsoDateString | null;
1263
1504
  }
1264
- /** Stable successful response of GET /routines/:id/shares. */
1265
- interface RoutineShareListResponse {
1266
- items: RoutineShare[];
1267
- }
1268
- interface SharedRoutineOwner {
1269
- username: string;
1270
- name: string;
1271
- lastName?: string | null;
1272
- avatarUrl?: string | null;
1505
+ interface ModerationQueueQuery {
1506
+ /** Omitted means `OPEN`, which is the queue's reason to exist. */
1507
+ status?: ReportStatus;
1508
+ cursor?: string;
1509
+ limit?: number;
1273
1510
  }
1274
- /**
1275
- * How a reader reached a routine. A link is the owner's explicit consent for
1276
- * that one routine and ignores visibility; a visibility read is governed by
1277
- * both the routine's own rule and the account's.
1278
- */
1279
- declare const SHARED_ROUTINE_SOURCES: readonly ["LINK", "VISIBILITY"];
1280
- type SharedRoutineSource = (typeof SHARED_ROUTINE_SOURCES)[number];
1281
- /**
1282
- * Stable successful response of the unauthenticated
1283
- * GET /shared/routines/:token, and of the authenticated member read.
1284
- */
1285
- interface SharedRoutine {
1286
- /** Kept so a future clone (`ROUT-05`) can record what it came from. */
1287
- routineId: string;
1288
- setup: RoutineVersionSetup;
1289
- owner: SharedRoutineOwner;
1290
- source: SharedRoutineSource;
1291
- /** When the routine was last changed, not when it was shared. */
1292
- updatedAt: IsoDateString;
1511
+ /** Stable successful response of GET /moderation/queue. */
1512
+ interface ModerationQueueResponse {
1513
+ reports: ModerationReport[];
1514
+ nextCursor: string | null;
1515
+ /** Open reports in total, independent of the page or the filter. */
1516
+ openCount: number;
1293
1517
  }
1294
1518
  /**
1295
- * ROUT-06. Where a cloned routine came from. It is recorded on the clone and
1296
- * never on the source, so a routine cannot learn who copied it. It is served
1297
- * only to a viewer who could read the source anyway: lineage must not become
1298
- * a way to discover that a private routine exists.
1519
+ * One enforcement record. It is written once and never updated or deleted —
1520
+ * including when a hide is undone, which appends a `RESTORE_SUBJECT` record
1521
+ * rather than editing the `HIDE_SUBJECT` one.
1299
1522
  */
1300
- interface RoutineLineage {
1301
- /** The routine this one was cloned from, when the viewer may read it. */
1302
- sourceRoutineId: string | null;
1303
- /** The original author's public identity, when they still have one. */
1304
- author: SharedRoutineOwner | null;
1305
- /**
1306
- * True when the source exists but this viewer may not read it, or its author
1307
- * is gone. The clone still says it was cloned; it just cannot say from what.
1308
- */
1309
- isSourceHidden: boolean;
1310
- clonedAt: IsoDateString;
1523
+ interface ModerationActionRecord {
1524
+ id: string;
1525
+ kind: ModerationActionKind;
1526
+ moderator: UserSearchResponse;
1527
+ /** The report the action was taken from, when it was taken from one. */
1528
+ reportId: string | null;
1529
+ subjectKind: ReportSubjectKind;
1530
+ subjectId: string;
1531
+ note: string | null;
1532
+ createdAt: IsoDateString;
1311
1533
  }
1312
- /** A routine in a member's visible list, without loading its whole setup. */
1313
- interface SharedRoutineSummary {
1314
- routineId: string;
1315
- name: string;
1316
- description: string | null;
1317
- scheduleMode: RoutineScheduleMode;
1318
- dayCount: number;
1319
- exerciseCount: number;
1320
- updatedAt: IsoDateString;
1534
+ interface ModerationHistoryQuery {
1535
+ cursor?: string;
1536
+ limit?: number;
1537
+ /** Narrow to one subject's records, newest first. */
1538
+ subjectKind?: ReportSubjectKind;
1539
+ subjectId?: string;
1321
1540
  }
1322
- /** Stable successful response of GET /users/:identifier/routines. */
1323
- interface MemberRoutinesResponse {
1324
- routines: SharedRoutineSummary[];
1541
+ /** Stable successful response of GET /moderation/actions. */
1542
+ interface ModerationHistoryResponse {
1543
+ actions: ModerationActionRecord[];
1544
+ nextCursor: string | null;
1325
1545
  }
1326
1546
  /**
1327
- * POST /routines/clones. Exactly one source, matching the two ways a routine
1328
- * can be read: `token` for a private link, `routineId` for one the viewer may
1329
- * already see. Sending both, or neither, is refused.
1547
+ * POST /moderation/reports/:id/dismiss, /hide and /restore. The note is the
1548
+ * reviewer's own record of why; it is never shown to the reporter or to the
1549
+ * subject's owner, because neither is promised an explanation.
1330
1550
  */
1331
- interface CloneRoutineRequest {
1332
- /** The `/shared/routines/:token` link the reader followed. */
1333
- token?: string;
1334
- /** A routine the viewer may read under `ROUT-04` visibility. */
1335
- routineId?: string;
1551
+ interface ReviewReportRequest {
1552
+ note?: string | null;
1553
+ }
1554
+ /** Stable successful response of every review action, including a view. */
1555
+ interface ReviewReportResponse {
1556
+ report: ModerationReport;
1557
+ action: ModerationActionRecord;
1336
1558
  }
1337
- /** The refusals a clone states by name rather than as a bare 4xx. */
1338
- declare const CLONE_ROUTINE_REFUSALS: {
1339
- /** Neither `token` nor `routineId`, or both at once. */
1340
- readonly SOURCE_REQUIRED: "SOURCE_REQUIRED";
1341
- /** An exercise the routine programs is no longer in the catalog. */
1342
- readonly UNKNOWN_EXERCISE: "UNKNOWN_EXERCISE";
1343
- };
1344
- type CloneRoutineRefusal = (typeof CLONE_ROUTINE_REFUSALS)[keyof typeof CLONE_ROUTINE_REFUSALS];
1345
1559
 
1346
1560
  declare const USERNAME_MIN_LENGTH = 3;
1347
1561
  declare const USERNAME_MAX_LENGTH = 30;
@@ -1737,128 +1951,6 @@ interface PersonalGoalsResponse {
1737
1951
  goals: PersonalGoalProgress[];
1738
1952
  }
1739
1953
 
1740
- /** A local calendar date, YYYY-MM-DD. */
1741
- type CalendarDate = string;
1742
- /**
1743
- * The schedule-instance model: per-date overrides of a routine's plan. MOVE
1744
- * puts one weekly occurrence on another date (SCHED-04); SKIP marks it
1745
- * skipped on purpose (SCHED-05). Overrides are kept by calendar date, not by
1746
- * routine day, because routine edits replace the days.
1747
- */
1748
- declare const SCHEDULE_OVERRIDE_KINDS: readonly ["MOVE", "SKIP"];
1749
- type ScheduleOverrideKind = (typeof SCHEDULE_OVERRIDE_KINDS)[number];
1750
- /** A move goes at most this many days before or after the planned date. */
1751
- declare const SCHEDULE_MOVE_MAX_DAYS = 6;
1752
- /**
1753
- * SCHED-05: a skip can explain a planned day up to this many days back;
1754
- * ahead, any planned day can be skipped.
1755
- */
1756
- declare const SCHEDULE_SKIP_PAST_DAYS = 6;
1757
- /** Overrides are read for at most this many days at once. */
1758
- declare const SCHEDULE_OVERRIDES_MAX_RANGE_DAYS = 62;
1759
- interface ScheduleOverride {
1760
- id: string;
1761
- routineId: string;
1762
- /** The planned date this override changes. */
1763
- date: CalendarDate;
1764
- kind: ScheduleOverrideKind;
1765
- /** MOVE: the date the occurrence now falls on; null for SKIP. */
1766
- toDate: CalendarDate | null;
1767
- createdAt: IsoDateString;
1768
- }
1769
- /**
1770
- * `GET /schedule/overrides?from&to`: the owner's overrides whose date or
1771
- * target falls in the range, both ends included.
1772
- */
1773
- interface ScheduleOverridesQuery {
1774
- from: CalendarDate;
1775
- to: CalendarDate;
1776
- }
1777
- interface ScheduleOverridesResponse {
1778
- overrides: ScheduleOverride[];
1779
- }
1780
- /** `PUT /schedule/overrides/move`: moves one occurrence, or moves it again. */
1781
- interface MoveOccurrenceRequest {
1782
- routineId: string;
1783
- date: CalendarDate;
1784
- toDate: CalendarDate;
1785
- }
1786
- /**
1787
- * `PUT /schedule/overrides/skip`: marks one occurrence skipped. A moved
1788
- * occurrence is skipped as a whole; undoing is `DELETE` like a move.
1789
- */
1790
- interface SkipOccurrenceRequest {
1791
- routineId: string;
1792
- date: CalendarDate;
1793
- }
1794
-
1795
- /** One routine may have this many current, non-overlapping planned blocks. */
1796
- declare const ROUTINE_TRAINING_BLOCKS_MAX = 24;
1797
- /** A started block keeps at most this many immutable revisions. */
1798
- declare const ROUTINE_TRAINING_BLOCK_REVISIONS_MAX = 20;
1799
- declare const ROUTINE_TRAINING_BLOCK_NAME_MAX = 60;
1800
- declare const ROUTINE_TRAINING_BLOCK_STATES: readonly ["FUTURE", "ACTIVE", "COMPLETE"];
1801
- type RoutineTrainingBlockState = (typeof ROUTINE_TRAINING_BLOCK_STATES)[number];
1802
- declare const ROUTINE_TRAINING_BLOCK_SOURCE_KINDS: readonly ["CURRENT_ROUTINE", "SAVED_VERSION"];
1803
- type RoutineTrainingBlockSourceKind = (typeof ROUTINE_TRAINING_BLOCK_SOURCE_KINDS)[number];
1804
- /**
1805
- * The origin is snapshotted so a later routine edit or ROUT-08 version
1806
- * deletion cannot change what this block says it was authored from.
1807
- */
1808
- interface RoutineTrainingBlockSource {
1809
- kind: RoutineTrainingBlockSourceKind;
1810
- versionId: string | null;
1811
- versionNumber: number | null;
1812
- versionName: string | null;
1813
- }
1814
- /** One immutable revision of a named, dated setup. */
1815
- interface RoutineTrainingBlockRevision {
1816
- id: string;
1817
- routineId: string;
1818
- /** Stable across every replacement of the same logical block. */
1819
- seriesId: string;
1820
- /** 1, 2, 3… inside the series. */
1821
- revision: number;
1822
- name: string;
1823
- startDate: CalendarDate;
1824
- endDate: CalendarDate;
1825
- setup: RoutineVersionSetup;
1826
- source: RoutineTrainingBlockSource;
1827
- createdAt: IsoDateString;
1828
- /** Null only on the current revision. */
1829
- supersededAt: IsoDateString | null;
1830
- }
1831
- /** A current block in the routine timeline. State is relative to `today`. */
1832
- interface RoutineTrainingBlock extends Omit<RoutineTrainingBlockRevision, 'supersededAt'> {
1833
- state: RoutineTrainingBlockState;
1834
- revisionCount: number;
1835
- }
1836
- /** `GET /routines/:id/training-blocks`, ordered by start date. */
1837
- interface RoutineTrainingBlocksResponse {
1838
- blocks: RoutineTrainingBlock[];
1839
- max: number;
1840
- revisionMax: number;
1841
- /** The owner's local date used to derive every state in this response. */
1842
- today: CalendarDate;
1843
- }
1844
- /** `GET /routines/:id/training-blocks/:blockId/revisions`, newest first. */
1845
- interface RoutineTrainingBlockRevisionsResponse {
1846
- seriesId: string;
1847
- revisions: RoutineTrainingBlockRevision[];
1848
- max: number;
1849
- }
1850
- /**
1851
- * `POST /routines/:id/training-blocks` and
1852
- * `PUT /routines/:id/training-blocks/:blockId`.
1853
- * Null or omission captures the routine as it exists at request time.
1854
- */
1855
- interface UpsertRoutineTrainingBlockRequest {
1856
- name: string;
1857
- startDate: CalendarDate;
1858
- endDate: CalendarDate;
1859
- sourceVersionId?: string | null;
1860
- }
1861
-
1862
1954
  /** Owner-declared, both optional. Undeclared is not a default, it is unknown. */
1863
1955
  interface RoutineClassification {
1864
1956
  goal?: TrainingGoal | null;
@@ -2910,4 +3002,4 @@ interface AccountExportV1 {
2910
3002
  /** The file name a download of the export is saved under. */
2911
3003
  declare function accountExportFileName(username: string, exportedAt: string): string;
2912
3004
 
2913
- export { ACCOUNT_EXPORT_FORMAT, ACCOUNT_EXPORT_VERSION, ACHIEVEMENT_CATEGORIES, ACHIEVEMENT_DEFINITIONS, ACTIVITY_CAPS, ACTIVITY_COMMENTS_PAGE_SIZE, ACTIVITY_COMMENTS_PER_DAY_MAX, ACTIVITY_COMMENT_MAX_LENGTH, ACTIVITY_DEFAULT_AUDIENCE, ACTIVITY_ENTRY_ID_MAX_LENGTH, ACTIVITY_FEED_FOLLOWED_MAX, ACTIVITY_PAGE_DEFAULT_LIMIT, ACTIVITY_PAGE_MAX_LIMIT, ACTIVITY_PREVIEW_AUDIENCES, ACTIVITY_REACTIONS, ACTIVITY_TYPES, ACTIVITY_TYPE_SECTIONS, type AccountExportGoal, type AccountExportMember, type AccountExportPersonalRecord, type AccountExportRoutine, type AccountExportScheduleOverride, type AccountExportTrainingEvent, type AccountExportTrainingLocation, type AccountExportV1, type AchievementActivity, type AchievementCategory, type AchievementCategoryProgress, type AchievementDefinition, type AchievementNotification, type AchievementUnlockedEventPayload, type AchievementsResponse, type ActivityAudience, type ActivityCap, type ActivityComment, type ActivityCommentNotification, type ActivityCommentSummary, type ActivityCommentsQuery, type ActivityCommentsResponse, type ActivityEntry, type ActivityEntrySharing, type ActivityFeedResponse, type ActivityLink, type ActivityPage, type ActivityPageQuery, type ActivityPreviewAudience, type ActivityPreviewQuery, type ActivityReaction, type ActivityReactionSummary, type ActivitySection, type ActivitySharingSettings, type ActivityType, type AppNotification, BLOCKED_MEMBERS_MAX, type BlockedMember, type BlockedMembersResponse, type Brand, CLONE_ROUTINE_REFUSALS, COMEBACK_MIN_INACTIVE_DAYS, COMEBACK_RECOGNITION_LIMIT, COMEBACK_REQUIRED_ACTIVE_DAYS, COMEBACK_SESSION_LOOKBACK, COMEBACK_WINDOW_DAYS, type CalendarDate, type CloneRoutineRefusal, type CloneRoutineRequest, type ComebackActivity, type ComebackRecognition, type ComebackRecognitionSummary, type CorrectSessionRequest, type CorrectSessionResponse, type CorrectSessionSetRequest, type CreateActivityCommentRequest, type CreateActivityCommentResponse, type CreateReportRequest, type CreateReportResponse, type CreateRoutineDayInput, type CreateRoutineExerciseInput, type CreateRoutineRequest, type CreateRoutineVersionRequest, type CreateSessionShareRequest, type DeleteAccountRequest, type DeleteAccountResponse, type DeleteActivityCommentResponse, type DeletePushSubscriptionRequest, type DiscoverableRoutine, EXERCISE_EQUIPMENT, EXERCISE_MECHANICS, type EarnedAchievement, type EarnedPersonalRecord, type Exercise, type ExerciseEquipment, type ExerciseId, type ExerciseMechanic, type ExercisePerformanceHistoryQuery, type ExercisePerformanceHistoryResponse, type ExercisePerformancePrescription, type ExercisePerformancePrescriptionSet, type ExercisePerformanceSession, type ExercisePerformanceSet, type ExercisePerformanceSummary, type ExercisePlateau, type ExerciseStrengthSummary, type ExerciseStrengthTrendPoint, type ExerciseStrengthTrendQuery, type ExerciseStrengthTrendResponse, FEATURED_PROFILE_ITEMS_MAX, FEATURED_PROFILE_ITEM_KINDS, FEATURED_PROFILE_REFERENCE_MAX_LENGTH, FOLLOW_SUGGESTIONS_DEFAULT_LIMIT, FOLLOW_SUGGESTIONS_MAX_LIMIT, FOLLOW_SUGGESTION_REASONS, type FeaturedProfileAchievementItem, type FeaturedProfileItem, type FeaturedProfileItemKind, type FeaturedProfileRankItem, type FeaturedProfileRecordItem, type FeaturedProfileRoutineItem, type FeaturedProfileSelection, type FeaturedProfileSelectionInput, type FeaturedProfileSelectionsResponse, type FinishStatus, type FinishWorkoutRequest, type FinishWorkoutResponse, type FollowSuggestion, type FollowSuggestionReason, type FollowSuggestionsResponse, type IsoDateString, type ListSessionsParams, MEASURABLE_GOALS_MAX, MEASURABLE_GOAL_DIRECTIONS, MEASURABLE_GOAL_TYPES, MINUTES_IN_DAY, MODERATION_ACTION_KINDS, MODERATION_HISTORY_PAGE_SIZE, MODERATION_NOTE_MAX_LENGTH, MODERATION_QUEUE_PAGE_SIZE, MOVEMENT_PATTERNS, MUSCLE_GROUPS, type MarkNotificationsReadRequest, type MarkNotificationsReadResponse, type MeasurableGoal, type MeasurableGoalDirection, type MeasurableGoalExercise, type MeasurableGoalInput, type MeasurableGoalType, type MemberActivityResponse, type MemberModerationState, type MemberRoutinesResponse, type ModerationActionKind, type ModerationActionRecord, type ModerationHistoryQuery, type ModerationHistoryResponse, type ModerationQueueQuery, type ModerationQueueResponse, type ModerationReport, type ModeratorState, type MoveOccurrenceRequest, type MovementPattern, type MuscleGroup, type MuscleGroupHeatmapQuery, type MuscleGroupHeatmapResponse, type MuscleGroupHeatmapValue, type MuscleGroupHeatmapWeek, type MuscleVolumeTrendSeries, NOTIFICATIONS_LIST_LIMIT, NOTIFICATIONS_LOOKBACK_DAYS, NOTIFICATIONS_RETENTION_DAYS, NOTIFICATION_CATEGORIES, NOTIFICATION_KINDS, type NewFollowerNotification, type NotificationCategory, type NotificationKind, type NotificationPreferences, type NotificationPreferencesResponse, type NotificationsResponse, type OwnActivityEntry, type OwnActivityResponse, PLATEAU_MIN_DAYS_SINCE_BEST, PLATEAU_MIN_SESSIONS, PLATEAU_MIN_SESSIONS_MAX, PLATEAU_MIN_SESSIONS_MIN, PLATEAU_RECENT_DAYS, PLATEAU_WINDOW_DAYS, PREFERRED_TRAINING_STYLE_VALUES, PROFILE_BIO_MAX_LENGTH, PROFILE_FAVORITE_EXERCISES_MAX, PROFILE_LOCATION_MAX_LENGTH, PROFILE_TRAINING_DISCIPLINES_MAX, PROFILE_TRAINING_GOALS_MAX, PROFILE_VISIBILITY_VALUES, PROGRESSION_SCHEMES, PROGRESS_TIMELINE_EVENT_TYPES, PUSH_PAYLOAD_KINDS, PUSH_SUBSCRIPTIONS_MAX, type PaginatedResponse, type PersonalGoalProgress, type PersonalGoalsResponse, type PersonalRecordActivity, type PersonalRecordEntry, type PersonalRecordKind, type PlannedReminder, type PlatePairInventory, type PlateauPreferences, type PlateauSet, type PlateausResponse, type PreferredTrainingStyle, type PreviousPerformanceResponse, type PreviousSessionRecap, type PreviousSetPerformance, type ProfileDiscoverySettings, type ProfileFavoriteExercise, type ProfilePrivacySettings, type ProfileViewerAccess, type ProfileVisibility, type ProgressTimelineEventType, type ProgressTimelineItem, type ProgressTimelinePersonalRecordItem, type ProgressTimelineProgressionItem, type ProgressTimelineQuery, type ProgressTimelineRecordPerformance, type ProgressTimelineRecordReason, type ProgressTimelineResponse, type ProgressTimelineSessionContext, type ProgressionActivity, type ProgressionChange, type ProgressionRule, type ProgressionScheme, type ProgressionSetChange, type PublicBodyMetrics, type PublicProfileAchievements, type PublicTrainingSummary, type PublicUserProfile, type PushPayload, type PushPayloadKind, type PushSubscriptionKeys, type PushSubscriptionSummary, type PushSubscriptionsResponse, type QuietHours, RELATIONSHIP_LIST_DEFAULT_LIMIT, RELATIONSHIP_LIST_KINDS, RELATIONSHIP_LIST_MAX_LIMIT, RENAISSANCE_RANK_DEFINITIONS, REPORTS_PER_DAY_MAX, REPORT_DETAILS_MAX_LENGTH, REPORT_REASONS, REPORT_STATUSES, REPORT_SUBJECT_KINDS, REP_TYPES, RESERVED_USERNAMES, REST_ALERT_MAX_LEAD_SECONDS, REST_ALERT_MIN_LEAD_SECONDS, REST_ALERT_REFUSALS, ROUTINE_DAYS_MAX, ROUTINE_DAY_NAME_MAX, ROUTINE_DISCOVERY_DEFAULT_LIMIT, ROUTINE_DISCOVERY_MAX_LIMIT, ROUTINE_DISCOVERY_SCAN_LIMIT, ROUTINE_DURATION_BANDS, ROUTINE_DURATION_BAND_MAX_MINUTES, ROUTINE_SCHEDULE_MODES, ROUTINE_SHARE_MAX_ACTIVE_LINKS, ROUTINE_TRAINING_BLOCKS_MAX, ROUTINE_TRAINING_BLOCK_NAME_MAX, ROUTINE_TRAINING_BLOCK_REVISIONS_MAX, ROUTINE_TRAINING_BLOCK_SOURCE_KINDS, ROUTINE_TRAINING_BLOCK_STATES, ROUTINE_VERSIONS_MAX, ROUTINE_VERSION_KINDS, ROUTINE_VERSION_NAME_MAX, ROUTINE_VISIBILITY_VALUES, type RecentActivityEntry, type RegisterPushSubscriptionRequest, type RelationshipListKind, type RelationshipListQuery, type RelationshipListResponse, type RelationshipMember, type RenaissanceRankDefinition, type RenaissanceRankProgress, type RepType, type ReplaceFeaturedProfileItemsRequest, type ReplaceMeasurableGoalsRequest, type ReplaceTrainingLocationsRequest, type ReportReason, type ReportStatus, type ReportSubjectKind, type ReportSubjectPreview, type RestAlertPushPayload, type RestAlertRefusal, type RestoreRoutineVersionResponse, type ReviewReportRequest, type ReviewReportResponse, type Routine, type RoutineClassification, type RoutineDay, type RoutineDayId, type RoutineDiscoveryQuery, type RoutineDiscoveryResponse, type RoutineDurationBand, type RoutineExercise, type RoutineFacets, type RoutineId, type RoutineLineage, type RoutineScheduleMode, type RoutineSet, type RoutineShare, type RoutineShareListResponse, type RoutineSharedActivity, type RoutineTrainingBlock, type RoutineTrainingBlockRevision, type RoutineTrainingBlockRevisionsResponse, type RoutineTrainingBlockSource, type RoutineTrainingBlockSourceKind, type RoutineTrainingBlockState, type RoutineTrainingBlocksResponse, type RoutineVersion, type RoutineVersionDay, type RoutineVersionExercise, type RoutineVersionKind, type RoutineVersionSetup, type RoutineVersionsResponse, type RoutineVisibility, SCHEDULE_MOVE_MAX_DAYS, SCHEDULE_OVERRIDES_MAX_RANGE_DAYS, SCHEDULE_OVERRIDE_KINDS, SCHEDULE_SKIP_PAST_DAYS, SESSION_CORRECTIONS_MAX, SESSION_CORRECTION_LIMITS, SESSION_CORRECTION_WINDOW_HOURS, SESSION_EXERCISE_NOTE_MAX_LENGTH, SESSION_NOTE_MAX_LENGTH, SESSION_SHARE_DEFAULT_FIELDS, SESSION_SHARE_FIELDS, SESSION_SHARE_MAX_ACTIVE_LINKS, SEXES, SHARED_ROUTINE_SOURCES, STARRED_EXERCISES_MAX, STREAK_MAX_GAP_DAYS, type ScheduleOverride, type ScheduleOverrideKind, type ScheduleOverridesQuery, type ScheduleOverridesResponse, type ScheduleRestAlertRequest, type ScheduleRestAlertResponse, type SendTrainingPartnerEncouragementRequest, type SendTrainingPartnerEncouragementResponse, type SessionComparisonExercise, type SessionComparisonQuery, type SessionComparisonResponse, type SessionComparisonRoutineDay, type SessionComparisonSession, type SessionComparisonSet, type SessionCompletedActivity, type SessionCorrection, type SessionCorrectionClosedReason, type SessionCorrectionWindow, type SessionCorrectionsResponse, type SessionExerciseNote, type SessionExerciseSubstitution, type SessionProgressNotification, type SessionRecapExerciseNote, type SessionRecapRecord, type SessionSetCorrection, type SessionShare, type SessionShareField, type SessionShareListResponse, type SetAccountTimeZoneRequest, type SetActivityEntryAudienceRequest, type SetActivityEntryAudienceResponse, type SetActivityReactionRequest, type SetActivityReactionResponse, type SetLog, type SetLogValues, type Sex, type SharedRoutine, type SharedRoutineOwner, type SharedRoutineSource, type SharedRoutineSummary, type SharedSessionOwner, type SharedSessionRecap, type SkipOccurrenceRequest, type StarredExercise, type StarredExercisesResponse, type StartWorkoutRequest, type StartWorkoutResponse, type StreakAtRiskPushPayload, type StreakMilestoneActivity, type StreakMilestoneEventPayload, type SubstituteSessionExerciseRequest, type SubstituteSessionExerciseResponse, type SupabaseAuthResponse, type SupabaseAuthUser, type SupabaseMigrationResponse, TRAINING_DISCIPLINE_VALUES, TRAINING_EVENT_TYPES, TRAINING_EXPERIENCE_LEVEL_VALUES, TRAINING_GOAL_VALUES, TRAINING_PARTNERSHIP_STATUSES, TRAINING_PARTNERS_MAX, TRAINING_PARTNER_ENCOURAGEMENTS_PER_24_HOURS_MAX, TRAINING_PARTNER_ENCOURAGEMENT_KINDS, TRAINING_PARTNER_PERMISSION_KEYS, TRAINING_PARTNER_REQUESTS_PER_DAY_MAX, type TrainingDiscipline, type TrainingEventType, type TrainingExperienceLevel, type TrainingGoal, type TrainingIdentity, type TrainingLocationPreference, type TrainingLocationPreferenceInput, type TrainingPartnerEncouragementKind, type TrainingPartnerEncouragementNotification, type TrainingPartnerPermissions, type TrainingPartnerScheduleDay, type TrainingPartnerScheduleResponse, type TrainingPartnership, type TrainingPartnershipStatus, type TrainingPartnershipsResponse, type TrainingReminderPreference, type TrainingReminderPushPayload, USERNAME_MAX_LENGTH, USERNAME_MIN_LENGTH, USERNAME_PATTERN_SOURCE, type UpdateActivitySharingRequest, type UpdateNotificationPreferencesRequest, type UpdateProfileDiscoveryRequest, type UpdateProfilePrivacyRequest, type UpdateProfileRequest, type UpdateRoutineRequest, type UpdateRoutineVisibilityRequest, type UpdateSessionNotesRequest, type UpdateSessionNotesResponse, type UpdateTrainingPartnerPermissionsRequest, type UpsertRoutineTrainingBlockRequest, type UpsertSetLogRequest, type UpsertSetLogResponse, type UserId, type UserProfile, type UserSearchResponse, type VolumeTrendPoint, type VolumeTrendQuery, type VolumeTrendResponse, type VolumeTrendSeries, WEIGHT_UNITS, WORKOUT_SESSION_STATUSES, type WeightUnit, type WorkoutAnalyticsStatus, type WorkoutProgressQuery, type WorkoutProgressResponse, type WorkoutSession, type WorkoutSessionId, type WorkoutSessionListResponse, type WorkoutSessionRecap, type WorkoutSessionSnapshotV1, type WorkoutSessionStatus, type WorkoutSessionSummary, type WorkoutStatsQuery, type WorkoutStatsResponse, accountExportFileName, isWithinQuietHours, matchesDeletionConfirmation, routineDayLabel, sessionCorrectionWindow, setLogValuesDiffer };
3005
+ export { ACCOUNT_EXPORT_FORMAT, ACCOUNT_EXPORT_VERSION, ACHIEVEMENT_CATEGORIES, ACHIEVEMENT_DEFINITIONS, ACTIVITY_CAPS, ACTIVITY_COMMENTS_PAGE_SIZE, ACTIVITY_COMMENTS_PER_DAY_MAX, ACTIVITY_COMMENT_MAX_LENGTH, ACTIVITY_DEFAULT_AUDIENCE, ACTIVITY_ENTRY_ID_MAX_LENGTH, ACTIVITY_FEED_FOLLOWED_MAX, ACTIVITY_PAGE_DEFAULT_LIMIT, ACTIVITY_PAGE_MAX_LIMIT, ACTIVITY_PREVIEW_AUDIENCES, ACTIVITY_REACTIONS, ACTIVITY_TYPES, ACTIVITY_TYPE_SECTIONS, type AccountExportGoal, type AccountExportMember, type AccountExportPersonalRecord, type AccountExportRoutine, type AccountExportScheduleOverride, type AccountExportTrainingEvent, type AccountExportTrainingLocation, type AccountExportV1, type AchievementActivity, type AchievementCategory, type AchievementCategoryProgress, type AchievementDefinition, type AchievementNotification, type AchievementUnlockedEventPayload, type AchievementsResponse, type ActivityAudience, type ActivityCap, type ActivityComment, type ActivityCommentNotification, type ActivityCommentSummary, type ActivityCommentsQuery, type ActivityCommentsResponse, type ActivityEntry, type ActivityEntrySharing, type ActivityFeedResponse, type ActivityLink, type ActivityPage, type ActivityPageQuery, type ActivityPreviewAudience, type ActivityPreviewQuery, type ActivityReaction, type ActivityReactionSummary, type ActivitySection, type ActivitySharingSettings, type ActivityType, type AppNotification, BLOCKED_MEMBERS_MAX, type BlockedMember, type BlockedMembersResponse, type Brand, CLONE_ROUTINE_REFUSALS, COMEBACK_MIN_INACTIVE_DAYS, COMEBACK_RECOGNITION_LIMIT, COMEBACK_REQUIRED_ACTIVE_DAYS, COMEBACK_SESSION_LOOKBACK, COMEBACK_WINDOW_DAYS, type CalendarDate, type CloneRoutineRefusal, type CloneRoutineRequest, type ComebackActivity, type ComebackRecognition, type ComebackRecognitionSummary, type CorrectSessionRequest, type CorrectSessionResponse, type CorrectSessionSetRequest, type CreateActivityCommentRequest, type CreateActivityCommentResponse, type CreateReportRequest, type CreateReportResponse, type CreateRoutineDayInput, type CreateRoutineExerciseInput, type CreateRoutineRequest, type CreateRoutineVersionRequest, type CreateSessionShareRequest, type DeleteAccountRequest, type DeleteAccountResponse, type DeleteActivityCommentResponse, type DeletePushSubscriptionRequest, type DiscoverableRoutine, EXERCISE_EQUIPMENT, EXERCISE_MECHANICS, type EarnedAchievement, type EarnedPersonalRecord, type Exercise, type ExerciseEquipment, type ExerciseId, type ExerciseMechanic, type ExercisePerformanceHistoryQuery, type ExercisePerformanceHistoryResponse, type ExercisePerformancePrescription, type ExercisePerformancePrescriptionSet, type ExercisePerformanceSession, type ExercisePerformanceSet, type ExercisePerformanceSummary, type ExercisePlateau, type ExerciseStrengthSummary, type ExerciseStrengthTrendPoint, type ExerciseStrengthTrendQuery, type ExerciseStrengthTrendResponse, FEATURED_PROFILE_ITEMS_MAX, FEATURED_PROFILE_ITEM_KINDS, FEATURED_PROFILE_REFERENCE_MAX_LENGTH, FOLLOW_SUGGESTIONS_DEFAULT_LIMIT, FOLLOW_SUGGESTIONS_MAX_LIMIT, FOLLOW_SUGGESTION_REASONS, type FeaturedProfileAchievementItem, type FeaturedProfileItem, type FeaturedProfileItemKind, type FeaturedProfileRankItem, type FeaturedProfileRecordItem, type FeaturedProfileRoutineItem, type FeaturedProfileSelection, type FeaturedProfileSelectionInput, type FeaturedProfileSelectionsResponse, type FinishStatus, type FinishWorkoutRequest, type FinishWorkoutResponse, type FollowSuggestion, type FollowSuggestionReason, type FollowSuggestionsResponse, type IsoDateString, type ListSessionsParams, MEASURABLE_GOALS_MAX, MEASURABLE_GOAL_DIRECTIONS, MEASURABLE_GOAL_TYPES, MINUTES_IN_DAY, MODERATION_ACTION_KINDS, MODERATION_HISTORY_PAGE_SIZE, MODERATION_NOTE_MAX_LENGTH, MODERATION_QUEUE_PAGE_SIZE, MOVEMENT_PATTERNS, MUSCLE_GROUPS, type MarkNotificationsReadRequest, type MarkNotificationsReadResponse, type MeasurableGoal, type MeasurableGoalDirection, type MeasurableGoalExercise, type MeasurableGoalInput, type MeasurableGoalType, type MemberActivityResponse, type MemberModerationState, type MemberRoutinesResponse, type ModerationActionKind, type ModerationActionRecord, type ModerationHistoryQuery, type ModerationHistoryResponse, type ModerationQueueQuery, type ModerationQueueResponse, type ModerationReport, type ModeratorState, type MoveOccurrenceRequest, type MovementPattern, type MuscleGroup, type MuscleGroupHeatmapQuery, type MuscleGroupHeatmapResponse, type MuscleGroupHeatmapValue, type MuscleGroupHeatmapWeek, type MuscleVolumeTrendSeries, NOTIFICATIONS_LIST_LIMIT, NOTIFICATIONS_LOOKBACK_DAYS, NOTIFICATIONS_RETENTION_DAYS, NOTIFICATION_CATEGORIES, NOTIFICATION_KINDS, type NewFollowerNotification, type NotificationCategory, type NotificationKind, type NotificationPreferences, type NotificationPreferencesResponse, type NotificationsResponse, type OwnActivityEntry, type OwnActivityResponse, PLATEAU_MIN_DAYS_SINCE_BEST, PLATEAU_MIN_SESSIONS, PLATEAU_MIN_SESSIONS_MAX, PLATEAU_MIN_SESSIONS_MIN, PLATEAU_RECENT_DAYS, PLATEAU_WINDOW_DAYS, PREFERRED_TRAINING_STYLE_VALUES, PROFILE_BIO_MAX_LENGTH, PROFILE_FAVORITE_EXERCISES_MAX, PROFILE_LOCATION_MAX_LENGTH, PROFILE_TRAINING_DISCIPLINES_MAX, PROFILE_TRAINING_GOALS_MAX, PROFILE_VISIBILITY_VALUES, PROGRESSION_SCHEMES, PROGRESS_TIMELINE_EVENT_TYPES, PUSH_PAYLOAD_KINDS, PUSH_SUBSCRIPTIONS_MAX, type PaginatedResponse, type PersonalGoalProgress, type PersonalGoalsResponse, type PersonalRecordActivity, type PersonalRecordEntry, type PersonalRecordKind, type PlannedReminder, type PlatePairInventory, type PlateauPreferences, type PlateauSet, type PlateausResponse, type PreferredTrainingStyle, type PreviousPerformanceResponse, type PreviousSessionRecap, type PreviousSetPerformance, type ProfileDiscoverySettings, type ProfileFavoriteExercise, type ProfilePrivacySettings, type ProfileViewerAccess, type ProfileVisibility, type ProgressTimelineEventType, type ProgressTimelineItem, type ProgressTimelinePersonalRecordItem, type ProgressTimelineProgressionItem, type ProgressTimelineQuery, type ProgressTimelineRecordPerformance, type ProgressTimelineRecordReason, type ProgressTimelineResponse, type ProgressTimelineSessionContext, type ProgressionActivity, type ProgressionChange, type ProgressionRule, type ProgressionScheme, type ProgressionSetChange, type PublicBodyMetrics, type PublicProfileAchievements, type PublicTrainingSummary, type PublicUserProfile, type PushPayload, type PushPayloadKind, type PushSubscriptionKeys, type PushSubscriptionSummary, type PushSubscriptionsResponse, type QuietHours, RELATIONSHIP_LIST_DEFAULT_LIMIT, RELATIONSHIP_LIST_KINDS, RELATIONSHIP_LIST_MAX_LIMIT, RENAISSANCE_RANK_DEFINITIONS, REPORTS_PER_DAY_MAX, REPORT_DETAILS_MAX_LENGTH, REPORT_REASONS, REPORT_STATUSES, REPORT_SUBJECT_KINDS, REP_TYPES, RESERVED_USERNAMES, REST_ALERT_MAX_LEAD_SECONDS, REST_ALERT_MIN_LEAD_SECONDS, REST_ALERT_REFUSALS, ROUTINE_DAYS_MAX, ROUTINE_DAY_NAME_MAX, ROUTINE_DISCOVERY_DEFAULT_LIMIT, ROUTINE_DISCOVERY_MAX_LIMIT, ROUTINE_DISCOVERY_SCAN_LIMIT, ROUTINE_DURATION_BANDS, ROUTINE_DURATION_BAND_MAX_MINUTES, ROUTINE_SCHEDULE_MODES, ROUTINE_SHARE_MAX_ACTIVE_LINKS, ROUTINE_TRAINING_BLOCKS_MAX, ROUTINE_TRAINING_BLOCK_NAME_MAX, ROUTINE_TRAINING_BLOCK_REVISIONS_MAX, ROUTINE_TRAINING_BLOCK_SOURCE_KINDS, ROUTINE_TRAINING_BLOCK_STATES, ROUTINE_VERSIONS_MAX, ROUTINE_VERSION_KINDS, ROUTINE_VERSION_NAME_MAX, ROUTINE_VISIBILITY_VALUES, type RecentActivityEntry, type RegisterPushSubscriptionRequest, type RelationshipListKind, type RelationshipListQuery, type RelationshipListResponse, type RelationshipMember, type RenaissanceRankDefinition, type RenaissanceRankProgress, type RepType, type ReplaceFeaturedProfileItemsRequest, type ReplaceMeasurableGoalsRequest, type ReplaceTrainingLocationsRequest, type ReportReason, type ReportStatus, type ReportSubjectKind, type ReportSubjectPreview, type ResolvedRoutinePlan, type RestAlertPushPayload, type RestAlertRefusal, type RestoreRoutineVersionResponse, type ReviewReportRequest, type ReviewReportResponse, type Routine, type RoutineClassification, type RoutineDay, type RoutineDayId, type RoutineDiscoveryQuery, type RoutineDiscoveryResponse, type RoutineDurationBand, type RoutineExercise, type RoutineFacets, type RoutineId, type RoutineLineage, type RoutinePlanSource, type RoutineScheduleMode, type RoutineSet, type RoutineShare, type RoutineShareListResponse, type RoutineSharedActivity, type RoutineTrainingBlock, type RoutineTrainingBlockPlan, type RoutineTrainingBlockRevision, type RoutineTrainingBlockRevisionsResponse, type RoutineTrainingBlockSource, type RoutineTrainingBlockSourceKind, type RoutineTrainingBlockState, type RoutineTrainingBlocksResponse, type RoutineVersion, type RoutineVersionDay, type RoutineVersionExercise, type RoutineVersionKind, type RoutineVersionSetup, type RoutineVersionsResponse, type RoutineVisibility, SCHEDULE_MOVE_MAX_DAYS, SCHEDULE_OVERRIDES_MAX_RANGE_DAYS, SCHEDULE_OVERRIDE_KINDS, SCHEDULE_SKIP_PAST_DAYS, SESSION_CORRECTIONS_MAX, SESSION_CORRECTION_LIMITS, SESSION_CORRECTION_WINDOW_HOURS, SESSION_EXERCISE_NOTE_MAX_LENGTH, SESSION_NOTE_MAX_LENGTH, SESSION_SHARE_DEFAULT_FIELDS, SESSION_SHARE_FIELDS, SESSION_SHARE_MAX_ACTIVE_LINKS, SEXES, SHARED_ROUTINE_SOURCES, STARRED_EXERCISES_MAX, STREAK_MAX_GAP_DAYS, type ScheduleOverride, type ScheduleOverrideKind, type ScheduleOverridesQuery, type ScheduleOverridesResponse, type ScheduleRestAlertRequest, type ScheduleRestAlertResponse, type SendTrainingPartnerEncouragementRequest, type SendTrainingPartnerEncouragementResponse, type SessionComparisonExercise, type SessionComparisonQuery, type SessionComparisonResponse, type SessionComparisonRoutineDay, type SessionComparisonSession, type SessionComparisonSet, type SessionCompletedActivity, type SessionCorrection, type SessionCorrectionClosedReason, type SessionCorrectionWindow, type SessionCorrectionsResponse, type SessionExerciseNote, type SessionExerciseSubstitution, type SessionProgressNotification, type SessionRecapExerciseNote, type SessionRecapRecord, type SessionSetCorrection, type SessionShare, type SessionShareField, type SessionShareListResponse, type SessionTrainingBlock, type SetAccountTimeZoneRequest, type SetActivityEntryAudienceRequest, type SetActivityEntryAudienceResponse, type SetActivityReactionRequest, type SetActivityReactionResponse, type SetLog, type SetLogValues, type Sex, type SharedRoutine, type SharedRoutineOwner, type SharedRoutineSource, type SharedRoutineSummary, type SharedSessionOwner, type SharedSessionRecap, type SkipOccurrenceRequest, type StarredExercise, type StarredExercisesResponse, type StartWorkoutRequest, type StartWorkoutResponse, type StreakAtRiskPushPayload, type StreakMilestoneActivity, type StreakMilestoneEventPayload, type SubstituteSessionExerciseRequest, type SubstituteSessionExerciseResponse, type SupabaseAuthResponse, type SupabaseAuthUser, type SupabaseMigrationResponse, TRAINING_DISCIPLINE_VALUES, TRAINING_EVENT_TYPES, TRAINING_EXPERIENCE_LEVEL_VALUES, TRAINING_GOAL_VALUES, TRAINING_PARTNERSHIP_STATUSES, TRAINING_PARTNERS_MAX, TRAINING_PARTNER_ENCOURAGEMENTS_PER_24_HOURS_MAX, TRAINING_PARTNER_ENCOURAGEMENT_KINDS, TRAINING_PARTNER_PERMISSION_KEYS, TRAINING_PARTNER_REQUESTS_PER_DAY_MAX, type TrainingDiscipline, type TrainingEventType, type TrainingExperienceLevel, type TrainingGoal, type TrainingIdentity, type TrainingLocationPreference, type TrainingLocationPreferenceInput, type TrainingPartnerEncouragementKind, type TrainingPartnerEncouragementNotification, type TrainingPartnerPermissions, type TrainingPartnerScheduleDay, type TrainingPartnerScheduleResponse, type TrainingPartnership, type TrainingPartnershipStatus, type TrainingPartnershipsResponse, type TrainingReminderPreference, type TrainingReminderPushPayload, USERNAME_MAX_LENGTH, USERNAME_MIN_LENGTH, USERNAME_PATTERN_SOURCE, type UpdateActivitySharingRequest, type UpdateNotificationPreferencesRequest, type UpdateProfileDiscoveryRequest, type UpdateProfilePrivacyRequest, type UpdateProfileRequest, type UpdateRoutineRequest, type UpdateRoutineVisibilityRequest, type UpdateSessionNotesRequest, type UpdateSessionNotesResponse, type UpdateTrainingPartnerPermissionsRequest, type UpsertRoutineTrainingBlockRequest, type UpsertSetLogRequest, type UpsertSetLogResponse, type UserId, type UserProfile, type UserSearchResponse, type VolumeTrendPoint, type VolumeTrendQuery, type VolumeTrendResponse, type VolumeTrendSeries, WEIGHT_UNITS, WORKOUT_SESSION_STATUSES, type WeightUnit, type WorkoutAnalyticsStatus, type WorkoutProgressQuery, type WorkoutProgressResponse, type WorkoutSession, type WorkoutSessionId, type WorkoutSessionListResponse, type WorkoutSessionRecap, type WorkoutSessionSnapshotV1, type WorkoutSessionStatus, type WorkoutSessionSummary, type WorkoutStatsQuery, type WorkoutStatsResponse, accountExportFileName, isWithinQuietHours, matchesDeletionConfirmation, resolveActiveTrainingBlock, resolveRoutinePlan, routineDayLabel, sessionCorrectionWindow, setLogValuesDiffer, trainingBlockCovers };