From ef3d079ca21dbe4aa61c2e65f1dbabd3b4a04020 Mon Sep 17 00:00:00 2001 From: Keysat Date: Tue, 16 Jun 2026 14:07:03 -0500 Subject: [PATCH] Record first-class-set-metric convention + CSV round-trip backlog Capture the ~17-touchpoint recipe for promoting a set metric to a column (the watts precedent) so the next one doesn't need a repo-wide grep, and log the pre-existing CSV export/import header-name asymmetry as backlog. --- AGENTS.md | 1 + ROADMAP.md | 1 + 2 files changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index df8d700..cbf3606 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -72,6 +72,7 @@ Canonical publish path for this project: `~/.proof-of-work/publish.sh` (builds, - **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. +- **Adding a first-class numeric set metric** (precedent: `watts`, 1.2.0:4): mirror `calories` end-to-end — `schema.prisma` column + `prisma generate`; guarded additive `ALTER` in `docker_entrypoint.sh`; zod field + insert in all **5 set-write paths** (`workouts` POST, `workouts/[id]` PATCH, `workouts/[id]/sets`, `workouts/import/save`, `me/import`); `SetRow.tsx` (`show*`/state/`emitUpdate`/`buildSummary`/`firstField`/input) + `WorkoutForm.tsx` (set interfaces, `buildPayload`, `handleUpdateSet`, `initial*` prop, has-data checks); read summary in `app/main/workouts/[id]/page.tsx` + edit mapping in `app/main/workouts/new/page.tsx`; CSV export/parse + `page-csv` payload; field-option label lists (`lib/exerciseOptions.ts`, `app/main/exercises/[id]/page.tsx`, `ExercisePicker.tsx`). The `inputFields` token == the column name; the human label lives in those option lists (token `watts` → "Avg. watts"). `me/export` rides the 1:1 Prisma dump automatically. Add a round-trip test in `tests/routes-crud.test.ts`. - **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`. - **Authorization tiers**: whole-instance routes (`settings/{export,import}-db`) are **admin-only** (`!user.isAdmin → 403`); per-user data routes scope by `user.id`. Custom-URL AI providers (Ollama, OpenAI-compatible — anything with `requiresBaseUrl`) are **admin-only** (SSRF surface); fixed-URL cloud providers (claude/openai/gemini) stay per-user. Gate the server route AND hide the control in the UI. diff --git a/ROADMAP.md b/ROADMAP.md index 66f31bc..3323a28 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -25,6 +25,7 @@ Done in 1.1.0:9 (P2 batch): input-validation 500s → 400 (`lib/http.ts readJson - Adherence tracking: compare logged workouts against the planned `ProgramDay` (the `programDayId` link already exists). - Per-user export/import polish and scheduled backups. +- CSV export↔import round-trip: export writes `setX`-prefixed headers (`setCalories`/`setWatts`/`setNotes`) the importer doesn't read (it expects `calories`/`watts`/`notes`), so the app's own CSV export silently drops those on re-import (calories long-standing; watts since 1.2.0:4). Fix by aligning export header names with the parser, or adding the prefixed names as `knownColumns` aliases. (JSON account export/import round-trips fine.) - Charts/progress views over history (the data and 1RM estimates already exist). ## Hygiene