|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +This file (`AGENTS.md`) is the canonical agent configuration. `CLAUDE.md` is a symlink to this file. |
| 4 | + |
| 5 | +Browser-based control app for a GRBL-style pen plotter (a UUNA TEK 3.0 with an A0 |
| 6 | +bed). A long-running gateway daemon owns the serial port and streams plots |
| 7 | +autonomously; the browser is a thin WebSocket client. See [README.md](README.md) |
| 8 | +for what it does and how to run it. |
| 9 | + |
| 10 | +## Safety — this code drives a physical machine |
| 11 | + |
| 12 | +These are not style preferences. Getting them wrong wastes a sheet of paper, or |
| 13 | +drives the gantry into the frame. |
| 14 | + |
| 15 | +**Never interrupt a running plot.** A plot is a one-shot physical job and there is |
| 16 | +**no resume** — a client cannot re-attach to a plot it did not start. While the |
| 17 | +machine is moving (the state file's mtime is within ~3 s), do not: |
| 18 | + |
| 19 | +- `systemctl restart plotter-gateway`, run `deploy.sh`, or install an update — restarting the daemon aborts the plot. |
| 20 | +- Open a WebSocket to the gateway. When no client holds control, the next client to connect inherits it. |
| 21 | +- Suggest pressing **Plot**. `streamProgram` has no in-progress guard, so a second program interleaves into the running queue. |
| 22 | + |
| 23 | +To check progress without touching the plot, read |
| 24 | +`/var/lib/penplotter271/.plotter-state.json` over SSH: `wpos` is the live work |
| 25 | +position, `z: 0` is pen-down (drawing), `z: 2` is pen-up (travel). That connects |
| 26 | +no client and transfers no control. Defer every fix and restart until the plot |
| 27 | +finishes. |
| 28 | + |
| 29 | +**No limit switches** (`$22=0`, homing disabled). There is no `$H`. The operator |
| 30 | +sets work zero by hand at the paper's top-left corner each session. After any |
| 31 | +power cycle the restored origin can be ~1 cm off — and if it is wrong, nothing |
| 32 | +stops the machine. |
| 33 | + |
| 34 | +**Machine conventions**, baked into the G-code generator: |
| 35 | + |
| 36 | +- **Inverted Z:** `Z+` moves the pen **down**. Pen-down Z is positive (default `3`), pen-up is `0`. |
| 37 | +- **Origin = the paper's top-left corner**, and the SVG→G-code mapping is **identity — no Y flip**. Machine `+Y` runs physically *down* the page. Drawing fills the `+X`/`+Y` quadrant. |
| 38 | +- **The daemon opens the serial port exactly once.** Repeated reopen wedges the macOS CH340 driver (errno 22) and only a physical replug recovers it. Never add a reopen path. |
| 39 | + |
| 40 | +## Architecture |
| 41 | + |
| 42 | +The GRBL engine depends only on a `Transport` interface — never on Web Serial, the |
| 43 | +DOM, or React — so the same engine runs on the Pi behind a Node serial adapter. |
| 44 | +Keep that seam intact. |
| 45 | + |
| 46 | +``` |
| 47 | +src/grbl/ Portable GRBL protocol engine: streaming, status, alarms (no UI deps) |
| 48 | +src/transport/ The seam — Transport interface + the browser's WebSocket client |
| 49 | +src/gateway/ Shared WebSocket protocol (commands, snapshot, forwarded events) |
| 50 | +src/plot/ Pure pipeline: SVG/PNG → polylines → placement → G-code |
| 51 | +src/ui/ React app (the only DOM-aware layer) |
| 52 | +gateway/ Raspberry Pi / dev daemon |
| 53 | +``` |
| 54 | + |
| 55 | +`src/plot/` and `src/grbl/` are the pure, unit-tested core and the only code the |
| 56 | +coverage floor measures. New logic belongs there rather than in `src/ui/` wherever |
| 57 | +that is a real choice. |
| 58 | + |
| 59 | +## Development commands |
| 60 | + |
| 61 | +Use `mise`. It pins the toolchain — including Node `22.20.0`, the same version |
| 62 | +`packaging/assemble.sh` bundles into the `.deb` — and CI runs the same tasks, so |
| 63 | +local and CI cannot drift. |
| 64 | + |
| 65 | +```bash |
| 66 | +mise install # install the pinned toolchain |
| 67 | +mise run install # npm ci |
| 68 | +mise run ci # the full gate: format-check, both typechecks, test, build |
| 69 | +``` |
| 70 | + |
| 71 | +| Task | What it does | |
| 72 | +| --- | --- | |
| 73 | +| `mise run dev` | Vite dev server for UI work on :5173 | |
| 74 | +| `mise run gateway` | Run the plotter gateway daemon on :8717 | |
| 75 | +| `mise run build` | Typecheck + build the GUI into `dist/` | |
| 76 | +| `mise run test` | Unit tests, enforcing the coverage floor | |
| 77 | +| `mise run typecheck` | Type-check the browser sources | |
| 78 | +| `mise run typecheck-node` | Type-check the gateway sources | |
| 79 | +| `mise run format` / `format-check` | Prettier write / check | |
| 80 | +| `mise run audit` | Security-audit the workflows + dependabot config (zizmor) | |
| 81 | +| `mise run lint-actions` | Lint the workflows (actionlint) | |
| 82 | +| `mise run ci-watch` | Watch the GitHub Actions run for the current branch | |
| 83 | + |
| 84 | +Coverage is measured over `src/plot` and `src/grbl` only, with per-metric floors in |
| 85 | +`vite.config.ts`. Raise them as coverage improves; never lower one to make CI pass. |
| 86 | + |
| 87 | +## Spec-driven changes |
| 88 | + |
| 89 | +Non-trivial work goes through OpenSpec: proposals and tasks under |
| 90 | +`openspec/changes/`, capability specs under `openspec/specs/`, completed changes in |
| 91 | +`openspec/changes/archive/`. Use the `/opsx:*` skills (`propose`, `apply`, `archive`, |
| 92 | +`sync`, `explore`). There are currently no active changes. |
| 93 | + |
| 94 | +Hardware-dependent tasks are **not** done when the code typechecks. Several |
| 95 | +position-restore bugs passed review and failed on the actual Pi. Leave hardware |
| 96 | +verification tasks unchecked until the operator confirms them on the machine. |
| 97 | + |
| 98 | +## Agent skills |
| 99 | + |
| 100 | +### Git remote |
| 101 | + |
| 102 | +GitHub, via the `gh` CLI. The repo is `LAB271/labs-pen-plotter`. |
| 103 | + |
| 104 | +**The maintainer handles all git operations themselves.** Do not commit, push, or |
| 105 | +open PRs unless explicitly asked in that instance. |
| 106 | + |
| 107 | +### Issue tracker |
| 108 | + |
| 109 | +GitHub Issues, via `gh`. See [`docs/agents/issue-tracker.md`](docs/agents/issue-tracker.md). |
| 110 | + |
| 111 | +### Triage labels |
| 112 | + |
| 113 | +`needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, `wontfix`. See |
| 114 | +[`docs/agents/issue-tracker.md`](docs/agents/issue-tracker.md). |
| 115 | + |
| 116 | +### Commits |
| 117 | + |
| 118 | +[Conventional Commits](https://conventionalcommits.org/) — see |
| 119 | +[CONTRIBUTING.md](CONTRIBUTING.md). Explain *why*, not *what*: this codebase carries |
| 120 | +unusually detailed inline rationale and that is deliberate. |
0 commit comments