# AGENTS.md — rules for any agent (or human) working on dsh-remote-dashboard Read this whole file before changing anything. It is the source of truth; `CLAUDE.md` and the skill in `.agents/skills/` only point here. ## What this project is A **read-only** DSH plugin that shows session activity from several machines on a tablet/wall screen. Modes: `agent` (hub polls it), `push` (it sends to the hub), `hub`, `both`. Plain ESM, **no dependencies, no build step**, Node >= 22. The page is plain **ES5** for old tablet browsers. ## Non-negotiable rules 1. **Read-only.** Nothing here may write to DSH or run anything on a machine. The only non-GET route is `POST /v1/push`, and it only stores a sanitised snapshot in the hub's memory. 2. **No secrets in git, ever.** Tokens, viewer keys and real deploy configs live in `.research/` (git-ignored) or `tokenFile`s. Before every push run the check in *Release checklist*. If a secret reached a commit, stop and tell the user; do not "fix" it with a force-push on your own. 3. **No real data in anything committed**: screenshots, tests, docs and changelog visuals use **fake data** (`docs/screenshots.mjs`). Never commit a screenshot of a real tablet or real session titles/paths. 4. **Fail closed.** Non-loopback binds need `allowRemote: true` and a token/key; auth is checked before reading a body; compare secrets with `safeEqual`; the hub names machines from its config, never from a payload; everything a hub stores goes through `sanitizeSnapshot`. 5. **Never throw into DSH.** `apply()` logs `dsh-remote-dashboard: NOT RUNNING - …` and returns. 6. **No new dependencies** without asking the user. 7. **Ask before irreversible or shared actions**: `git push`, `npm publish`, restarting `dsh web`, editing `~/.dsh`, opening PRs. Local commits are fine. Never run destructive git commands (force-push, reset --hard) unprompted. 8. **The running plugin code does not hot-reload** — `patchReload: live` reloads config only. After updating the plugin on a machine, `dsh web` must be restarted. Say so whenever you change behaviour. 9. **The user's own config files are theirs.** Edit them only with `scripts/apply-config.py` (it backs up first), and only when asked. ## Follow the plan - Work from `ROADMAP.md`. Before starting a non-trivial change, state the plan (what, files, tests, visual) and get a yes if it changes behaviour or config format. - Implement what the roadmap item says, including its **Rules to keep** and **Tests** sections. If you must deviate, say why and update `ROADMAP.md` in the same commit. - When an item ships, move it from `ROADMAP.md` to `CHANGELOG.md` (don't leave it in both). - Unplanned fixes are fine, but record them in the changelog like anything else. ## Changelog rules Every user-visible change **must** add an entry to `CHANGELOG.md` in the same commit, under a version heading (`## X.Y.Z — YYYY-MM-DD`; unreleased work goes under `## Unreleased`), in Keep-a-Changelog groups (Added / Changed / Fixed / Removed / Security). **Every entry needs a visual**, committed under `docs/changelog/` and embedded in the entry: - UI change → **before/after screenshots** from fake data: ```sh git archive lib | tar -x -C .research/lib-before # then move lib/* up one level node docs/screenshots.mjs --lib .research/lib-before --scenario crowded --size 1280x710 --out docs/changelog/X.Y.Z-before.png node docs/screenshots.mjs --scenario crowded --size 1280x710 --out docs/changelog/X.Y.Z-after.png ``` (Add a scenario to `docs/screenshots.mjs` if none shows your change; scenarios must use invented data.) - Non-UI change (config, protocol, security) → a diagram or a rendered config/flow image, or a screenshot of the visible effect (a log line, the offline tile, the error). A text-only entry is not acceptable. - Refresh `docs/tablet.png` and `docs/phone.png` (`node docs/screenshots.mjs`) whenever the page's look changes. - Look at every image you generated before committing it (it must show what the entry claims, with no real data). `test/repo-rules.test.js` enforces the mechanical parts: an entry for the current `package.json` version, a visual that exists on disk and is embedded, no real-looking secrets, and the files npm needs. ## Release checklist (version bump) 1. `npm test` passes (all green, no hangs). 2. `package.json` version bumped (SemVer: config/protocol break = major; feature = minor; fix = patch), `CHANGELOG.md` entry + visual added. 3. README matches reality (modes table, config examples, Safety section, screenshots). 4. Secret scan: `for each token/key in .research/deploy: git grep -q $(git rev-list --all)` must find nothing. 5. Ask the user, then `git push`. `npm publish` and the catalogue PR are done by the user from their own accounts: prepare them (`npm pack --dry-run` content, the catalogue YAML), never publish yourself. ## How to work here - Tests: `node --test test/*.test.js`. Run a single test with `--test-name-pattern`. Network tests use port 0 and close everything they open; a test that hangs usually has an unclosed server or timer. - Layout: `lib/collect.js` (live per-session data from DSH events) · `lib/server.js` (config, hub, agent, `POST /v1/push`) · `lib/pusher.js` · `lib/snapshot.js` (the sanitiser) · `lib/page.html` (the whole UI, ES5) · `scripts/` (operator helpers) · `docs/` (screenshots + generator). - Page layout code mirrors CSS heights as constants (`HEAD`, `ROW`, `ROWC`, `MORE`…). If you change row CSS, change them too and re-run the fit check against several viewport sizes. - Sandbox notes for agents: `/tmp` does not persist between shell calls; each call is a fresh shell; background servers die with it (use a background job); `pkill -f` can match your own command line. - Style: small functions, comments only where the reason isn't obvious, plain error messages that say what to do.