Running Powerlifting Training with an AI Agent and Git

Language: English

Last year I joined a gym and started powerlifting, a sport that scores the total weight lifted across three events: squat, bench press, and deadlift.

In powerlifting, the usual way is to design a plan of several weeks to several months around the current goal. The plan may include fine load adjustments, and when it does, even the same lift may change in weight or reps from session to session. Write it, train, check it against the record, revise the plan. I run that loop too.

Until recently I wrote plans and records on paper or in a spreadsheet, and none of it sat right. Why not, and what kind of training notebook did I actually want? Recently I went back and rethought the requirements.

This article is my provisional answer. In one line, the requirement is to treat a training plan as an experimental design and to function as its lab notebook. I now run the loop, from planning through execution and review to planning the next one, in conversation with an agent on top of a single Git repository. I’m still partway through the first plan run this way. The repository itself is private, since it holds personal information and what my coach has taught me, but the structure and the thinking are showable. The subject is powerlifting, but what I’m writing about is the design of a system that checks plans against results so you can run the improvement loop yourself. Where in that loop to put an AI agent, and where not to: that part, I think, holds whatever the subject.

A training plan is an experimental design

There are well-known theories for how to build a plan, but what’s optimal differs between people, and even for one person it differs by season and by the current weakness. The shape that fits me, at least, can only be found by forming a hypothesis, trying it, and reading the results. So I keep the plan and the outcomes as a lab notebook.

The plan is a hypothesis. At design time I write down what to try, what to test, and which indicators to watch during the plan. The daily logs are the data. The results are the RM tests (the maximum weight you can lift for a prescribed number of reps) at the end of each block (a unit of several weeks that divides a plan) and the meet records. When a plan ends, hypothesis and data get compared, findings get written down, and those feed into designing the next plan. The repository structure is a direct copy of this loop: hypotheses in plans/, data in logs/, results in meets/.

Treat a menu as a schedule to get through, and a day I fail to hit the intended weight and reps is evaluated, on that alone, as a “failure.” But designing the next plan accurately requires separating whether the shortfall came from execution or from the plan. Treat it as an experiment, and there are two separate things to evaluate: whether I followed the plan (adherence), and whether the plan was right (validity). Distinguishing the two presupposes recording deviations from the plan accurately.

When plan and actual diverge, the side that needs revising isn’t necessarily execution. A day I couldn’t complete the planned volume (sets × reps), if the cause was how I felt, is not a reason to lower the next planned weight. I try again in better condition. The other way around, a day I lifted more than planned and it felt easier than intended means execution had room to spare, so the plan may have been too conservative. Keeping the day’s condition alongside plan and actual is what makes it possible to tell whether execution or the plan should be revised.

I started building an app, then stopped

If plan and actual are going to be kept as a lab notebook, they need a vessel. After paper and spreadsheets, the next thing I tried was building a dedicated app.

What I was building was Rx, a training-data backend: a Go API server, a Next.js web UI, PostgreSQL, and a schema-first design in which Protocol Buffers definitions generate the gRPC and grpc-gateway code along with the OpenAPI spec. About two and a half months and 753 commits (though most of the implementation was delegated to Claude Code and Cursor, so it took less effort than that sounds).

The problem it targeted was clear to me. Most training apps assume a fixed schema such as sets × reps × weight, while a real program needs information they have no fields for: tempo prescriptions, target RPE (rating of perceived exertion), pause durations, rest intervals between sets. I think one reason spreadsheets remain so entrenched among lifters is exactly this freedom of schema. Rx tried to reconcile flexibility with structure through a design of “a fixed structured core plus user-definable fields.” Planning and analysis were the agent’s work from the start; Rx itself was to be a data store that stores without interpreting.

I stopped because of what I noticed while building it. If you run a plan as an experiment, what you want to record changes from plan to plan. In one plan I was testing stance width and foot angle on wide-stance deadlift. In another I was changing the rest between sets. In another I wanted a fine-grained record of how much power dropped at a given rate of weight cut. Anything the core lacks can be added as a user-defined field, so the design covers it. But if what I want to know changes every plan, how much meaning does a core structure fixed across plans really have? That suspicion stuck. The information I most want to know ends up in user-defined fields, and as their number grows, mechanical aggregation and UI design both get harder. The advantages of a database thin out.

The other reason is where the agent sits. If an agent is always at the read and write boundary, the persistence layer doesn’t need a strict schema of its own: the agent can shape each write into a Markdown convention. If planning and analysis were Claude Code’s job anyway, I could drop the backend in the middle and have it read and write Markdown directly. What went away wasn’t the schema. It was the dedicated backend that owned the schema. For a notebook still being explored, I judged it better to move the responsibility for validation and formatting off a fixed schema the database enforces and onto a convention the agent applies at write time.

There is a trade-off, of course. On input effort alone, a dedicated form takes fewer steps than conversation. I chose flexibility anyway. Right now I’m still exploring what the record schema should even be.

Keep plan and actual in a comparable shape

Free-form doesn’t mean everything is prose. The partner writing this lab notebook with me is an agent, so the structure is decided on the premise that an agent will read it. The data that copies the experiment loop, and the control plane the agent uses to operate on it, live in the same repository.

.
├── plans/                          # hypotheses; 1 plan = 1 directory
│   ├── 2026-05/
│   │   ├── plan.md                 # plan design document (design decisions, change log)
│   │   ├── b1w1d1.md               # 1 session = 1 menu (block, week, day)
│   │   ├── b1w1d2.md
│   │   └── ...
│   └── ...
├── logs/                           # data; 1 session = 1 log
│   ├── 2026-05-05.md
│   ├── 2026-05-06.md
│   └── ...
├── meets/                          # results; 1 meet = 1 file
│   ├── 2026-04_tokyo-meet.md
│   └── ...
├── CLAUDE.md                       # conventions shared by every use case
├── .claude/skills/                 # procedures per use case (five; see below)
├── context/                        # free-form notes
│   ├── 2026-04-26_form-coaching.md # coaching sessions and seminars
│   ├── program-design-principles.md # theory and policy summaries
│   └── ...
└── docs/
    └── decisions/                  # decisions about the system; 1 decision = 1 file
        ├── 2026-07-18_four-layer-decision-records.md
        ├── 2026-07-19_companion-mode-start-flow.md
        └── ...

The schema didn’t disappear. It lives on as conventions in CLAUDE.md. The line of discipline coincides with the directory boundary: plans/, logs/, and meets/ follow the format strictly, while context/ and docs/ are free-form. Data that gets aggregated and cross-referenced keeps its structure; one-off information, like notes on what a coach taught me, is written freely. Log frontmatter carries the owning plan and session name, so the corresponding menu file can be reached. Here is what one actually looks like:

---
date: 2026-07-24(Fri)
cycle: 2026-05
session: b2w2d3
---

## Record

| Exercise | Segment | RPE | weight | reps | sets | note |
|---|---|---|---|---|---|---|
| Narrow BP | top | 6 | 110 | 1 | 1 | heavier than planned 107.5 |
| Narrow BP | main | 6 | 100 | 4 | 1 | heavier than planned 97.5 |
| Narrow BP | back-off | - | 90 | 4 | 6 | heavier than planned 87.5; sets 2–4 without wraps |
| Low-bar SQ | top | 8 | 205 | 1 | 1 | heavier than planned 200 |
| Low-bar SQ | main | 6 | 175 | 6 | 1 | heavier than planned 172.5 |
| Low-bar SQ | back-off | - | 155 | 6 | 2 |  |
| DB press | - | - | 30 | 10 | 3 | as planned |

**Narrow BP — back-off**: Without wraps the wrists felt a bit unsafe. Opening the elbows lets the wrists dump; tucking them to keep the wrists stacked then hurts.

**Low-bar SQ**: On the top set I focused on catching with hamstrings and glutes and went too slow, killing the bounce at the bottom. Corrected from the main set on. Bar position on the back didn't feel great today.

## Session recap

- Narrow BP: heavier than planned. Wrist vs elbow-width dilemma without wraps
- Low-bar SQ: caught with hamstrings and glutes but killed the bounce on the top set; fixed after. Bar position still a problem
- DB press: as planned

## What to try next

- Narrow BP: keep watching the wrists without wraps; look for a balance of elbow width and wrist angle
- Low-bar SQ: catch with hamstrings and glutes without killing the bounce. Check bar position next time

This is the log format. The menu file on the plan side (here, plans/2026-05/b2w2d3.md) uses almost the same table, with planned values in the weight and RPE columns. That is what makes comparing plan against actual easy. In the segment column, top, main, and back-off are the heaviest set of the day, the main working set, and the lighter sets that add volume. - marks “no value”: it appears in the segment column for accessory exercises where segments don’t apply, and in the RPE column for back-off sets that have no target RPE. An exercise I didn’t do keeps its row, with - for weight and reps and the reason in the note column. A missing row alone can’t tell “no record” from “didn’t do it.”

The note column holds short facts, such as a deviation from the plan. Form notes and coaching cues that don’t fit the table go right after it, labeled with the exercise name. The log ends with a session recap and a “what to try next” list: the day’s judgment and the handoff to the next session.

If the agent fills in blanks, neither adherence nor validity can be evaluated afterward, so CLAUDE.md says not to fill in guesses. It spells out that if the start and end times aren’t known during after-the-fact entry, the date stands alone, and if I don’t remember the felt RPE, the cell stays -.

“Strict format,” though, ultimately means Markdown headings and tables. If the next plan needs another column, the next plan adds it. There is no migration that rewrites past data; the agent reads across the per-plan format differences instead. As those differences accumulate, how to read them gets appended to the Skills and CLAUDE.md. At this scale, pushing that complexity to the read side is the cheaper deal.

Training results are shaped into the log format in conversation with the agent and committed as they are. One conversation takes a dictated or scribbled report, saves it under logs/, commits, and pushes. There is no dedicated ingestion system; Git history doubles as the record of input events. At the gym I open Claude Code on my phone and enter results during the rest between sets.

Write decisions back before the conversation ends

Even with the day’s observations and a list of what to try next, the log still doesn’t keep why the plan was what it was — the experimental intent. Plan validity can’t be evaluated later unless that is kept as a primary record too.

Plan design happens in conversation with the agent, but there is no guarantee that the next session inherits the alternatives we considered and rejected. Git diffs express only what changed. So the rule is: decisions and their reasons are written back into the notes before the conversation ends. Where they go is determined by the decision’s layer.

LayerExampleWhere it’s recorded
One-off deviation during a sessionDropped the weight just for todayThe log’s note column
Plan change after the plan startedSwapped an operating rule, rewrote future menus”Change Log” in plan.md
Judgment made while designing the planBlock structure, exercise selection, target numbers”Design Decisions” in plan.md
Decision about the systemChanges to Skills or conventions, including decisions not to changedocs/decisions/ (1 decision = 1 file)

“Considered it and decided not to change anything” also counts as one decision and gets recorded. It is primary data so that the next time the same question comes up, the reconsideration doesn’t start from zero. Decision files are append-only: overturning a past decision means writing a new file and cross-linking old and new.

Between humans, this sort of thing tends to get handed along informally through memory and conversation. In an operation with an agent whose dialogue resets every session, what isn’t written down might as well not exist. The flip side is that as long as it is written down, the next conversation starts from where the last deliberation left off.

Procedures become Skills

The operation is split by use case into Agent Skills (procedure documents that Claude Code loads for a given purpose). Conventions shared across use cases stay in CLAUDE.md; each Skill holds only the differences for its own use case.

There are five: plan design (design-cycle), the gym companion (live-training-session), after-the-fact batch entry (log-training-session), meet records (meet-record), and end-of-plan review (review-cycle). The companion and batch entry see daily use, and plan design was used to create the current plan. Meet records and the end-of-plan review haven’t run yet, because the first plan isn’t over. The review is wired so that its output becomes the input to the next plan’s design, but that connection has yet to fire for real. The way the Skills connect is designed to be the experiment loop itself.

What earns its place in the companion is less the steps than the constraints attached to each of them. I’m tired during the rest between sets, so it says: replies within a few lines, confirm only when a number is ambiguous, no explanation, no encouragement, no summarizing. Deviations from the plan, whether a changed weight or rep count, a swapped exercise, or a session cut short, are recorded as-is in the note column with the reason, never normalized, never deleted. That line is there to take effect at the moment of writing, so the agent that takes the report cannot round numbers toward the plan or summarize a deviation out of existence.

What changed from paper and spreadsheets

I haven’t been running it this way long enough to claim my competition results have improved. What has definitely changed is the effort planning takes, and how the records get used.

First, building a plan’s menus takes less time. A plan should reflect what the coach pointed out, what felt right in the previous plan, and what didn’t work then. All of that fades with time. With a paper notebook, recalling it meant paging back through the notebook, and that chore sat in front of every planning session. On top of that, programming by RPE means converting a target RPE and rep count into an actual working weight, exercise by exercise. Each conversion is small, but they stack up with the number of sessions. Now the coaching records and past logs sit in the same repository, and the agent reads them before proposing menus, so both costs dropped together.

Second, the records became searchable. When some body part starts hurting, or my form feels out of balance, I can check right away whether the same symptom has shown up before. If it has, following the logs shows what menus I was doing around then and what I was focusing on. Whether the cause lies in the training pace or in the form can then be inferred from the record rather than from memory. The paper notebook had the same information, but given the effort of an exhaustive manual search, in practice I never did it. The same search works mid-session. Ask the agent before a set and it tells me, from the coaching records and past logs, what to focus on in today’s training.

The agent is not, however, making the judgments. What I pull from it is only information grounded in the coach’s words and my own records; I don’t let it give general-purpose advice. Menu proposals, too, are required to be built from the approach my coach taught and from past logs, and the final call is mine.

And as these uses came into view, I started writing more into the notebook than I ever did on paper. Knowing that what you write will be pulled up later makes the cost of writing worth paying. More volume means more data to cross-reference, and the material for the next hypothesis feels thicker than it did on paper.

Closing

Looking back, once I decided to treat a training plan as an experimental design, the rough shape of this operation followed. Because it’s an experiment, I dropped the dedicated app with its fixed schema, so that the record can follow changes in what I want to record. Because it’s an experiment, deviations from the plan are recorded accurately, and adherence is evaluated separately from validity. And because it’s an experiment, decisions and their reasons are kept as primary records that feed the next plan’s design.

Where the AI agent went was decided along the same line. It went into the write boundary, where reports are shaped into the convention; into search, pulling up past logs and coaching records; and into drafting menu proposals from that material. It stayed out of guessing at blanks, normalizing deviations from the plan, ungrounded general advice, and the final decision. The agent sits on the side that protects the accuracy of the record, not on the side that chooses the hypothesis. That line, I think, carries over whatever the subject.

I haven’t discarded the option of a dedicated app. Once the record schema and procedures stabilize, I can think about building an app that fits the shape they settle into. In that sense, today’s setup is also a prototype of the lab notebook itself. Fix the schema once the exploration has settled. At least for now, that order seems right to me.

← Blog