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