Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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/

Expand Down
25 changes: 25 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -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
67 changes: 21 additions & 46 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -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 <workspace> <cmd>` 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 <recipe> [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: `<type>/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 <workspace> <cmd>`, workspaces `frontend` `backend` `cli` `database` (`fe` `be` `db`)
- `just ops <recipe>` 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
20 changes: 14 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,16 @@ cd ../frontend && pnpm test

### 1. Create a Branch
```bash
# Branch naming:
# - feature/add-xyz (new features)
# Branch naming: <type>/<slug>
# - 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 <type>/<ticket>-<slug>,
# 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
Expand Down Expand Up @@ -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:**
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down
3 changes: 1 addition & 2 deletions backend/internal/util/httputil/auth_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 1 addition & 2 deletions deploy/edge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`,
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/worker/cs108/barcode/accumulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 1 addition & 2 deletions load-tests/ingest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Loading