diff --git a/.gitignore b/.gitignore index ae97dcc52..7a7417733 100644 --- a/.gitignore +++ b/.gitignore @@ -196,7 +196,7 @@ docs/superpowers/ # being built and reviewed, then go stale fast (PIDs, bench state, host facts) # while still reading as authoritative because they are checked in. Durable # conclusions belong in CLAUDE.md, an ADR or a code comment; the narrative and -# evidence belong on the ticket. Named to match ble-mcp-test. +# evidence belong on the ticket. docs/notes/ docs/investigations/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..bd94ac2f1 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,25 @@ +# AGENTS.md — trakrf working agreements + +Identical in `docs`, `platform` and `infra` — change it in all three or none. +These repos are public. Add only what a fresh session gets wrong otherwise. +Branches, commits, merges: `CONTRIBUTING.md`. Tooling: `.claude/csw.json`. + +## Specs, plans and notes are never tracked + +Per-session artifacts, disposable by decision. Ignored: `superpowers/`, +`docs/superpowers/`, `.superpowers/`, `docs/notes/` — each repo's `CLAUDE.md` +names which to write to. A plan worth a reviewer's time goes in the PR body. + +**Re-used, or merely re-read?** Re-used lives in the repo under a _behaviour_ +name — `describe` blocks, log prefixes and artifact dirs included. Merely +re-read belongs on the ticket, with the narrative and evidence. Durable +conclusions go to `CLAUDE.md`, an ADR or a code comment. Keep ticket ids out of +published prose. + +## Working rules + +- Ask when requirements are unclear; never delete code without instruction +- Verify before claiming done: run it, read the output, report what it said +- Verify the premise, not just the citation +- Search wide, then filter — absence from a narrowed search is not absence +- No invented imports; keep files under 500 lines diff --git a/CLAUDE.md b/CLAUDE.md index f6f0c12c6..283283b6a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,51 +1,26 @@ # CLAUDE.md -## Package Managers -- **Backend**: `go mod`, `go get` -- **Frontend**: `pnpm` exclusively (`pnpm dlx` instead of `npx`) +@AGENTS.md -## Task Runner (Just) -- **Run everything from the project root** — delegate with `just ` rather than `cd`-ing in -- Workspaces: `frontend`, `backend`, `cli`, `database` (aliases `fe`, `be`, `db`) -- Combined across all four: `just lint`, `just test`, `just build`, `just validate` -- Every workspace justfile sets `fallback := true`, so root recipes still resolve from inside one - -## Cluster Ops -- `just ops [args]` forwards to the trakrf/infra justfile; bare `just ops` lists what's available -- Shortcuts: `just gcp-auth`, `just psql preview`, `just logs prod 1h` -- Cluster/namespace/pod knowledge stays in infra — never reimplement a kubectl incantation here -- Infra checkout is `TRAKRF_INFRA_DIR`, else a sibling `infra/`; set it in `.env.local` if yours is elsewhere - -## Git Workflow -- **Never push directly to main** — all changes via PR -- **Never squash merge** — `gh pr merge --merge` -- Branch naming: `/tra-NNNN-slug`, e.g. `feat/tra-1065-kitting-capability`, `fix/broken-xyz`, `chore/...`, `docs/...` -- Conventional commits: `feat:`, `fix:`, `docs:`, `chore:` -- Prefer incremental commits over amending - -## Migrations -- Adding a migration **requires** `just backend migrate-checksums` — CI fails without it, and applied migrations are immutable - -## Testing -- Playwright e2e **never runs in CI** — green CI does not mean e2e passes; run it against preview yourself -- **Name specs and tooling for what they do, never for a ticket** — `locate-mask-length-variants.spec.ts`, not `tra-1120-…`. Cite the ticket in the file header. Applies to `describe` blocks, log prefixes and artifact dirs, since those appear in output -- **Re-used vs merely re-read** — reusable things live in the repo under a behaviour name; point-in-time records go on the ticket, and working notes in `docs/notes/` stay untracked (see `.gitignore`) - -## Hardware access -- **`ble-mcp-test` is test tooling only, never the product path** — the app reaches a CS108 solely via browser `navigator.bluetooth` -- **One connection at a time**: a *connected client* blocks preview/prod hand-testing — a running bridge daemon does not. It holds the port, not the radio -- **An idle bridge port does *not* mean the reader is free** — a browser tab holding it appears in no process list. `get_connection_state` answers it in one call; see `docs/ble-hardware-access.md` -- The bridge is a supervised `systemctl --user` unit, so `pkill` is the wrong tool — it comes back in 5s - -## Preview Deployments -- Opening/updating a PR auto-deploys to `https://app.preview.trakrf.id` -- See `.github/workflows/sync-preview.yml` for details +Platform specifics only. Session artifacts go to `docs/superpowers/` and `docs/notes/`. ## Stack -- Go backend, React/TypeScript frontend, TimescaleDB, Go CLI, MQTT ingestion (`mqtt-rpc/`) -- Architecture context: `README.md`, `docs/architecture-decisions.md`, `docs/adr/`, `docs/logical-schema.md` - -## Worktrees -- Git worktrees live in `.claude/worktrees/` (gitignored) — where the native `EnterWorktree` tool writes -- **`just bootstrap` first, backgrounded** — generates the gitignored `go:embed` targets; without it `just validate` fails with `pattern frontend/dist: no matching files found`. Idempotent, near-instant warm -- **An unbootstrapped tree invalidates verifications** — everything fails, so a deliberate-break check proves nothing. Confirm bootstrap ran before believing a failure +Go backend · React/TypeScript frontend · TimescaleDB · Go CLI · MQTT ingestion (`mqtt-rpc/`). +Architecture: `README.md`, `docs/architecture-decisions.md`, `docs/adr/`, `docs/logical-schema.md`. + +## Tooling +- Backend `go mod`; frontend `pnpm` only — `pnpm dlx`, never `npx` +- Run `just` from the project root: `just `, workspaces `frontend` `backend` `cli` `database` (`fe` `be` `db`) +- `just ops ` forwards to trakrf/infra and bare `just ops` lists them; cluster knowledge stays there. Checkout via `TRAKRF_INFRA_DIR`, else sibling `infra/` + +## Gotchas +- A new migration requires `just backend migrate-checksums`; applied migrations are immutable +- Playwright e2e never runs in CI — run it against preview yourself +- `just bootstrap` a fresh worktree before validating, or the `go:embed` targets are missing and every check fails for that reason alone +- Opening a PR auto-deploys to `https://app.preview.trakrf.id` (`.github/workflows/sync-preview.yml`) + +## Hardware +- `ble-mcp-test` is test tooling only; the app reaches a CS108 via browser `navigator.bluetooth` +- One connection at a time: a connected client blocks hand-testing, a running daemon does not +- An idle bridge port does not mean the reader is free — `get_connection_state` answers it; see `docs/ble-hardware-access.md` +- The bridge is a supervised `systemctl --user` unit; `pkill` returns in 5s diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e363a9318..24364bc76 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,12 +42,16 @@ cd ../frontend && pnpm test ### 1. Create a Branch ```bash -# Branch naming: -# - feature/add-xyz (new features) +# Branch naming: / +# - feat/add-xyz (new features) # - fix/broken-xyz (bug fixes) # - docs/update-xyz (documentation) +# - chore/tidy-xyz (everything else) +# +# Maintainers working a tracked issue use /-, +# e.g. feat/tra-1065-kitting-capability. -git checkout -b feature/add-asset-history +git checkout -b feat/add-asset-history ``` ### 2. Write Your Code @@ -144,7 +148,7 @@ cd tests/api && pnpm test 1. **Push to your fork:** ```bash - git push origin feature/add-asset-history + git push origin feat/add-asset-history ``` 2. **Open a Pull Request:** @@ -160,6 +164,10 @@ cd tests/api && pnpm test - [ ] API documentation updated - [ ] Commit messages use conventional format +4. **How changes land:** + - Every change goes through a PR. Nothing is pushed directly to `main`. + - PRs are merged with `gh pr merge --merge` — never squash, never rebase. + ## Common Tasks ### Adding a New API Endpoint @@ -243,8 +251,8 @@ release. Do not couple them. ### Conventional Commits -Optional. The git-log readability convention (`feat:`, `fix:`, `chore:`, -`docs:`) is encouraged but no tool depends on it — bumps are manual. +Required. Use `feat:`, `fix:`, `docs:` or `chore:`. No tool enforces it and +version bumps are manual, but the convention is not optional. ## Getting Help diff --git a/backend/internal/util/httputil/auth_error.go b/backend/internal/util/httputil/auth_error.go index 44f7e0014..51306a1b8 100644 --- a/backend/internal/util/httputil/auth_error.go +++ b/backend/internal/util/httputil/auth_error.go @@ -13,8 +13,7 @@ const AuthRealm = "trakrf-api" // in public and internal handlers should route through this helper so the // envelope, WWW-Authenticate header, and title are consistent. // -// detail is caller-supplied from a short canonical set — see -// docs/superpowers/specs/2026-04-20-tra-407-contract-bugs-design.md. +// detail is caller-supplied from a short canonical set — see TRA-407. func Respond401(w http.ResponseWriter, r *http.Request, detail, requestID string) { w.Header().Set("WWW-Authenticate", `Bearer realm="`+AuthRealm+`"`) WriteJSONError(w, r, http.StatusUnauthorized, apierrors.ErrUnauthorized, diff --git a/deploy/edge/README.md b/deploy/edge/README.md index 98ea38c6a..0d6bc12b7 100644 --- a/deploy/edge/README.md +++ b/deploy/edge/README.md @@ -2,8 +2,7 @@ Rootless Podman quadlets for the offline demo box (`trakrf-demo`). Hosts the backend + Timescale + Mosquitto + a Traefik TLS edge, all systemd-managed. -Design specs: `docs/superpowers/specs/2026-06-07-deploy-edge-design.md`, -`docs/superpowers/specs/2026-06-13-srv-trakrf-runtime-layout-design.md` (TRA-988). +Design context: TRA-988. Tim drives the demo from his laptop at **`https://app.demo.trakrf.id`** over the Slate WiFi. Break-glass = a shell over the tailnet (`systemctl --user`, diff --git a/frontend/src/worker/cs108/barcode/accumulator.ts b/frontend/src/worker/cs108/barcode/accumulator.ts index 8bea3c946..78ab9339c 100644 --- a/frontend/src/worker/cs108/barcode/accumulator.ts +++ b/frontend/src/worker/cs108/barcode/accumulator.ts @@ -6,8 +6,8 @@ * terminated by 0x0D (CR). A single payload may contain multiple complete * records, or a record may span multiple payloads. * - * See `docs/superpowers/specs/2026-05-23-tra-821-barcode-fragment-reassembly-design.md` - * for the empirical firmware behaviors this class handles. + * The empirical firmware behaviors this class handles were characterised in + * TRA-821. */ const RECORD_TERMINATOR = 0x0D; diff --git a/load-tests/ingest/README.md b/load-tests/ingest/README.md index aebf0d24c..cc90c65e2 100644 --- a/load-tests/ingest/README.md +++ b/load-tests/ingest/README.md @@ -8,8 +8,7 @@ stack to characterize the ingestion path and tune the reader dedup window. The **reader's MQTT publish path is the bottleneck**, not the backend. At `dedup=0` a CS463 wedges its own publish in ~4 min (publish-buffer exhaustion); the single-replica backend consumer handled everything one reader could emit -(~270 reads/s) with zero drops. See -`docs/superpowers/specs/2026-06-16-cs463-ingest-load-and-dedup-design.md`. +(~270 reads/s) with zero drops. ## Golden config (validated, live on cs463-212 → preview)