CODEX // oaoisme wiki

§ Forge — hypertrophy tracker · Architecture — stack & data model

The progression engine

updated 2026-06-08

The feature that earns the app. All of it lives in engine.js as pure functions, so it's unit-tested in isolation (engine.test.js).

Double progression

"Double progression" means you progress along two dimensions in order: first reps, then weight. Pick a rep range (say 6–10). Keep the weight fixed and add reps each session until you hit the top of the range on every prescribed set. Only then add weight — and drop back to the bottom of the range. Repeat.

It's the right scheme for a barbell+dumbbell hypertrophy setup because small fixed jumps (2.5 kg on the bar, 2 kg per dumbbell) are the only increments you have, and double progression lets you make progress between those jumps via reps.

nextTarget(exercise, lastSets, targetSets)

Given the working sets from the most recent prior session for that exercise, it returns one decisive verdict:

  • No historyaction: 'start', no weight, note: "First time — pick a load you can get N clean reps with."
  • Every set at the top weight hit the rep ceiling, and you did all prescribed setsaction: 'add_weight', weight = topWeight + increment, reps reset to the bottom. Shown as e.g. 4×6 62.5kg ▲ +2.5.
  • Otherwiseaction: 'add_reps', keep the weight, per-set targets nudge +1 rep toward the ceiling.

A subtle but important detail: "today's target" must use your previous performance, so db.lastSetsForExercise(exKey, excludeSessionId) skips the currently-active session. Log a set mid-workout and the target doesn't rewrite itself from your own in-progress work.

Estimated 1RM (Epley)

A one-rep-max estimate lets you compare sets at different weights and reps on one axis. Forge uses the Epley formula:

1RM ≈ weight × (1 + reps / 30)

So 60 kg × 10 ≈ 80 kg, and 62.5 kg × 10 ≈ 83.3 kg. This drives the Progress trend charts (best working set per session, plotted over time) and PR detection.

The PR stamp

When you log a working set, the server computes its est-1RM and compares it to your best from all prior sessions (db.bestE1RM(exKey, excludeCurrentSession)). Beat it and the API returns isPR: true; the client stamps the set white-hot — the only place, with the body map, that warm colour appears. PRs compare across sessions, so beating your own earlier set within the same workout is not a PR (correct: you haven't beaten your record yet, you're setting it).

Why pure functions

engine.js reads no clock and touches no database — the caller passes now and pre-shaped rows. That's what makes engine.test.js able to assert, e.g., "3×10 at the ceiling ⇒ +2.5 kg and reps reset to 6" deterministically, and it's why the same math can run server-side for rendering and in the PR check without duplication.