cc9b83ef84
Retrofit per the playbook. Resolve both AGENTS.md TODOs with verified facts (make-target set; db:seed is live at image-build + local dev), reconcile the AI-provider count (4 files -> 5 registered providers), and extract the AI subsystem cheat-sheet into docs/guides/ai-subsystem.md, lazy-loaded via a .claude/rules symlink with an index line in AGENTS.md. All AGENTS.md commands verified green (tests 177/177, build, tsc, lint).
110 lines
7.8 KiB
Markdown
110 lines
7.8 KiB
Markdown
# 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; openai.ts exports both openai + openai-compatible = 5 registered providers)
|
||
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`. Targets come from `s9pk.mk` (the wrapper `Makefile` just sets `ARCHES := x86`):
|
||
|
||
- `make x86` — build the x86 s9pk.
|
||
- `make install` — sideload the newest local `.s9pk` to the StartOS box at `host:` in `~/.startos/config.yaml` (via `start-cli package install`).
|
||
- `make publish` — upload every `.s9pk` to the S3 bucket (`s9pk-s3base:`) and index it on `registry:` from `~/.startos/config.yaml` (via `s3cmd` + `start-cli s9pk publish`). **Distinct from `~/.proof-of-work/publish.sh`** below.
|
||
- `make clean` — remove build artifacts.
|
||
|
||
Both `install` and `publish` read host/registry config from `~/.startos/config.yaml`, which is **not in the repo** — verify against the live setup, not from a checkout.
|
||
|
||
Canonical publish path for this project: `~/.proof-of-work/publish.sh` (builds, uploads to FileBrowser, registers) — separate from the generic `make publish`. Unpublish: `~/.proof-of-work/unpublish.sh`.
|
||
|
||
`npm run db:seed` (= `tsx prisma/seed.ts`) seeds the `InstanceSettings` singleton + curated library; it is **live, not dead** — invoked at Docker image-build time (`start9/0.4/Dockerfile`) to bake the library into the image, and also the local-dev first-run path (`proof-of-work/README.md`). Runtime first-boot/upgrade seeding is handled separately by `docker_entrypoint.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`.
|
||
- Tests live in `proof-of-work/tests/`; mock server-action deps with `vi.hoisted()` + `vi.mock`.
|
||
- **Before editing the AI subsystem (`proof-of-work/lib/ai/**` or the generate/generations routes), read `docs/guides/ai-subsystem.md`** — provider abstraction, SSE/lenient-JSON, pricing/model menus, and the background-runner architecture live there.
|
||
|
||
## 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.
|
||
|
||
## 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).
|