Files
proof-of-work/AGENTS.md
T
Keysat 29b9d2437c Add AGENTS.md, ROADMAP.md, and CLAUDE.md symlink
Onboarding doc for fresh agent sessions: stack, commands, layout,
conventions, and an Always/Never list of gotchas hit during the AI
overhaul. Current state section tracks the 1.1.0:7 checkpoint.
ROADMAP.md holds the longer-term backlog. CLAUDE.md symlinks AGENTS.md
so Claude Code loads it. Secrets kept out — private registry/file-host
URLs and creds referenced by file location, not value.
2026-06-12 20:02:27 -05:00

113 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AGENTS.md — Proof of Work
Self-hosted multi-user workout logger (Next.js app) packaged as a StartOS 0.4 `s9pk`, published to a private Start9 registry.
## Stack (versions that matter)
- **Next.js 14** (App Router, server components + server actions, SSE streaming)
- **React 18**, **TypeScript 5**, **TailwindCSS 3**
- **Prisma 5** ORM over **SQLite** (WAL mode; tuned at boot)
- **bcrypt** (native — NOT bcryptjs), **zod 3** for validation
- **Vitest 4** for tests
- **@start9labs/start-sdk** for the 0.4 packaging layer
- Node **>= 20** to build
## Layout (two projects in one repo)
```
proof-of-work/ ← the Next.js app (THIS is where you run npm)
app/ ← App Router routes; app/api/** = route handlers
app/main/ ← authed UI; navigation.tsx = sidebar
components/ ← React components (workouts/, ai/, settings/)
lib/ai/ ← AI subsystem (see below)
lib/ai/providers/ ← claude.ts openai.ts gemini.ts ollama.ts + index.ts (getProvider)
prisma/schema.prisma ← schema (mirror; real DB migrates via entrypoint ALTERs)
prisma/*.seed.json ← curated exercise library + AI templates (reconciled each boot)
tests/ ← Vitest specs (ai-*.test.ts, routes-*.test.ts, ...)
start9/0.4/ ← StartOS packaging wrapper
docker_entrypoint.sh ← boot: first-boot seed, additive ALTERs, library reconcile
Makefile / s9pk.mk ← s9pk build (ARCHES := x86)
startos/versions/ ← one file per ExVer version + index.ts (the version graph)
~/.proof-of-work/ ← publish.sh + unpublish.sh (NOT in repo; self-hosted registry)
```
`workout-planner/` is scratch (only `logs/`) — ignore. `start9/0.4/*.s9pk` are build artifacts.
## Commands
Run app commands **from `proof-of-work/`** (running tsc/vitest/next from repo root fails — wrong cwd):
```bash
cd proof-of-work
npm run dev # local dev server
npm run build # next build (run this to catch route/type errors before shipping)
npm run lint # next lint
npm test # vitest run (full suite)
npx vitest run tests/ai-pricing.test.ts # single file
npx vitest run -t "findPrice" # single test by name
npx tsc --noEmit # typecheck only
npx prisma generate # REQUIRED after editing schema.prisma (else TS can't see new fields)
```
Build/sideload the s9pk (from `start9/0.4/`): `make x86` then `make install`.
Publish to the registry: `~/.proof-of-work/publish.sh` (builds, uploads to FileBrowser, registers). Unpublish: `~/.proof-of-work/unpublish.sh`.
## Conventions
- **Versioning is ExVer**: `1.1.0:4` (note the colon). Every release = a new `start9/0.4/startos/versions/vMAJOR.MINOR.PATCH.N.ts` file, imported into `versions/index.ts` and promoted to `current` (previous `current` moves into `other[]`).
- **Bump the version BEFORE building the s9pk** — Start9 0.4 won't recognize a rebuild as an update otherwise.
- **Schema changes are additive ALTERs in `docker_entrypoint.sh`**, guarded by `PRAGMA table_info` checks. Keep `schema.prisma` in sync as the mirror, but the entrypoint is what migrates live `/data`. Never write a destructive migration.
- **Commit subject** = `vX.Y.Z:N — short summary`, imperative, body explains the *why*.
- **Git remote is self-hosted** (a private Start9 registry + a FileBrowser artifact host), NOT GitHub. The actual registry/file-host URLs are constants in `~/.proof-of-work/{publish,unpublish}.sh`; FileBrowser creds live in `~/.keysat/filebrowser.env` (outside the repo, gitignored). Default branch is `master`.
- AI provider abstraction: each provider yields an async iterable of `GenerateChunk` (`text` / `usage` / `done` / `error`); add new ones under `lib/ai/providers/` and register in `index.ts`.
- Streaming AI uses SSE; partial JSON is recovered with `lib/ai/lenientJson.ts`.
- Tests live in `proof-of-work/tests/`; mock server-action deps with `vi.hoisted()` + `vi.mock`.
- Pricing/model menus live in `lib/ai/pricing.ts` (`PRICES`, `MODEL_MENU`) — keep them paired so every menu model has a price entry (there's a test enforcing this).
## Always
- Run `npx prisma generate` after any `schema.prisma` edit, then `npx tsc --noEmit`.
- Run `npm test` AND `npm run build` before shipping a version.
- Add the boot-time `ALTER TABLE` (with an existence guard) for any new column, in `docker_entrypoint.sh`.
- Treat API keys / secrets as plaintext in `/data` BY DESIGN (threat model: the operator owns `/data`). Reference env-var names (`DATABASE_URL`, etc.); never hardcode values.
- Keep migrations idempotent and additive; data already on a user's server must survive upgrades.
- Verify the published file actually changed (size / 404 / Last-Modified) after publish.sh.
## Never
- **Never add `Co-Authored-By` / "Generated with" trailers** to commits — the user authors commits solo. (This was done wrong in earlier commits; do not repeat.)
- **Never reintroduce nonce-based CSP** — it broke first paint. Use the static `'unsafe-inline'` CSP in `next.config.js`.
- **Never run app commands from the repo root** — always `cd proof-of-work` first.
- **Never export non-HTTP-method symbols from a `route.ts`** — Next.js rejects the build (helpers go in `lib/`, e.g. `lib/ai/activateConfig.ts`).
- **Never commit `app.db`, `*.bak`, or any user data** — they're gitignored; double-check `git status` before `git add`.
- **Never click Uninstall on a StartOS package during a data cutover** — it destroys the volume; use Stop.
- **Never assume GitHub** — don't add a GitHub remote or push there.
## AI subsystem cheat-sheet
- `generate/route.ts` kicks off a **detached background runner** (`generationRunner.ts`) and returns an id; the client attaches via SSE (`generations/[id]/stream`) and can also poll the row. Navigating away does NOT cancel generation.
- System prompt = `systemPromptBase.ts` (output contract: JSON-only, library `exerciseId`s only, suggested weights) + the template's coaching prompt + `PROGRAM_OUTPUT_SHAPE` + library + optional history block (`historyContext.ts`).
- Multi-config: `AIConfigProfile` rows per user; `UserPreferences.activeAIConfigId` points at the active one and is mirrored into the legacy `ai*` columns for back-compat.
## TODO (verify before relying on)
- Exact `make` target set beyond `x86` (Makefile sets `ARCHES := x86`; `make install` sideloads — confirm host config).
- Whether `npm run db:seed` is needed in any current workflow (entrypoint handles seeding on the server).
## Current state
Latest version is **1.1.0:7** (built locally, installed on the StartOS server). The registry is currently **empty** — all versions were unpublished; nothing is downloadable until `publish.sh` runs again.
Working: workout logging, programs (manual + AI), multi-user, curated library, full AI subsystem (5 providers, multi-config, background generation, history detail, cost/duration, Ollama auto-detect, infinite-scroll exercise history).
In progress: none — repo is at a clean checkpoint.
Decided but not implemented: tiered AI prompt formatting — JSON-Schema enforcement (Ollama `format` / OpenAI `response_format`), pipe-separated library, XML-tagged sections, Ollama-only few-shot. Targets local-model output quality; would ship as 1.1.0:8.
Known issues: earlier commits (`8f149d3``5b0535f`) carry `Co-Authored-By` trailers to scrub from history. publish.sh Step 3 (registry register) silently no-op'd on 1.1.0:6 and :7 — uploaded the file but didn't register; investigate before relying on those versions appearing in the registry.
Next steps:
1. Scrub `Co-Authored-By` trailers from history (filter-branch or rebase).
2. Re-publish current version once the Step-3 registry-register failure is diagnosed.
3. Implement the tiered AI prompt formatting (1.1.0:8).