This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
- See @ai_context/IMPLEMENTATION_PHILOSOPHY.md for core development principles
- See @ai_context/MODULAR_DESIGN_PHILOSOPHY.md for architecture patterns
BenchMesh is one cockpit for lab instruments: power supplies, multimeters, electronic loads,
signal generators, oscilloscopes, LCR meters and spectrum analyzers, connected over USB, serial or
LAN, controlled from one window, recorded on one shared timeline and automated with flows. It is
a Rust server with a React UI, shipped as a Tauri desktop app for Windows, macOS and
Linux. Working branch: develop. Public site: https://benchmesh.online (repo ../BenchMeshOnline).
Instruments are described by declarative device definitions (devices/*.yaml, shared parts
in devices/bases/, spec ai_context/migration/DEVICE_DSL.md, class contracts
ai_context/migration/CLASS_CONTRACT.md) interpreted by one generic driver: there is no
per-device code. Design history and decisions: ai_context/RUST_TAURI_MIGRATION_PLAN.md. The
Python/Electron/Node-RED system it replaced is kept only in git (tag legacy-python).
Crate (crates/) |
Role |
|---|---|
bm-devicedef |
DSL types (source of truth), validator, JSON Schema, bm-devicedef validate |
bm-driver |
interpreter: framing, parsing, CEL, SI dicts, engine, per-device actor, sim transport |
bm-transport |
serial (serialport), LAN (raw SCPI socket, VXI-11), USBTMC (Linux kernel driver; Windows/macOS: own USBTMC protocol over nusb, or an installed VISA loaded at run time), port discovery (Linux sysfs, Windows SetupAPI, macOS IORegistry), instrument identity matching + in-process port lock (ai_context/migration/IDENTITY.md) |
bm-server |
axum REST API, WebSockets, recordings v2 (one whole-second sampler from the registry, /ws/recordings), metrics, OpenAPI, virtual SCPI instruments (scpi/), UI; benchmesh-server binary |
bm-store |
recordings database v2 (SQLite/WAL ~/.benchmesh/recordings.db, 0600 with its journal: recordings, series, samples; contract ai_context/migration/RECORDINGS.md) |
bm-probe |
bench tool: verify (read-only) / get / set / screenshot against real instruments |
benchmesh-app |
Tauri desktop app: embedded bm-server + webview (not a default member) |
cargo test # all default crates (incl. capture replay tests)
cargo clippy --all-targets -- -D warnings
cargo run -q -p bm-devicedef -- validate devices
cargo run -p bm-server -- --config captures/bench-config.yaml # API + UI on :57666
cargo run -p bm-server -- --config captures/bench-config.yaml --simulate captures/20261016-203514 # no hardware
./target/debug/bm-probe --config captures/bench-config.yaml verify # bench check (read-only)
cargo tauri dev / cargo tauri build # desktop app (run in crates/benchmesh-app)App updates: the desktop app checks once per start for a newer published GitHub release
(signed latest.json, crates/benchmesh-app/src/update.rs) and the UI offers it in a modal
(frontend/src/ui/update/). Releases are signed with the updater key (CI secrets
TAURI_SIGNING_PRIVATE_KEY[_PASSWORD]); losing that key means installed apps can no longer update.
Cross-site guard: the loopback listener (127.0.0.1:57666, the app window) has no
authentication, so crates/bm-server/src/guard.rs refuses what a foreign web page could make the
browser send: a Host that is not a loopback name (DNS rebinding), and any request whose Origin
is neither the server's own nor a loopback origin (form posts, fetch, WebSocket upgrades). There
is no CORS layer: the UI is same-origin. UI files are sent no-store, never 304 (lib.rs fresh_ui_request): different builds share the
address and the webview cache, and a stale index.html asks for assets the running build lacks (white window). Nothing unauthenticated listens on the network.
Remote access (contract and threat model ai_context/REMOTE_ACCESS.md, crates/bm-server/src/remote/,
UI frontend/src/ui/remote/): off by default; switched on in Instruments → Remote access with a password, the server
opens a second listener, HTTPS (rustls, self-signed certificate in <data dir>/remote-tls/, its
fingerprint shown in the app) on every interface, port 57667, accepting only local-network
addresses. Browsers sign in (/remote/login, PBKDF2 hash in <data dir>/remote.json, rate
limited) and get a __Host- session cookie; remote::gate checks Host/Origin, the session and the
route's scope (remote/scope.rs: unclassified routes are admin = this computer only; remote
sessions hold every scope except admin: remote access itself, MCP, assistant keys, the Windows
driver install). An optional view-only password (PUT|DELETE /remote/view-password) signs in
sessions of role view: the gate refuses everything but GET/HEAD (and sign-out) with 403, the UI
shows Remote - view only, panel bodies inert, no park or new recording (useViewOnly). The app window and remote browsers work at the same time; the top bar shows
Remote: N with Disconnect all and switch off; ending a session closes its WebSockets.
Headless: benchmesh-server remote-password (stdin). Future online accounts map onto the same
principals/scopes (see the contract).
Headless (contract ai_context/HEADLESS.md): benchmesh --headless (the desktop app's executable, every OS)
and benchmesh-server run without a window, used through remote access; --remote-password (or
BENCHMESH_REMOTE_PASSWORD) switches it on and the hash is kept, --port, --data-dir, --config. Shared runner
crates/bm-server/src/headless.rs, the app's part crates/benchmesh-app/src/headless.rs. Instruments → System
(frontend/src/ui/system/, API /system/**, scope system, crates/bm-server/src/system/, resources via sysinfo):
host, OS, cores, uptime, temperature, load, processor / memory / disk meters and BenchMesh's own use for everyone;
headless only Restart (same command line; Unix exec in place) and Shut down (exit 0), two presses, refused
while a flow runs, and Start with this computer (system/autostart.rs: systemd user service + linger command on
Linux, LaunchAgent on macOS, Run key on Windows; never the password; not for a simulation), and Updates
(system/update.rs, UI UpdateGroup.tsx): Check for updates reads the signed latest.json; Install (two presses)
downloads this installation's file to <data dir>/updates/, verifies the minisign signature with the updater key
(same as tauri.conf.json, a test keeps them equal), then stops and replaces the AppImage / .app in place or runs the
Windows installer silently, and starts again; a .deb gets the sudo apt install command. Releases include Linux
ARM64 (Raspberry Pi 4/5, ubuntu-22.04-arm runner); CI tests the desktop app on ARM and the server's system code on
Windows and macOS.
Automation, assistant, MCP (contract ai_context/AUTOMATION.md): flows are JSON documents
(benchmesh.flow/v1: params, setup / steps / cleanup-always-runs; steps set, read, wait, let,
if, repeat, sweep, check, log, prompt, record, stop; CEL expressions) validated against the
bench's vocabulary and run by one executor (crates/bm-server/src/automation/): one run at a
time, energising steps need arm, every set confirmed by read-back within 6 s, status reads
newer than the last set, abort interrupts waits, a failed/aborted run switches off what it
switched on. Flows live in <data dir>/flows/, settings (assistant provider/model/API keys, MCP
access) in <data dir>/automation.json (0600). The assistant (assist.rs) sends the user's
description + vocabulary to the user's own provider (Anthropic / OpenAI / OpenRouter /
OpenAI-compatible) and validates the returned flow (up to two self-corrections); it never runs
it. POST /mcp (mcp.rs) is an MCP server (Streamable HTTP, JSON responses; access off / read
(default) / control); benchmesh --mcp and benchmesh-server mcp are stdio bridges to it.
Connectors (Instruments → Connectors, frontend/src/ui/connectors/): virtual SCPI instruments
(contract ai_context/SCPI.md, crates/bm-server/src/scpi/): off by default (access off / read /
control, <data dir>/scpi.json); every configured instrument gets a raw SCPI socket on
127.0.0.1 (TCPIP0::127.0.0.1::<port>::SOCKET, ports from base_port 5025, kept per instrument)
so MATLAB, LabVIEW, PyVISA and TestStand use it like a LAN instrument whatever its own link. Dialect:
*IDN?, [CLS:][CHn:]<op>? / <op> <value> (the definition's operations), FETCh:<field>?
(latest polled status), SYST:ERR?, SYST:HELP:HEAD?; a failed query answers an empty line.
A connection that sends an HTTP request line or header is closed (a web page cannot smuggle
commands); setters for an instrument the running flow uses answer -221; GET|PUT /scpi are admin.
The Python package benchmesh (PyPI, zero dependencies; separate repository, local copy
../benchmesh-python) wraps the REST API: read / set / status / watch, recordings to pandas, flows,
remote access with certificate pinning.
Website (BenchMeshOnline, ../BenchMeshOnline): the public instrument database is exported
from the definitions (cargo run -p bm-devicedef -- export devices --notes ai_context/website/instrument-notes.yaml, schema benchmesh.instruments/v1, no wire details)
by the site's npm run sync, which also copies the UI kit. At start the server reads the
community status once (GET {BENCHMESH_ONLINE_URL or https://benchmesh.online}/api/v1/status,
benchmesh.status/v1, crates/bm-server/src/community.rs; BENCHMESH_ONLINE=off disables it,
simulations stay offline); /catalog merges it per model (community; an awaiting model users
confirmed counts as confirmed) and Instruments → Supported shows it with the confirming versions.
Parking (contract ai_context/PARKING.md): POST|DELETE /devices/{id}/park sets parked: true in
the config entry; the device thread closes the link (after on_disconnect), sends nothing and only checks
presence every 5 s (bm_transport::presence: port enumeration / TCP connect; for a serial link that proves only the port - presence_of: port, shown amber - since a USB-serial interface powered by the computer stays attached with the instrument off), requests answer 409, a
parked instrument is never opened at start. Refused while the running flow uses the instrument; a run
using a parked one is refused. UI: park icon before Fold, parked panels wait in the parking strip left of
the rack (bench/ParkingStrip.tsx, bench/parking.ts) and return to their place when unparked.
Instrument identity: a config entry may carry match (USB vid/pid + serial number or chip
revision, port position as a tie-breaker, and the serial number from the identify reply) instead
of a fixed port/device; the instrument is then found on whatever port it is plugged into, on
every reconnect, on Linux/Windows/macOS without udev rules or admin rights.
On Windows a USBTMC instrument without a usable driver gets Install driver (drawer / Detect):
the server re-runs its executable elevated (--install-winusb, bm_transport::winusb) to bind
Microsoft's in-box WinUSB driver - the one admin prompt Windows requires. A port the OS refuses is explained (bm_transport::access,
message ending " - see "): Linux "Permission denied" (/dev/usbtmcN belongs to root, serial ports to
dialout/uucp) and macOS 15+ "No route to host" to a LAN instrument (Local Network permission; the app bundle's
crates/benchmesh-app/Info.plist gives the prompt's reason). The panel and Detect show the fix (bench/AccessFix.tsx):
a command with Copy (Linux), Open Local Network settings (macOS desktop app), and "How to fix" to
https://benchmesh.online/docs/linux#usb-permissions / #serial-permissions / docs/macos#local-network (same commands in
the website's src/lib/linux.ts, section id in src/lib/macos.ts). POST /devices/detect
identifies attached instruments (identify queries only) and the Instruments drawer adds or locks
them. Contract: ai_context/migration/IDENTITY.md; cargo run -p bm-transport --example ports
lists what the OS reports.
Bench/hardware notes and wire captures: captures/ (golden transcripts replayed by the tests),
scripts/bench_setters.sh, scripts/bench_scope_setters.sh, ai_context/migration/device-notes/.
GET /instruments,/instruments/{CLS},/instruments/{CLS}/{id}(features),/instruments/{CLS}/{id}/methodsGET /instruments/{CLS}/{id}/status: the latest polled status per channel with its update time (ms), nothing sent to the instrument (what the bench shows; used by the Python package).GET /instruments/{CLS}/{id}/{ch}/{op}runs the definition's queryop;POST /instruments/{CLS}/{id}/{ch}/{op}/{value}runs its setter. Only operations the definition declares for that class can be called, a GET never runs a setter and a POST never runs a query; values are validated against the setter's value spec before anything is sent./methodslists every operation with its HTTP verb;POSTentries carry the setter's value spec from the definition, e.g."value": {"type": "number", "min": 0, "max": 60, "format": "%.2f", "unit": "V"}or{"type": "enum", "values": ["OFF", "ON"]}, plus"energizes": truefor operations that can switch an output/input on (the UI requires two presses for those). Implemented incrates/bm-server/src/opspec.rs(also used by/ai/contextand the automation vocabulary)./openapi.json//openapi.yamldescribe every route (crates/bm-server/src/openapi.rs; a test fails when a route is missing; extensionsx-websocketandx-no-try: <reason>for what cannot be sent from the UI);/docsopens the UI's API reference drawer (top bar API,frontend/src/ui/apidocs/: endpoints by tag, filter, parameters with instrument suggestions, JSON body, Send - two presses for anything but GET - with status, time and response, curl line, first message of a WebSocket; pure modelopenapi.ts).
User data lives in ~/.benchmesh/ (BENCHMESH_DATA_DIR): config.yaml (the instruments),
recordings.db, flows/, automation.json, scpi.json. A simulation (--simulate <capture>) keeps
recordings, flows and settings in memory. config.yaml:
version: 1
devices:
- {id: tenma-72-2540, name: Tenma, driver: tenma_72, model: 72-2540, transport: serial, port: /dev/ttyACM0, baud: 9600, serial: 8N1}
- {id: rigol-dho804, name: Rigol DHO804, driver: rigol_dho800, model: DHO804, transport: usbtmc, device: /dev/usbtmc0}driver names the definition (devices/<driver>.yaml); parked: true keeps an instrument disconnected
(see Parking); transports are serial (port, baud,
serial), usbtmc (device) and tcpip (raw SCPI socket) / vxi11 (host, optionally
:port). Instead of port/device a USB entry may carry match (see Instrument identity); the
UI's Instruments drawer and Detect write these entries (crates/bm-server/src/config.rs).
- Find the family's definition in
devices/(or a base indevices/bases/) and add the model, or write a new definition followingDEVICE_DSL.mdand the class contract. cargo run -q -p bm-devicedef -- validate devices(0 errors).- With hardware:
bm-probe verify(read-only), thenget/setchecks; record a capture for the replay tests when the definition is confirmed. Without hardware: mark it from the manual (speculative coverage,ai_context/migration/coverage/). - The UI needs nothing: class panels read features and value specs from the API.
The UI is a bench: one panel per instrument class (PSU, DMM, ELL, AWG, OSC, LCR, SAL; any other
class gets a read-only generic panel), laid out in a responsive rack. Every instrument of a class
uses the same panel; manufacturer differences (limits, steps, ranges, modes, optional controls)
come only from the API - features (GET /instruments/{CLS}/{id}) and setter value specs
(GET /instruments/{CLS}/{id}/methods, from the device definition). There is no per-device UI
code; the definitions' ui_component is not used by the UI.
Path (frontend/src/ui/) |
Role |
|---|---|
kit/ |
instrument component kit with three skins in kit.css - Reflective LCD (default), LED segment, Cockpit glass - switched in Instruments → Miscellaneous (drawers/SkinSetting.tsx, kit/skin.tsx, stored in localStorage benchmesh:skin; canvases read colour tokens at draw time). Components: segment readout, digit-wise setpoint editor, arm-to-enable switch (two presses for anything that energizes), micro switch, annunciators, rotary dial, knob, 1-2-5 stepper, bargraph, sparkline, waveform preview, oscilloscope screen, spectrum screen, action button (momentary actions confirmed by a reported change). Fonts are bundled (B612, B612 Mono, Chakra Petch) - the desktop app runs offline |
bench/profiles.ts |
features + methods → what a class panel shows (limits, decimals, modes, ranges, optional controls) |
bench/panels/ |
class panels (PsuPanel, DmmPanel, EllPanel - modes without a setpoint, Short (two presses); AwgChannel - frequency or period, amplitude as Vpp / Vrms / dBm / high-low levels, phase, ramp symmetry, load, pulse duty/width/edges, mode CW / MOD (AM/FM/PM: modulating wave, frequency, depth or deviation) / SWEEP (start, stop, time, spacing, trigger) / BURST (N cycles or infinite, period, trigger), each control only when the definition binds it: conversions in bench/awgMath.ts, values the instrument does not report tagged CALC; OscPanel - one screen for all channels, LcrPanel - primary/secondary readouts, function keys L/C/R/Z/DCR → secondary → SER/PAR, frequency/level, range/speed, open/short correction with a prompt, bias; SalPanel - spectrum screen (kit/SpectrumScreen: dB graticule, max-hold ghost, marker diamond, click to place), marker readout, annotation bar, soft-key groups FREQ/BW/AMPT/SWEEP/TRACE/MKR/TG; GenericPanel) |
bench/ParkingStrip.tsx, bench/parking.ts |
parked instruments (ai_context/PARKING.md): park control in the panel header (asks twice with an output on, disabled while the running flow uses the instrument), slim strip left of the rack with presence dot and name, sheet with details and Unpark |
bench/layout.ts, Rack.tsx |
panel order / width (S, M, L, W) / hidden / channel layout of multi-channel panels (side by side or stacked), persisted in localStorage (benchmesh:bench-layout); columns follow the window width (300 px units) |
bench/BenchLayout.tsx, bench/TitleBar.tsx |
shell: a fixed head of two rows (only the content below it, .bench-body, scrolls) - the title bar (brand, version on its baseline, Arrange as an app control, then on Windows/Linux the window controls minimize / maximize / close; the bar drags the window, a double press maximizes) and the top bar (API / live / run / Remote on the left, drawer buttons on the right) - Arrange mode (drag, ◀ ▶, width, channels ⇆ / ⇅, hide), drawers (Instruments config, Request log; they open below the head, which stays usable: a menu button switches the open drawer). The desktop app's main window has no OS title bar on Windows and Linux (decorations(false) in benchmesh-app main.rs, permissions capabilities/main-window.json); macOS keeps its native title bar and the UI shows no window controls there (desktop.ts hasOwnWindowControls) |
drawers/ |
Instruments drawer tabs: configuration (incl. Detect: DetectView.tsx, detect.ts), Supported, Miscellaneous, Metrics (MetricsView.tsx + metrics.ts: per instrument link busy, exchanges/s, API latency from /ws/metrics, judged against limits), System (system/: resources; restart, shut down, start with the computer when headless), Connectors (connectors/: virtual SCPI access, addresses with MATLAB / PyVISA / LabVIEW code, the Python package; this computer only), Remote access; request log |
recording/ |
recordings v2 (ai_context/migration/RECORDINGS.md): RecordingsProvider (REST + /ws/recordings, live sample buffers; main and panel windows), recordable readings (signal.tsx: SignalScope + Recordable, kit ContextMenu on right-click / context-menu key, red dot when recorded), recorder dock (bottom of the main window, benchmesh:recorder-dock), recordings drawer (history, zoomable chart, rename, CSV export, delete). Chart: chartModel.ts (one y-axis per series, colours --kit-series-1..8), ECharts core loaded lazily in RecordingChart.tsx |
kit/brand.ts, kit/BrandMark.tsx |
the logo ("Readout": seven-segment b, ghost segments, amber decimal point): one geometry, colours on dark / on light; BrandMark follows the skin (--kit-brand-*) or takes fixed colours (website). node scripts/logo/build.mjs writes docs/static/logo/*.svg, frontend/public/favicon.svg and the app icons (crates/benchmesh-app/icons, via Inkscape + cargo tauri icon); a test fails when they are stale |
about/ |
title-bar version (GET /version, version.ts) and the Feedback drawer (AboutView.tsx: version, Copy version, GitHub issues links, contact email support@benchmesh.online with Copy email). External pages open by name (links.tsx): in the desktop app the system browser (or mail app) via the open_page command (fixed list, crates/benchmesh-app/src/links.rs, capability links.json), in a browser a new tab |
DisclaimerModal.tsx, disclaimer.css |
start screen until accepted (benchmesh:disclaimer-accepted when "don't show again" is ticked): centred logo, thanks, the app is in development and needs feedback, contact support@benchmesh.online, then the safety points; kit tokens, so every skin. The flag lives in disclaimer.ts; Instruments → Miscellaneous Start screen - Show at start (drawers/StartScreenSetting.tsx) brings it back |
automation/ |
Automation drawer (top bar Automation, contract ai_context/AUTOMATION.md): saved flows (new, duplicate, delete, import/export JSON), flow editor (parameter form, Setup / Steps / Cleanup step cards with vocabulary pickers, live validation shown at each step's path, JSON view; pure document model flowModel.ts), run panel (arm dialog for energising flows, live log, variables, prompt Continue, Abort / Escape), assistant (user's own LLM key, result loaded as unsaved), settings tab (provider, key - only its hint is ever shown, model, base URL; MCP access + connection snippets). RunProvider.tsx polls /automation/run for the whole UI: top-bar ▶ flow - state + red Stop while a run is active |
Panel window controls (header, top right): fold to the header line (saved with the layout;
double-click the header works too) and open in its own window. A detached panel lives at
/ui/?panel=<instrumentId:CLASS> (bench/PanelWindow.tsx): in the desktop app an undecorated
Tauri window (panel-*, opened from the UI via window.__TAURI__; permissions in
crates/benchmesh-app/capabilities/panels.json) whose header drags it, with fold (shrinks the
window), keep-on-top and close; in a browser a popup. While its window lives it writes a
heartbeat (bench/detach.ts) and the bench leaves the panel out (never drawn twice) and lists it
under "In own windows"; closing (or a crash) brings it back. Closing the main window closes the
app. On Linux Wayland the app runs through XWayland (GDK_BACKEND=x11) because Wayland does not
allow keep-on-top; BENCHMESH_NATIVE_WAYLAND=1 opts out. Run as an AppImage, the app writes its desktop entry
and icon for the user at start (~/.local/share/applications/benchmesh.desktop, benchmesh-app desktop_entry.rs):
docks match the window (class / app id benchmesh) to it, otherwise newer GNOME shows a generic icon.
Instrument console (panel header, console icon): an optional pane at the bottom of the panel
showing what is sent to (→) and received from (←) the instrument, plus port events (•), from
GET /devices/{id}/wire?after=<seq>&limit=<n> (per-device ring of 50 000 entries / 8 MiB, about
19 minutes of a polled oscilloscope and longer for meters, bm_driver::wirelog; reply chunks
joined in the UI, which shows the last 5000 lines). Auto / Text / Hex view, pause, clear, Copy:
paste-ready evidence with instrument, model, identify reply, link and app version, and Save:
the same for everything the server kept, as a text file (bench/wire.ts, bench/WireConsole.tsx).
Transports add their own events as info lines (Transport::take_notes), e.g. a USBTMC recovery:
a write that times out or stalls gets abort bulk-out/in, clear halts and a device clear, then one
more try, else an error telling the user to replug (bm-transport usbtmc.rs, usbtmc_proto.rs). Open state per panel in localStorage benchmesh:consoles.
Every control that changes the instrument waits for confirmation: after the request is sent it
pulses discreetly until the instrument's reported value (registry status, or a read-back for
setpoints) equals the requested one, and shows a small red dot if that does not happen within 6 s
(kit/confirm.ts). Pass the reported value, never a locally assumed one.
Drawer pages of text and controls use the shared page standard in bench/bench.css: content in
.bench-page (16 px inset, 14 px rhythm, .bench-hint text), boxes in .bench-group, rows in
.bench-group__row (About and feedback, Metrics, Remote access); e2e/about-feedback.spec.ts fails when a
drawer's content comes closer than 8 px to the drawer's sides. Buttons are kit buttons (bench-btn, --primary for
the main action, --danger for removal, --icon for a glyph), fields the kit field (kit-field, kit-label,
kit-input); the Instruments form styles its fields with the same tokens.
Adding a class panel: write bench/panels/<Cls>Panel.tsx from kit components, a <cls>Profile()
in profiles.ts (with tests against bench/__tests__/bench-fixtures.json, captured from the
simulated bench), and dispatch it in InstrumentPanel.tsx.
npm ci
npm run dev # Vite dev server, proxies the API to :57666 (BENCHMESH_API overrides)
npm run build # into frontend/dist (served by bm-server under /ui)
npm run lint
npx vitest run # unit tests (CI)
npm run test:e2e # Playwright against a preview build with a mocked API (CI)| Variable | Effect |
|---|---|
BENCHMESH_CONFIG |
config file (default ~/.benchmesh/config.yaml) |
BENCHMESH_DATA_DIR |
user data directory (default ~/.benchmesh) |
BENCHMESH_DEVICES |
device definitions directory (default ./devices, in the app its bundled copy) |
BENCHMESH_UI |
UI directory for the app (default its bundled copy; bm-server --ui) |
BENCHMESH_SIMULATE |
capture to simulate instead of real hardware (--simulate) |
API_PORT |
bm-server loopback port (default 57666; --port); other computers use remote access |
BENCHMESH_ONLINE / BENCHMESH_ONLINE_URL |
off disables the community status / its base URL |
BENCHMESH_URL |
server address for the MCP stdio bridge |
BENCHMESH_UPDATE_URL |
headless updates: another latest.json address (staging; the signature key stays fixed) |
BENCHMESH_REMOTE_PASSWORD |
headless: remote access password at start (as --remote-password, not shown in process lists) |
BENCHMESH_NATIVE_WAYLAND |
1: run the app on native Wayland instead of XWayland |
BM_WS_INTERVAL |
registry WebSocket push interval in seconds |
.github/workflows/README.md: ci.yaml (frontend lint/tests/build, Rust clippy/tests/definition
validation, transport on Windows and macOS), e2e-tests.yml, release-tauri.yml (tag vX.Y.Z
-> draft release for all platforms incl. Linux ARM64; publish with gh release edit vX.Y.Z --draft=false).
- apply TDD principles when adding new features
- always MUST run tests after the code changes
- do not try to maintain "Fallback for legacy" we are developing a new software, there is no legacy we need to maintain
- always MUST cover new development with tests - whatever is added or improved
- always MUST validate if the documentation is still up to date
- always MUST follow single responsibility principle
- differentiate between unit tests and integration tests - integration tests should NOT run in GitHub Actions (reserve for local/staging testing only)
- all new unit tests that are suitable for GitHub Actions execution must be automatically added to the CI workflow
- when you add or remove dependency update THIRD-PARTY-NOTICES.md
- when you create any temporary or tool scripts place them always in scripts folder
- plain text (UI, messages, definitions, docs, release notes) uses "-" only, never em or en dashes;
frontend/src/__tests__/noDashes.test.tschecks every tracked file - always verify CI tests localy
- when implementing new feature always try to test on the real working service particularly applies to the server and API (
--simulatewhen no hardware is attached) - do apply defensive programming principle - especially with API implementation the parameters must be validated and resources need to return meaningful response - catch and log 500 responses
- feel free to kill and spinn up the services we are developing in this project but always MUST clean up - do not leave used ports, release all resources
- VERY IMPORTANT: Always think through a plan for every ask, and if it is more than a simple request, break it down and use TodoWrite tool to manage a todo list. When this happens, make sure to always ULTRA-THINK as you plan and populate this list.
- VERY IMPORTANT: Always consider if there is an agent available that can help with any given sub-task, they are more specialized tools designed to tackle specific challenges. Your role is to be a general coordinator. Use the Task tool to delegate specific tasks to these agents. Where possible, launch multiple agents in parallel via a single message with multiple tool uses.
- VERY IMPORTANT: If user has not provided enough clarity to CONFIDENTLY proceed, ask clarifying questions until you have a solid understanding of the task.
CRITICAL: Always ask yourself: "What can I do in parallel here?" Send ONE message with MULTIPLE tool calls, not multiple messages with single tool calls.
Parallelize when tasks:
- Don't depend on each other's output
- Perform similar operations on different targets
- Can be delegated to different agents
- Gather independent information
When fixing the same issue across files (e.g., type errors, import updates):
Single message with multiple Edit/MultiEdit calls:
- Edit: Fix type error in src/auth.py
- Edit: Fix type error in src/database.py
- Edit: Fix type error in src/api.py
When pyright reports multiple type errors:
Single message addressing all errors:
- Read: Check current implementation in affected files
- MultiEdit: Fix all type errors in utils.py
- MultiEdit: Fix all type errors in models.py
- Edit: Update type imports in __init__.py
Before implementing features:
Single message with parallel reads and searches:
- Grep: Search for existing patterns
- Read: Main implementation file
- Read: Test file
- Read: Related configuration
For comprehensive review:
Single message with multiple Task calls:
- Task zen-architect: "Design approach"
- Task bug-hunter: "Identify potential issues"
- Task test-coverage: "Suggest test cases"
Don't do this:
"Let me read the first file"
[Read file1.py]
"Now let me read the second file"
[Read file2.py]
Do this instead:
"I'll examine these files in parallel"
[Single message: Read file1.py, Read file2.py, Read file3.py]
- Parallel execution is the default, not an optimization
- Sequential execution needs justification (true dependencies)
- Context is preserved better with parallel operations
- Users prefer comprehensive results over watching sequential progress
- Limited context requires strategic compaction - Details get summarized and lost
- Two key solutions:
- Use memory system for critical persistent information
- Use sub-agents to fork context and conserve space
- Smart memory usage - Not everything goes in memory, be selective about what's truly critical
- Each sub-agent only returns the parts of their context that are requested or needed
- Fork context for parallel, unbiased work
- Conserve context by delegating and receiving only essential results
- Create specialized agents for reusable, focused purposes
- Analysis tasks - Let them do deep work and return synthesis
- Parallel exploration - Fork for unbiased opinions
- Complex multi-step work - Delegate entire workflows
- Specialized expertise - Use focused agents over generic capability
- Don't hesitate to request new specialized agents
- Specialized and focused > generalized and generic
- Request that user creates them via user's
/agentscommand - You provide the user with a detailed description
- New agents undergo Claude Code optimization
- Better to have too many specialized tools than struggle with generic ones
- I am the overseer/manager/orchestrator
- Delegate EVERYTHING possible to sub-agents
- Focus on what ONLY I can do for the user
- Be the #1 partner, not the worker
- Wrap sub-agent capabilities into code utilities using Claude Code SDK
- See docs in
ai_context/claude_code/CLAUDE_CODE_SDK.md - See examples in
ai_context/git_collector/CLAUDE_CODE_SDK_PYTHON.md
- See docs in
- Create "recipes" for dependable workflow execution that are "more code than model"
- Orchestrates the use of the Claude Code sub-agents for subtasks, using code where more structure is beneficial
- Reserve use of Claude Code sub-agents for tasks that are hard to codify
- Balance structured data needs with valuable natural language
- Build these progressively as patterns emerge
- Clarification - Ask when truly uncertain about direction
- Checkpoints - Surface completed work stages for validation
- Proxy decisions - Answer sub-agent questions when possible, escalate when needed
- Learning stance - Act as skilled new employee learning "our way"
- Track what I learn from user interactions
- Make learnings visible and actionable
- Consider memory retrieval sub-agent for context-appropriate recall
- Avoid repeated teaching of same concepts
- Become more aligned with user over time
- Working Memory - Current session critical info
- Long-term Memory - Persistent learnings and patterns
- Retrieval System - Sub-agent to pull relevant memories per task
- Learning Log - Track what's been learned and when
- Regularly mine articles for new ideas
- Run experimental implementations
- Measure and test changes systematically
- Evaluate improvements vs degradations
- Support parallel experimentation in different trees
- Becoming the most valuable tool in user's arsenal
- Amplifying user's work effectively
- Acting as true partner and accelerator
- Learning and improving continuously
- Maintaining alignment with user's approach
See ai_context/IMPLEMENTATION_PHILOSOPHY.md for complete implelemtation philosophy guide.
See ai_context/MODULAR_DESIGN_PHILOSOPHY.md for complete modular design philosophy guide.
- Embrace ruthless simplicity
- Build as bricks and studs
- Trust in emergence over control
- Design comprehensive knowledge synthesis architecture
- Create specialized planning sub-agent
- Build memory retrieval system
- Establish measurement framework
- Begin continuous learning cycle
When working with documents that contain references:
- Always check for references/citations at the end of documents
- Re-read source materials when implementing referenced concepts
- Understand the backstory/context before applying ideas
- Track which articles informed which decisions for learning
This ensures we build on the full depth of ideas, not just their summaries.