diff --git a/AGENTS.md b/AGENTS.md index 11ab2ac..e915bb5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -116,13 +116,16 @@ Canonical publish path for this project: `~/.proof-of-work/publish.sh` (builds, ## Current state -Latest version is **1.2.0:8** — **decimal-tolerant AI parsing**, a fix-forward on the 1.2.0:7 SparkControl ship. **SparkControl is confirmed working end-to-end on-box** (smoke-tested with `RedHatAI/Qwen3.6-35B-A3B-NVFP4`: connected, streamed 9.7k in / 5.3k out, `$0`/FREE, model auto-detected). The smoke test surfaced one bug: local models emit decimals where the AI schema expected integers (a half-step `"rpe": 7.5`), and zod `.int()` failed the **entire** parse. Fix: a shared `looseInt` (`programSchema.ts`, used by `workoutSchema.ts`) rounds a number to the nearest int **before** the `.int()` check, applied to every integer field in both the program and single-workout schemas (rpe, reps, sets, gear, order, durationSeconds, rest/week/day numbers). Parse-only, types unchanged. +Latest version is **1.2.0:9** — the **SparkControl + local-model arc is complete and confirmed working on-box** (smoke-tested with `RedHatAI/Qwen3.6-35B-A3B-NVFP4`: connected, model auto-detected, `$0`/FREE). Three ships: +- **:7** added **SparkControl (local)**, a 6th AI provider — the operator's own self-hosted local-inference gateway. OpenAI-compatible (reuses `generateOpenAIStyle`), **keyless** (`requireApiKey:false` → no `Authorization` header), reached over the **internal same-box address** `http://spark-control.startos:9999/v1` (plain HTTP, no TLS/cert-skip), model auto-detected via `/api/endpoints` (`app/api/ai/sparkcontrol/model`, admin-only + SSRF-guarded). Plus a **base-URL footgun fix**: a custom URL could attach to a fixed-URL provider (claude/openai/gemini) and be silently ignored — both config write paths now null `baseUrl` for non-custom-URL providers + the form clears it on provider change. +- **:8** made AI parsing **decimal-tolerant** — `looseInt` (`programSchema.ts`, used by `workoutSchema.ts`) rounds a float (e.g. a half-step `rpe:7.5`) before the zod `.int()` check, on every integer field in both schemas. A local model had failed the whole parse on one decimal. +- **:9** added **fuzzy exercise→library matching** (`lib/ai/exerciseMatch.ts`): when the model's `exerciseId` misses, normalize the name (strip `(barbell)` etc.) and auto-map **unique confident** matches in both generate flows (`Overhead Press` → `Overhead Press (barbell)`); ambiguous names stay manual. -**1.2.0:7 (the SparkControl feature itself):** adds a 6th provider, **SparkControl (local)** — the operator's own self-hosted local-inference gateway. OpenAI-compatible wire format (reuses `generateOpenAIStyle`), **keyless** on the LAN (`requireApiKey:false` → no `Authorization` header), reached over the **internal same-box StartOS address** `http://spark-control.startos:9999/v1` (plain HTTP — no TLS/cert-skip; the public LAN interface is HTTPS w/ a self-signed cert we deliberately avoid). The Settings form **auto-detects the loaded vLLM model** via SparkControl's `/api/endpoints` (`app/api/ai/sparkcontrol/model`, admin-only + SSRF-guarded); $0 in the cost UI. Also fixed a **base-URL footgun**: a custom URL could ride along to a fixed-URL provider (claude/openai/gemini), get stored, and be silently ignored — both config write paths now null `baseUrl` for non-custom-URL providers and the form clears it on provider change. **No schema/data change** (`AIConfigProfile.provider` is free-text). Details: `docs/guides/ai-subsystem.md` → Provider abstraction (incl. the "adding a provider" fan-out checklist). **Built + sideloaded** (`immense-voyage.local`, 2026-06-19, `master`) as `proof-of-work_x86_64.s9pk` (80M). tsc clean (app + packaging), lint clean (pre-existing only), **261 tests pass**, `next build` + s9pk build succeed. Registry empty, **publishing parked** (sideload-only via `make install`). +Whole arc is **client/parse-only — no schema/data change** (`AIConfigProfile.provider` is free-text). Mechanics in `docs/guides/ai-subsystem.md` (provider abstraction + "adding a provider" fan-out checklist). **Built + sideloaded** (`immense-voyage.local`, 2026-06-19, `master`) as `proof-of-work_x86_64.s9pk` (80M); tsc clean (app + packaging), lint clean (pre-existing only), **274 tests pass**, `next build` + s9pk build succeed. Registry empty, **publishing parked** (sideload-only via `make install`). **Design contract established this session (2026-06-19, committed `7fda9ce`, no UI code changed):** the `design/` folder now holds the durable contract — `DESIGN.md` (9-section brief) + `tokens.tokens.json` (DTCG) + `brand/palette.css` + `inspiration/` provenance. From a Case-B *document-as-is* extract of the as-built dark UI: **monochrome gym-brutalist** (`#0A0A0A` canvas, zinc-only neutral, white primary button, Bebas-uppercase/tracked headings, flat border-based depth), plus two owner calls — **red elevated to the single brand accent `#DC2626`** and a **two-tier radius** (4px controls / 8px containers). AGENTS.md carries the read-before-UI Design line; `ROADMAP.md` → **Design** holds the `design-checker` cleanup backlog (gray→zinc, green→emerald, yellow→amber, `rounded-md`→`rounded`, overlay-only shadows, and a shared `