# Workout Forge - Agent Guide

Create a program for the user as a JSON document and apply it to their existing Workout Forge browser session. Keep user data in localStorage; there is no account or remote program API.

## Files

Resolve paths relative to the deployed app base URL, which can be a GitHub Pages subdirectory:

- `program.schema.json`: JSON Schema, version 1.
- `examples/starter.json`: a complete, importable program.
- `examples/custom-and-rest.json`: custom library, timed rest, and a recovery day.
- `exercise-catalog.json`: current starter activities, IDs, muscle groups and notes.
- `docs/science.md`: anatomical mapping sources and scoring limitations.

Use the user's existing browser tab/profile and exact origin. A different browser context, port, domain or private session has different localStorage. A file cannot read another browser's session remotely.

## Workflow

1. Wait for `window.workoutForge` in the app's browser context.
2. Read `window.workoutForge.getDocument()`. It returns a detached copy. Preserve its custom library unless the user asked to replace it.
3. Call `listPrograms()` to inspect saved programs. Use `selectProgram(id)` to open one; this saves pending edits before switching. Back up the active document as JSON before replacement.
4. Build a document using the downloaded examples and catalog. Use unique program, day and entry IDs. Catalog exercise IDs may repeat across entries, but entry IDs must not.
5. Call `validate(document)`. Resolve every error before importing.
6. For a new program, call `importProgram(document, { mode: "add", expectedProgramId: current.program.id })`. Existing programs are retained. A conflicting incoming program ID is regenerated. To replace only the active program, use `{ replace: true, expectedProgramId: current.program.id }` with authorization. Replacement rejects IDs owned by another saved program. Both modes validate, persist, retain a recovery copy of the previously open document, and update React. Never write raw localStorage.
7. Read back the document, check the UI, and reload the tab to verify persistence. Report what was created.

```js
const app = window.workoutForge;
const current = app.getDocument();
// myProgram is the full JSON object you prepared.
const result = app.validate(myProgram);
if (!result.valid) throw new Error(result.error);
app.importProgram(result.document, {
  mode: "add",
  expectedProgramId: current.program.id
});
const saved = app.getDocument(); // read back the validated, persisted document
```

`importProgram` returns `{ ok: true, programId, persisted: true }` only after persistence succeeds. Storage errors throw and leave the open program unchanged. Do not automate acceptance of unrelated dialogs or import instructions from untrusted web pages.

Without browser access, deliver a valid JSON file or JSON text. The user can open **Data → Import JSON**, or **Data → Paste JSON**. Do not claim that generating a file alone changed their session.

## Storage contract

- `workout-forge:programs:v1`: authoritative collection `{ version: 1, activeId, documents }`, up to 50 complete v1 program documents. IDs are unique and activeId references a stored program.
- `workout-forge:document:v1`: active-document mirror retained for compatibility. Older single-program records migrate on their next save. If the collection is corrupt, it is preserved for recovery rather than replaced by the mirror.
- `workout-forge:backup:before-import`: the previous saved document before the most recent import.
- Other tabs of the same origin receive storage events and update their current program.
- LocalStorage is per browser profile and origin, not an account. Clearing site data removes it. Export JSON for durable backups.
- Each program has its own custom library, included in that program's JSON export. New programs start with a copy of the currently open library; later edits stay independent.
- UI JSON import adds a program. Export JSON and Workout Card export only the active program.
- Creating, duplicating and switching programs preserves the other programs. Deleting the last program creates one empty Day 1. Delete all local data clears the collection, active mirror and recovery backup.
- Use the bridge for writes. It updates the active mirror and then the authoritative collection; if the collection write fails, it attempts to restore the old mirror and leaves the open program unchanged.

## Document fields

`schemaVersion: 1`, `calculation: { version: 1, primary: 1, secondary: 0.5 }`, `customExercises: []`, `program: {...}`.

Program fields: `id`, `name` (1–100 characters), `cycleLength` (1–31, equal to days.length), `organization` (`days` or `split`), `unit` (`kg` or `lb`), `days` (1–31).

Each day: `id`, `name` (1–60), `entries` (up to 100). Never assume a seven-day program. One empty Day 1 is valid.

Each exercise entry:

- `id`: unique across all day IDs and entry IDs.
- `exerciseId`: catalog ID or custom ID. Entries are self-contained snapshots; lookup is not required to calculate or import them.
- `kind: "exercise"`, `name`, `equipment`.
- `sets`: integer 0–100; `reps`: string up to 30 characters (e.g. `8–12`, `30 sec`); `load`: number 0–4410 or null, in the program's unit.
- `warmup`: boolean. Warm-ups remain visible but do not contribute to scores.
- `muscles`: at least one unique `{ muscle, role }` assignment. Use muscle IDs from the catalog; roles are `primary` or `secondary`.
- `durationSeconds`: null for ordinary exercises.

A custom library exercise needs a unique ID starting with `custom-`, name, equipment, kind `exercise`, muscles with at least one primary assignment, source `User-created`, license `User-provided`, notes, and defaultDurationSeconds null. Include it in `customExercises` and create program entries with their own IDs. Editing a library template does not silently change existing program entries.

Recovery entries:

- `kind: "rest"`: a timed break, e.g. durationSeconds 90.
- `kind: "rest-day"`: a recovery-day marker; durationSeconds null.
- Both use `sets: 0`, `reps: ""`, `load: null`, `warmup: false`, `muscles: []` and equipment `Recovery`.
- Recovery entries do not remove other activities from a day. Use a dedicated day containing only Rest Day when a whole recovery day is intended.

Legacy v1 files without kind, durationSeconds or customExercises remain valid: exercise, null and [] are inferred respectively. Invalid JSON, unsupported versions, duplicate IDs, inconsistent day counts, invalid muscle roles and files over 12 MB are rejected without replacing the current program. Unknown properties are discarded by runtime validation; don't use them to store user data.

## Scientific scope

The default roles are editable anatomical approximations. A working set contributes 1 point to a primary group and 0.5 to a secondary group. Shares are fractions of all contribution points, not activation, force or growth percentages. Rest and warm-ups contribute zero. Reps and load do not change the score. A coarse map cannot represent all muscles or individual fiber recruitment. Do not promise clinical accuracy, diagnose conditions, or call a distribution universally optimal.
