Hooks & macros reference

Hooks are blocks of logic that run at moments in a workout's lifecycle — they read what happened (reps, weights, whether you hit your targets) and write back training-max and state changes that make a program adapt from session to session. A progression macro packages hook logic (and optionally a set scheme) into a reusable rule you attach to as many exercises as you like with using.

This is the complete reference. For a gentler introduction, start with Progressions & hooks.

Two flavours of macro

FlavourBody containsWhat using does
Set-schemeset lines (5 @%90, amrap @%100)Expands into the exercise's sets
Hookan on_complete / on_set blockBecomes the exercise's progression hook

A macro may contain both. The built-in macros (below) are set-scheme only — they lay out reps and percentages but do not progress the training max on their own. Pair them with a hook (inline or your own macro) to add progression.

A macro's on_complete hook drives progression today; an on_set hook is parsed and validated but not yet evaluated at runtime (see runtime caveats below).

Attach with using:

Parameters

A macro declares parameters in parentheses. Each may have an optional : type annotation (number or weight) and an optional = default:

progression "ramp" (increment = 2.5, bump: weight = 2.5kg, target: weight) { ... }

Value- vs reference-binding

How you bind an argument at the using site decides whether the macro can write to it — this is what makes a macro stateful.

BindingExampleInside the macro
Value (literal)using "ramp" (bump: 5kg)Read-only. Assigning to it is a compile error.
Reference (state)using "ramp" (load: state.row_weight)Writable. Assignments write through to the referenced state variable.
Barbell Row {
  8-12 @state.row_weight
  using "double_prog" (load: state.row_weight, top: 12)
}

A set-scheme macro takes literal arguments only — passing a state reference to one is an error, because there's no number to expand into a set.

Override & precedence rules

Built-in macros

Four set-scheme macros ship with every program — use them by name without defining anything. Each declares an increment parameter for convention, but remember these are set layouts, not progression logic:

NameSets it lays outDefaults
lpamrap @%100increment = 5
lp_deload5 / 3 / amrap, all at @%deload_pctincrement = 5, deload_pct = 90
dp5 @%90, 3 @%95, amrap @%100increment = 2.5
wave_lp5 @%65, 5 @%75, 5 @%85increment = 5
Bench Press {
  using "dp"
}

Built-in functions

Available inside any hook body (and therefore any macro hook):

FunctionReturns
round(x, n)x rounded to the nearest multiple of n
floor(x) / ceil(x)Round down / up to a whole number
min(a, b) / max(a, b)Smaller / larger of the two
clamp(x, lo, hi)x constrained to [lo, hi]
pct_of(base, pct)base × pct (pct as a fraction)
rpe_to_pct(reps, rpe)%1RM for a reps/RPE pair (RTS table)
pick(cond, a, b)a if cond is non-zero, else b

Unit-aware increments

Weights in ReptoLang are raw numbers labelled with a unit — nothing ever converts kg ↔ lbs. To bump by the right amount for either lifter, write a unit pair { <kg>, <lbs> }:

{ Akg, Blbs } is shorthand for pick(unit_is_metric, A, B). It needs exactly one kg and one lbs slot (order doesn't matter), each a non-negative weight literal — a leading - is a parse error, so write a subtraction (tm.self -= { 2.5kg, 5lbs }) instead.

The two hook kinds

HookFiresTypical job
on_setmid-workout, as you finish each setPre-fill the next set (autoregulation)
on_completeafter a unit (exercise / day / week) is fully doneDecide progression — bump a TM, update state

on_set is meant to run during a workout; on_complete runs when you complete the session.

on_set is parsed and validated but not yet evaluated at runtime. The compiler accepts on_set blocks (inline or supplied by a macro), but the app currently drives behavior from on_complete only — next_set_weight / next_set_reps are not applied yet. Write on_set logic if you like, but don't depend on it taking effect today.

Hook scopes

on_complete can be attached at three scopes. They are evaluated in this order when a session completes:

  1. Exercise — once per completed exercise block.
  2. Day — once, after every exercise in the day.
  3. Week — once, only on the session that completes the last day of the week.

State writes thread forward: a state change made by an exercise hook is visible to the day hook, and the day's writes are visible to the week hook.

Program-level on_complete is parsed but not evaluated. The grammar accepts a top-level on_complete block, but the app does not run it at workout completion — don't rely on it. Use exercise, day, or week scope instead.

Variables by scope

Every variable is a number (booleans are 1/0). Weights are in the lifter's own unit and are never converted. Which variables exist depends on the scope, because day- and week-level hooks have no single "current set" to report.

Exercise on_complete

VariableMeaning
completed1 if every working set hit its target
completed_repsReps logged on the final working set
amrap_repsReps on the heaviest AMRAP working set (0 if none) — distinct from completed_reps for schemes like nSuns whose top AMRAP set isn't the final one
total_repsTotal reps across all working sets
target_repsTarget reps (a range's minimum)
current_weightWeight on the final working set
set_index / set_countIndex / count of working sets
failed_setsWorking sets that fell short of target
rpe_logged / rir_loggedRPE / RIR logged on the final set (0 if none)
is_amrap1 if the final set was AMRAP
is_pr1 if this exercise set a PR this session
estimated_1rmEstimated 1RM from the heaviest set
week_index / day_index1-based position in the program
session_countTotal sessions completed in this enrollment
sum_volumeWeight × reps across this exercise's sets
unit_is_metric1 for kg, 0 for lbs

Plus tm.self (this exercise's training max), tm.<exercise> (any TM in the tm { } block), and state.<name> (any declared state variable).

Day on_complete

Day hooks see only program-context variables — no per-set data, and no tm.self:

week_index, day_index, session_count, day_volume (weight × reps across the whole day), unit_is_metric, and state.<name>.

Week on_complete

Everything a day hook sees, plus week_volume (weight × reps across every session in the week).

on_set outputs

on_set is intended to run mid-workout and expose the in-progress set context. The two variables it may write are the only way to influence the next set (note the runtime caveat above — these outputs aren't applied yet):

Writable outputEffect
next_set_weightPre-fills the weight of the next set
next_set_repsPre-fills the target reps of the next set

What a hook may write

TargetAllowed inNotes
tm.selfexercise on_completeThis exercise's training max
tm.<exercise>exercise on_completeAny TM declared in tm { }
state.<name>any scopeAny variable declared in state { }
next_set_weight / next_set_repson_set onlyPre-fill the next set

Anything else is rejected at compile time. In particular, deep paths like state.x.y and reserved keys (__proto__, constructor, prototype, hasOwnProperty) are forbidden.

Type rules

Hook bodies are type-checked at compile time, distinguishing plain numbers from unit-carrying weights:

Runtime caveats

These rules are enforced when the app evaluates your hooks. A hook can be syntactically valid yet silently do nothing if it ignores them.

Deload weeks

On a deload week, on_complete hooks are suppressed by default so a back-off week doesn't trigger progression. on_set still runs. Re-enable on_complete with hooks: enabled at the week (or day) level:

A real example

This is the 5/3/1 main-lift progression: on the week-3 AMRAP top set, if you hit at least one rep, bump the training max by the unit-correct amount.

For the full grammar, see the language reference.