diff --git a/agent-skills.html b/agent-skills.html new file mode 100644 index 0000000..954fddb --- /dev/null +++ b/agent-skills.html @@ -0,0 +1,382 @@ + + + +Agent skills - MeshBench + + + + + + + + +
+ +

Agent skills

+

A coding agent pointed at MeshBench can read the verb list and the client references on this site, and from those it can work out what it is allowed to call. What it cannot work out is which calls fail quietly: which wait has a false premise, which reply is a refusal wearing the shape of a success, which step of an import decides whether the mesh relays anything at all. That is the knowledge MeshBench ships as agent skills.

+

Three skills are maintained beside the code, in .claude/skills/ in the MeshBench repository, and mirrored into two standalone repositories so they can be installed by an agent working somewhere else.

+

What a skill is#

+

A skill is a directory holding a SKILL.md: YAML front matter carrying a name and a description, then Markdown instructions. The agent loads only the description at startup, which is enough to decide whether the skill is relevant, and reads the body only when a task matches it. Many skills therefore cost almost nothing until one of them is needed.

+

The format is the Agent Skills open standard, which several agents and editors read. A skill is plain Markdown in a folder, under version control, with no runtime and nothing to install.

+

The three skills#

+
SkillLoads whenRepository
meshcoresimdriving the simulator to answer an RF or mesh question: link viability, coverage, why a packet missed, a firmware A/Bmeshbench-scripting-skills
meshbench-scriptingwriting or debugging a script that opens a session, brings a mesh up and waits for itmeshbench-scripting-skills
wb2-design-languagebuilding or changing a Gio panel, control, menu or map drawingmeshbench-dev-skills
+

The split is by audience. The first two are for using MeshBench from outside, and are useful to anyone driving a workbench. The third is for changing MeshBench itself, and is only useful inside a checkout.

+

meshcoresim carries the order in which a scenario has to be built, the region inference step that decides whether anything relays, the two spellings a region has and which one goes on the wire, and the honesty rules a result is held to: both directions, no verdict on an uncertain position, one run is not evidence, quote the provenance. It also carries what the simulator does not model, because the omissions all bias the same way and a number quoted without that is a claim the model cannot support.

+

meshbench-scripting carries the failure modes of a driven session: which waits have premises that do not hold, why sim.start is not the way a script starts a run, which of the two consoles a node kind actually reads, and how a refusal reaches a caller. It is written from faults found by running the shipped examples end to end rather than from the API surface.

+

wb2-design-language carries the interface decisions: that colour and size come from one theme package, that panels fire verbs rather than mutating state, that a long operation announces itself, and that nothing counts as done until it has been seen running.

+

The register differs from this site's. A skill states a rule with the reason attached, in the voice of something learned rather than something specified, because an agent that knows why a rule exists applies it to the case the rule did not anticipate.

+

Installing#

+

Every agent below reads the same SKILL.md. What differs is only the directory it looks in.

+
git clone https://github.com/MeshBench/meshbench-scripting-skills
+git clone https://github.com/MeshBench/meshbench-dev-skills
+

Then copy the skill directories from skills/ into whichever of these the agent reads. A project directory makes the skill available to anyone who clones the project; a user directory makes it available in every project.

+
AgentProject directoryUser directory
Claude Code.claude/skills/~/.claude/skills/
VS Code and GitHub Copilot.github/skills/, .claude/skills/ or .agents/skills/~/.copilot/skills/, ~/.claude/skills/ or ~/.agents/skills/
Cursor.cursor/skills/ or .agents/skills/~/.cursor/skills/ or ~/.agents/skills/
Gemini CLI.gemini/skills/ or .agents/skills/~/.gemini/skills/ or ~/.agents/skills/
Codex.agents/skills/, from the working directory up to the repository root~/.agents/skills/
+

Only the Claude Code row is verified here, because that is the agent the MeshBench repository itself is set up for and the one the skills are exercised under. Every other row is taken from that tool's own documentation, linked under further reading, and is stated rather than tested. Check the tool's documentation before concluding a path is wrong.

+

.agents/skills/ is read by Cursor, Gemini CLI, Codex and VS Code, so one directory covers several agents at once. A skill directory may be a symbolic link to a checkout elsewhere on disk, which is the way to install once and update with git pull rather than by copying again.

+
mkdir -p ~/.agents/skills
+ln -s "$PWD/meshbench-scripting-skills/skills/meshbench-scripting" ~/.agents/skills/
+

Claude Code, Cursor, Gemini CLI and Codex all resolve a symbolic link and read SKILL.md from its target.

+

Agents that upload rather than read a directory#

+

Claude in the browser and desktop applications, and the Skills API, take a skill as an upload rather than from a directory on disk. Those paths accept only the front-matter fields in the Agent Skills specification: name, description, license, compatibility, metadata and allowed-tools. Any other field fails the upload rather than being ignored. MeshBench's skills declare name and description and nothing else, so they upload unchanged.

+

Agents with no skill support#

+

An agent that has no skills mechanism can still be given the content: a SKILL.md is Markdown, so its body pasted into that agent's rules or instructions file works, at the cost of the progressive loading. The whole skill then occupies context in every session rather than only in the sessions that need it, which is the trade the skills format exists to avoid.

+

Using them#

+

A skill loads on its own when a task matches its description, so nothing has to be typed to get the benefit. Asking an agent to write a script that brings a mesh up should pull in meshbench-scripting; asking why a packet did not arrive should pull in meshcoresim. Claude Code also invokes one directly by name, as /meshbench-scripting.

+

A skill is not a substitute for the references on this site. It says what an agent would otherwise get wrong; the control socket reference, the Python, Go and Node client references and the cookbook say what the calls are. Both are wanted: an agent with the verb list and no skill writes code that runs and quietly measures nothing.

+

Keeping them true#

+

The copies in the MeshBench repository under .claude/skills/ are canonical. The two standalone repositories are mirrors, updated by copying, and a skill that changes in one place is expected to change in both in the same piece of work. Nothing enforces that, so a mirror can be behind: when the two disagree, the copy beside the code is the one that is right.

+

A skill that states something no longer true is worse than a skill that says nothing, because it is acted on with confidence. That is why the skills name the file or the command that settles each claim, and why counts that a script generates are cited by their generator rather than written out as a number.

+

Further reading#

+ + +
MeshBench documentation. Built from the running application, not from +mock-ups. Screenshots are window-only captures; see CLAUDE.md for the rule that +keeps them current. Edit this page.
+
diff --git a/app-development.html b/app-development.html index 387ef36..e4b66c4 100644 --- a/app-development.html +++ b/app-development.html @@ -225,7 +225,7 @@ - +

Writing an application against a mesh

One command gives you a running network and an address to point a client at.

meshbench serve
diff --git a/architecture.html b/architecture.html index 742703e..127d8e1 100644 --- a/architecture.html +++ b/architecture.html @@ -225,7 +225,7 @@ - +

Architecture

MeshBench is one binary on one machine. There is no service, no worker, no backend. The only things that cross the network are *data*: terrain tiles, map tiles, and the optional CoreScope, Beacon and MQTT feeds. Nothing in the simulation depends on anything remote.

diff --git a/concepts.html b/concepts.html index 266d5ad..d5968c6 100644 --- a/concepts.html +++ b/concepts.html @@ -225,7 +225,7 @@ - +

What is real, and what is modelled

MeshBench runs real MeshCore firmware. MeshBench models the air.

That one sentence is the whole design. MeshBench does not simulate the firmware's behaviour: every node is the actual MeshCore application, compiled and running as its own process, keeping its own state, making its own routing decisions. What MeshBench provides is everything the firmware cannot bring with it: the radio spectrum, the terrain, the distances, the noise, and a clock all the nodes share.

diff --git a/cookbook.html b/cookbook.html index 5924efb..8a1d21f 100644 --- a/cookbook.html +++ b/cookbook.html @@ -225,7 +225,7 @@ - +

The scripting cookbook

Seven runnable programs, the same seven in each client, that do a whole task end to end rather than showing one call. They are the worked answers behind Scripting a session: read that for how a session is opened and what a verb is, then come here for a shape close to what you want and change it.

They live beside the clients they use, in the meshbench repository:

@@ -995,7 +995,7 @@

The sevenWhat a scripted result still is#

Everything on the limits page applies to a script's output exactly as it does to the application's: a simulated result is a best case, kinder than the air. A number a script prints is worth no more than the same number read off the screen, and the caveats travel with it. The Scripting a session page ends on the two limits that belong to scripting itself - reproducibility, and what a script cannot see.

- +
MeshBench documentation. Built from the running application, not from mock-ups. Screenshots are window-only captures; see CLAUDE.md for the rule that keeps them current. Edit this page.
diff --git a/debugging.html b/debugging.html index faab7e8..2d36af0 100644 --- a/debugging.html +++ b/debugging.html @@ -225,7 +225,7 @@ - +

Debugging packet delivery

The event log records a cause for every reception and every failure, so the question has an answer rather than a theory.

The workflow is a loop: a packet failed, find it on the timeline, read its cause, follow the cause to the panel that explains it, change the one variable it points at, and rerun. Because runs are deterministic, the rerun is an answer: same seed, same scenario, only your change differs.

diff --git a/emulation.html b/emulation.html index bd36db9..9b11db1 100644 --- a/emulation.html +++ b/emulation.html @@ -225,7 +225,7 @@ - +

Emulating a board

Emulation runs the published firmware image - the same file that is flashed onto hardware - on a model of the chip it was built for, against MeshBench's radio channel.

It answers a question the native path cannot: does this release come up on this board, configure itself, and put a correctly formed packet on the air.

diff --git a/experiments.html b/experiments.html index a832ac2..71c6c7b 100644 --- a/experiments.html +++ b/experiments.html @@ -225,7 +225,7 @@ - +

Running an experiment

An experiment starts as a question: does my branch relay more than dev on this network, does raising the hop cap buy delivery or just airtime. The machinery exists to answer it credibly: a control arm as the baseline, one variable changed per arm, several seeds per arm, and the difference read against the control's own spread rather than against hope.

A sweep is a matrix, not an A/B: arms crossed with seeds, run one at a time against the same network, with the results tabulated side by side.

diff --git a/firmware-development.html b/firmware-development.html index 4f78228..7b7e9db 100644 --- a/firmware-development.html +++ b/firmware-development.html @@ -225,7 +225,7 @@ - +

Firmware development

Point MeshBench at a MeshCore checkout. It builds it, loads it, and assigns it to every node of that role. Nothing is added to your checkout and nothing in it is modified.

meshbench dev -from ~/src/MeshCore
diff --git a/firmware-integration.html b/firmware-integration.html index fa3ce65..b128026 100644 --- a/firmware-integration.html +++ b/firmware-integration.html @@ -225,7 +225,7 @@ - +

Running real firmware

Every node in a MeshBench scenario runs an actual MeshCore build. Not a model of one, not a re-implementation of the protocol: the same source, compiled, running as a process, keeping its own state on disk.

There are two ways to do that, and they answer different questions.

diff --git a/firmware-library.html b/firmware-library.html index db49e08..ea06819 100644 --- a/firmware-library.html +++ b/firmware-library.html @@ -225,7 +225,7 @@ - +

The firmware library

Every build MeshBench knows about, whether it is on this machine or merely published, and what each node is running.

The firmware library: every build, its role, size and how many nodes run it

diff --git a/first-simulation.html b/first-simulation.html index c86041d..c16fd88 100644 --- a/first-simulation.html +++ b/first-simulation.html @@ -225,7 +225,7 @@ - +

Your first simulation

Fifteen minutes from an empty workbench to a real 378-node network relaying real packets through real firmware. You will load a shipped snapshot of ScotMesh, the Scottish community mesh, boot MeshCore on every node, and watch a message flood across Scotland.

Run it#

diff --git a/fixtures.html b/fixtures.html index b77fc1b..8933a32 100644 --- a/fixtures.html +++ b/fixtures.html @@ -225,7 +225,7 @@ - +

Shipped networks

Real networks you can load and run without importing anything, built from live CoreScope with the transport regions the real nodes actually hold. A result on one of these is a result about a real topology rather than about a lattice.

fixture-scotland-ireland loaded: 378 nodes across Scotland and Ireland, links weighted by margin

diff --git a/gen.py b/gen.py index 4485179..7c65bcf 100644 --- a/gen.py +++ b/gen.py @@ -94,6 +94,7 @@ def check_synced(): ("SECTION", "Automate"), ("scripting.html", "Scripting a session"), ("cookbook.html", "Scripting cookbook"), + ("agent-skills.html", "Agent skills"), ("reference-cli.html", "CLI"), ("reference-control.html", "Control socket"), ("reference-python.html", "Python client"), diff --git a/getting-started.html b/getting-started.html index 2bf841a..f6fc64c 100644 --- a/getting-started.html +++ b/getting-started.html @@ -225,7 +225,7 @@ - +

Getting started

This page gets MeshBench installed and open. From there, your first simulation is fifteen minutes to a real network relaying real packets, and the concepts page explains what is real and what is modelled.

What you need#

diff --git a/golden-vectors.html b/golden-vectors.html index 7cac6ff..9cf8d2c 100644 --- a/golden-vectors.html +++ b/golden-vectors.html @@ -225,7 +225,7 @@ - +

Validated on the air

How MeshBench's LoRa coding chain was checked against a real SX1262 - not against a paper, not against another simulator, but against what an actual chip put on the air. This is the experiment behind the golden vectors in internal/rf/lora/testdata/, run on 18 August 2026, and the method is repeatable whenever the chain changes.

The problem it solves: the LoRa PHY's bit-level details - the sync word, the parity equations, the whitening sequence, the CRC - are not published by Semtech. Everything anyone implements comes from reverse-engineering literature, and the implementations in the wild disagree with each other on several of them. A simulator that is merely *self-consistent* decodes its own transmissions perfectly and proves nothing. The only referee is silicon.

diff --git a/importing.html b/importing.html index df1668d..d67cc77 100644 --- a/importing.html +++ b/importing.html @@ -225,7 +225,7 @@ - +

Importing a real network

A shipped network is a snapshot. To work on the network as it is today, import it from a live source.

A *region* is held by a node and decides what it relays; a *scope* rides a packet and decides who relays it. The distinction runs through every step below, and the concepts page has the full table.

diff --git a/index.html b/index.html index a5f66e1..4f58481 100644 --- a/index.html +++ b/index.html @@ -225,7 +225,7 @@ - +

MeshBench

MeshBench runs real MeshCore firmware. MeshBench models the air.

MeshCore is the open-source LoRa mesh firmware; MeshBench is the instrument it is measured with: an RF-accurate network simulator. It runs real MeshCore firmware against a modelled radio channel, so the question it answers is not "would a packet get through" but "what actually arrived at the antenna, and why".

diff --git a/native-vs-emulated.html b/native-vs-emulated.html index 6b48eda..a4eabf1 100644 --- a/native-vs-emulated.html +++ b/native-vs-emulated.html @@ -225,7 +225,7 @@ - +

Native and emulated firmware

MeshBench runs the real MeshCore application on every node. There are two ways it can do that, and choosing between them is the first decision in any piece of work.

diff --git a/pages/agent-skills.md b/pages/agent-skills.md new file mode 100644 index 0000000..2ea34a4 --- /dev/null +++ b/pages/agent-skills.md @@ -0,0 +1,156 @@ +# Agent skills + +A coding agent pointed at MeshBench can read the verb list and the client +references on this site, and from those it can work out what it is allowed to +call. What it cannot work out is which calls fail quietly: which wait has a +false premise, which reply is a refusal wearing the shape of a success, which +step of an import decides whether the mesh relays anything at all. That is the +knowledge MeshBench ships as agent skills. + +Three skills are maintained beside the code, in `.claude/skills/` in the +MeshBench repository, and mirrored into two standalone repositories so they can +be installed by an agent working somewhere else. + +## What a skill is + +A skill is a directory holding a `SKILL.md`: YAML front matter carrying a +`name` and a `description`, then Markdown instructions. The agent loads only +the description at startup, which is enough to decide whether the skill is +relevant, and reads the body only when a task matches it. Many skills therefore +cost almost nothing until one of them is needed. + +The format is the [Agent Skills](https://agentskills.io) open standard, which +several agents and editors read. A skill is plain Markdown in a folder, under +version control, with no runtime and nothing to install. + +## The three skills + +| Skill | Loads when | Repository | +|---|---|---| +| `meshcoresim` | driving the simulator to answer an RF or mesh question: link viability, coverage, why a packet missed, a firmware A/B | [`meshbench-scripting-skills`](https://github.com/MeshBench/meshbench-scripting-skills) | +| `meshbench-scripting` | writing or debugging a script that opens a session, brings a mesh up and waits for it | [`meshbench-scripting-skills`](https://github.com/MeshBench/meshbench-scripting-skills) | +| `wb2-design-language` | building or changing a Gio panel, control, menu or map drawing | [`meshbench-dev-skills`](https://github.com/MeshBench/meshbench-dev-skills) | + +The split is by audience. The first two are for using MeshBench from outside, +and are useful to anyone driving a workbench. The third is for changing +MeshBench itself, and is only useful inside a checkout. + +**`meshcoresim`** carries the order in which a scenario has to be built, the +region inference step that decides whether anything relays, the two spellings a +region has and which one goes on the wire, and the honesty rules a result is +held to: both directions, no verdict on an uncertain position, one run is not +evidence, quote the provenance. It also carries what the simulator does not +model, because the omissions all bias the same way and a number quoted without +that is a claim the model cannot support. + +**`meshbench-scripting`** carries the failure modes of a driven session: which +waits have premises that do not hold, why `sim.start` is not the way a script +starts a run, which of the two consoles a node kind actually reads, and how a +refusal reaches a caller. It is written from faults found by running the +shipped examples end to end rather than from the API surface. + +**`wb2-design-language`** carries the interface decisions: that colour and size +come from one theme package, that panels fire verbs rather than mutating state, +that a long operation announces itself, and that nothing counts as done until +it has been seen running. + +The register differs from this site's. A skill states a rule with the reason +attached, in the voice of something learned rather than something specified, +because an agent that knows why a rule exists applies it to the case the rule +did not anticipate. + +## Installing + +Every agent below reads the same `SKILL.md`. What differs is only the directory +it looks in. + +```console +git clone https://github.com/MeshBench/meshbench-scripting-skills +git clone https://github.com/MeshBench/meshbench-dev-skills +``` + +Then copy the skill directories from `skills/` into whichever of these the +agent reads. A project directory makes the skill available to anyone who +clones the project; a user directory makes it available in every project. + +| Agent | Project directory | User directory | +|---|---|---| +| Claude Code | `.claude/skills/` | `~/.claude/skills/` | +| VS Code and GitHub Copilot | `.github/skills/`, `.claude/skills/` or `.agents/skills/` | `~/.copilot/skills/`, `~/.claude/skills/` or `~/.agents/skills/` | +| Cursor | `.cursor/skills/` or `.agents/skills/` | `~/.cursor/skills/` or `~/.agents/skills/` | +| Gemini CLI | `.gemini/skills/` or `.agents/skills/` | `~/.gemini/skills/` or `~/.agents/skills/` | +| Codex | `.agents/skills/`, from the working directory up to the repository root | `~/.agents/skills/` | + +**Only the Claude Code row is verified here**, because that is the agent the +MeshBench repository itself is set up for and the one the skills are exercised +under. Every other row is taken from that tool's own documentation, linked +under [further reading](#further-reading), and is stated rather than tested. +Check the tool's documentation before concluding a path is wrong. + +`.agents/skills/` is read by Cursor, Gemini CLI, Codex and VS Code, so one +directory covers several agents at once. A skill directory may be a symbolic +link to a checkout elsewhere on disk, which is the way to install once and +update with `git pull` rather than by copying again. + +```console +mkdir -p ~/.agents/skills +ln -s "$PWD/meshbench-scripting-skills/skills/meshbench-scripting" ~/.agents/skills/ +``` + +Claude Code, Cursor, Gemini CLI and Codex all resolve a symbolic link and read +`SKILL.md` from its target. + +### Agents that upload rather than read a directory + +Claude in the browser and desktop applications, and the Skills API, take a +skill as an upload rather than from a directory on disk. Those paths accept +only the front-matter fields in the Agent Skills specification: `name`, +`description`, `license`, `compatibility`, `metadata` and `allowed-tools`. Any +other field fails the upload rather than being ignored. MeshBench's skills +declare `name` and `description` and nothing else, so they upload unchanged. + +### Agents with no skill support + +An agent that has no skills mechanism can still be given the content: a +`SKILL.md` is Markdown, so its body pasted into that agent's rules or +instructions file works, at the cost of the progressive loading. The whole +skill then occupies context in every session rather than only in the sessions +that need it, which is the trade the skills format exists to avoid. + +## Using them + +A skill loads on its own when a task matches its description, so nothing has to +be typed to get the benefit. Asking an agent to write a script that brings a +mesh up should pull in `meshbench-scripting`; asking why a packet did not +arrive should pull in `meshcoresim`. Claude Code also invokes one directly by +name, as `/meshbench-scripting`. + +A skill is not a substitute for the references on this site. It says what an +agent would otherwise get wrong; the [control socket +reference](reference-control.html), the [Python](reference-python.html), +[Go](reference-go.html) and [Node](reference-js.html) client references and the +[cookbook](cookbook.html) say what the calls are. Both are wanted: an agent +with the verb list and no skill writes code that runs and quietly measures +nothing. + +## Keeping them true + +The copies in the MeshBench repository under `.claude/skills/` are canonical. +The two standalone repositories are mirrors, updated by copying, and a skill +that changes in one place is expected to change in both in the same piece of +work. Nothing enforces that, so a mirror can be behind: when the two disagree, +the copy beside the code is the one that is right. + +A skill that states something no longer true is worse than a skill that says +nothing, because it is acted on with confidence. That is why the skills name +the file or the command that settles each claim, and why counts that a script +generates are cited by their generator rather than written out as a number. + +## Further reading + +- [Agent Skills](https://agentskills.io), the format and its specification +- [Claude Code skills](https://code.claude.com/docs/en/skills) +- [Agent skills in VS Code](https://code.visualstudio.com/docs/copilot/customization/agent-skills) +- [Cursor skills](https://cursor.com/docs/context/skills) +- [Gemini CLI skills](https://geminicli.com/docs/cli/skills/) +- [Codex skills](https://developers.openai.com/codex/skills/) diff --git a/pages/quality-gates.md b/pages/quality-gates.md index 448bc47..b837b03 100644 --- a/pages/quality-gates.md +++ b/pages/quality-gates.md @@ -90,6 +90,9 @@ is the class of finding a per-file linter cannot produce. ## What only reports Some limits are stated but not enforced, and it is worth knowing which. +The house rules for the interface are one of them: they are carried as an +[agent skill](agent-skills.html) rather than as a check, because most of them +are about what a control means rather than about what compiles. Function length is a **soft** 50 lines and 256 functions exceed it; enforcing that would be inventing a rule rather than mechanising one. Nesting depth has diff --git a/pages/reference-js.md b/pages/reference-js.md index 1d15c93..ee17beb 100644 --- a/pages/reference-js.md +++ b/pages/reference-js.md @@ -18,55 +18,105 @@ Needs Node 18 or newer, and the `meshbench` binary on `PATH`. See It is the thinnest of the three - a connection, one `call` method, and the handshake - because Node scripting so far is the cookbook's `small-mesh` example -and little else. This page is its whole surface, generated from the module's own -JSDoc. +and little else. Where the Go and Python clients put a generated façade of typed +methods and closed enums over the socket, this one does not: a generated enum +buys compile-time safety that a client with no compiler cannot spend. Every verb +is still reachable, by name, through `call`; the +[control socket reference](reference-control.html) lists them. ## The surface -The whole Node client - one zero-dependency ES module, `pkg/client-js/meshbench.mjs`, on the same control socket as the Go and Python clients. Generated from its JSDoc. See [Scripting a session](scripting.html) and the [cookbook](cookbook.html) for how it is used. +Every export of the Node client - the classes with their constructors, properties and methods, the module functions and the constants - lifted from the JSDoc in `pkg/client-js/meshbench.mjs`, so it cannot drift from the module a script imports. Start at [Scripting a session](scripting.html) for how a session is opened, and the [cookbook](cookbook.html) for whole programs. -**Classes** · [WorkbenchError](#workbencherror) · [Workbench](#workbench) +**The workbench** · [Workbench](#workbench) -## WorkbenchError +**Errors** · [WorkbenchError](#workbencherror) -Extends `Error`. +**Module functions** · [defaultAddress](#defaultaddress) -A verb the workbench refused, carrying its classification so a caller can tell "no such node" from "the workbench is closing" without matching prose. +**Constants** · [PROTOCOL](#protocol) · [DEFAULT_CALL_TIMEOUT_MS](#default-call-timeout-ms) · [MAX_UNIX_PATH](#max-unix-path) ## Workbench One connection to a workbench, and the queue that keeps two callers from interleaving a half-frame on the wire. -### `static attach(opts = {})` +```js +new Workbench(sock, address, callTimeoutMs) +``` + +**Properties** + +- `address` + +### `static Workbench.attach(opts = {})` Open a connection to a running workbench, and do the protocol handshake before handing it back - a workbench speaking a version this client does not understand is refused here, not on whichever call happens to notice. -### `call(verb, params, timeoutMs)` +### `Workbench.call(verb, params, timeoutMs)` Run one verb and return its result. Rejects if the workbench has not answered within `timeoutMs` - the default is `DEFAULT_CALL_TIMEOUT_MS`, set at `attach()` via `callTimeoutMs`, the same length Python's socket timeout defaults to. Pass `null` to wait indefinitely for a call known to take a while. -### `async hello()` +### `async Workbench.hello()` Ask the workbench what it is, and refuse a protocol this client does not speak. `attach()` calls this itself before handing back a connection, so calling it again is only useful to re-check. -### `close()` +### `Workbench.close()` Close the connection. Any calls still in flight reject. +## Errors + +### WorkbenchError + +Extends `Error`. + +A verb the workbench refused, carrying its classification so a caller can tell "no such node" from "the workbench is closing" without matching prose. + +```js +new WorkbenchError(message, code) +``` + +**Properties** + +- `name` +- `code` + ## Module functions -### `defaultAddress()` +### `defaultAddress` + +```js +defaultAddress() +``` Where a workbench answers on this operating system unless told otherwise. Matches the Go and Python clients exactly, because the choice is by OS, not by language: all three must name the same address on one machine. ## Constants -- `PROTOCOL = 1` - The wire version this client speaks. A workbench answering anything else is refused rather than failing halfway through a script. +### `PROTOCOL` + +```js +export const PROTOCOL = 1; +``` + +The wire version this client speaks. A workbench answering anything else is refused rather than failing halfway through a script. -- `DEFAULT_CALL_TIMEOUT_MS = 300000` - How long a call waits for a reply before it gives up, unless a caller says otherwise. Matches the Python client's socket timeout, so a script ported between the two waits the same length of time before it hears about a verb the workbench never answered. +### `DEFAULT_CALL_TIMEOUT_MS` + +```js +export const DEFAULT_CALL_TIMEOUT_MS = 300000; +``` + +How long a call waits for a reply before it gives up, unless a caller says otherwise. Matches the Python client's socket timeout, so a script ported between the two waits the same length of time before it hears about a verb the workbench never answered. + +### `MAX_UNIX_PATH` + +```js +export const MAX_UNIX_PATH = 104; +``` -- `MAX_UNIX_PATH = 104` - The shortest sun_path any platform we run on allows: 108 on Linux, 104 on macOS and the BSDs. Matches the Go and Python clients exactly. +The shortest sun_path any platform we run on allows: 108 on Linux, 104 on macOS and the BSDs. Matches the Go and Python clients exactly. diff --git a/pages/repositories.md b/pages/repositories.md index dd1c627..2628af9 100644 --- a/pages/repositories.md +++ b/pages/repositories.md @@ -12,8 +12,8 @@ it is. | `MeshBench/meshcore-native` | host builds of MeshCore, the virtual SX1262, the bridge and `radioserver` | | `MeshBench/docs` | this site | | `MeshBench/meshbench-reports` | published studies | -| `MeshBench/meshbench-scripting-skills` | agent skills for driving and scripting a workbench | -| `MeshBench/meshbench-dev-skills` | agent skills for developing MeshBench | +| `MeshBench/meshbench-scripting-skills` | [agent skills](agent-skills.html) for driving and scripting a workbench | +| `MeshBench/meshbench-dev-skills` | [agent skills](agent-skills.html) for developing MeshBench | | `MeshBench/qemu` | QEMU with an SX1262 device | | `MeshBench/tlib` | the CPU library, with the SEVONPEND fix | | `MeshBench/renode-infrastructure` | the C# half of that fix | diff --git a/pages/scripting.md b/pages/scripting.md index f1dc9d9..a766875 100644 --- a/pages/scripting.md +++ b/pages/scripting.md @@ -258,3 +258,10 @@ Two further limits belong to scripting itself. A run is only reproducible against the same seed and the same scenario, so a comparison that varies the build must hold both. And a mesh brought up by a script is emulated one board at a time on an ordinary machine: several emulated boards at once will exhaust it. + +## Scripting with a coding agent + +The verb list says what can be called. It does not say which reply is a refusal +wearing the shape of a success, or which wait has a premise that does not hold. +MeshBench publishes that as [agent skills](agent-skills.html), which install +into Claude Code, Cursor, VS Code, Gemini CLI and Codex. diff --git a/quality-gates.html b/quality-gates.html index d4a14c0..fb1e53f 100644 --- a/quality-gates.html +++ b/quality-gates.html @@ -225,7 +225,7 @@ - +

What the build enforces

MeshBench's limits are mechanical rather than advisory, because taste does not survive scale. This is what actually fails a build, what only reports, and why each one exists.

@@ -261,7 +261,7 @@

SonarQubeWhat only reports#

-

Some limits are stated but not enforced, and it is worth knowing which.

+

Some limits are stated but not enforced, and it is worth knowing which. The house rules for the interface are one of them: they are carried as an agent skill rather than as a check, because most of them are about what a control means rather than about what compiles.

Function length is a soft 50 lines and 256 functions exceed it; enforcing that would be inventing a rule rather than mechanising one. Nesting depth has partial cover from the linter set. One type per panel file has four known violations and wants those fixed before it can be a gate.

The release pipeline is checked separately, and less#

package.yml runs only on a tag push or a manual dispatch. Nothing in a pull request exercises it, which is worth stating plainly: a breakage in it sits invisible behind green pull-request checks until somebody dispatches it deliberately.

diff --git a/reference-cli.html b/reference-cli.html index 7283ca9..5ca2899 100644 --- a/reference-cli.html +++ b/reference-cli.html @@ -225,7 +225,7 @@ - +

CLI reference

workbench opens the desktop application. Every other command is headless, and that split is deliberate and permanent: the headless path is what scripted runs, regression suites and the MCP server are built on, not a stopgap.

Nothing but workbench needs a GPU, a display, or anything running anywhere else.

@@ -250,7 +250,7 @@

firmware
flagmeaning
-get <name>download an image by name, e.g. RAK_4631/repeater
-import <path>import your own .uf2, .bin or .elf
-board, -role, -labelwhat an imported build is for, and what to call it
-offlinelist and use only what is already downloaded
-board <b>filter the listing

Versions are per role: a repeater release and a companion release are different artefacts, and a bare version string resolves nothing. An import with no -label is stamped with the time, so importing twice gives two builds rather than one quietly replacing the other.

-
+
MeshBench documentation. Built from the running application, not from mock-ups. Screenshots are window-only captures; see CLAUDE.md for the rule that keeps them current. Edit this page.
diff --git a/reference-control.html b/reference-control.html index b19018c..0fdb7be 100644 --- a/reference-control.html +++ b/reference-control.html @@ -225,7 +225,7 @@ - +

Control socket reference

The application listens on $XDG_RUNTIME_DIR/meshbench.sock, newline delimited JSON:

{"id":1,"method":"session.describe","params":{}}
diff --git a/reference-go.html b/reference-go.html index 890cfbf..6a008c6 100644 --- a/reference-go.html +++ b/reference-go.html @@ -225,7 +225,7 @@ - +

Go client reference

The Go client is pkg/client-go/meshbench in the meshbench repository:

go get github.com/MeshBench/meshbench
diff --git a/reference-js.html b/reference-js.html index 167f073..aa3ba23 100644 --- a/reference-js.html +++ b/reference-js.html @@ -225,41 +225,58 @@ - +

Node client reference

The Node client is pkg/client-js/meshbench.mjs in the meshbench repository: one zero-dependency ES module on the same control socket as the Go and Python clients:

npm install @meshbench/client
import { Workbench } from class="tok-str">"@meshbench/client";

Needs Node 18 or newer, and the meshbench binary on PATH. See Installing a client.

-

It is the thinnest of the three - a connection, one call method, and the handshake - because Node scripting so far is the cookbook's small-mesh example and little else. This page is its whole surface, generated from the module's own JSDoc.

+

It is the thinnest of the three - a connection, one call method, and the handshake - because Node scripting so far is the cookbook's small-mesh example and little else. Where the Go and Python clients put a generated façade of typed methods and closed enums over the socket, this one does not: a generated enum buys compile-time safety that a client with no compiler cannot spend. Every verb is still reachable, by name, through call; the control socket reference lists them.

The surface#

-

The whole Node client - one zero-dependency ES module, pkg/client-js/meshbench.mjs, on the same control socket as the Go and Python clients. Generated from its JSDoc. See Scripting a session and the cookbook for how it is used.

-

Classes · WorkbenchError · Workbench

-

WorkbenchError#

-

Extends Error.

-

A verb the workbench refused, carrying its classification so a caller can tell "no such node" from "the workbench is closing" without matching prose.

+

Every export of the Node client - the classes with their constructors, properties and methods, the module functions and the constants - lifted from the JSDoc in pkg/client-js/meshbench.mjs, so it cannot drift from the module a script imports. Start at Scripting a session for how a session is opened, and the cookbook for whole programs.

+

The workbench · Workbench

+

Errors · WorkbenchError

+

Module functions · defaultAddress

+

Constants · PROTOCOL · DEFAULT_CALL_TIMEOUT_MS · MAX_UNIX_PATH

Workbench#

One connection to a workbench, and the queue that keeps two callers from interleaving a half-frame on the wire.

-

static attach(opts = {})#

+
new Workbench(sock, address, callTimeoutMs)
+

Properties

+
  • address
+

static Workbench.attach(opts = {})#

Open a connection to a running workbench, and do the protocol handshake before handing it back - a workbench speaking a version this client does not understand is refused here, not on whichever call happens to notice.

-

call(verb, params, timeoutMs)#

+

Workbench.call(verb, params, timeoutMs)#

Run one verb and return its result. Rejects if the workbench has not answered within timeoutMs - the default is DEFAULT_CALL_TIMEOUT_MS, set at attach() via callTimeoutMs, the same length Python's socket timeout defaults to. Pass null to wait indefinitely for a call known to take a while.

-

async hello()#

+

async Workbench.hello()#

Ask the workbench what it is, and refuse a protocol this client does not speak. attach() calls this itself before handing back a connection, so calling it again is only useful to re-check.

-

close()#

+

Workbench.close()#

Close the connection. Any calls still in flight reject.

+

Errors#

+

WorkbenchError#

+

Extends Error.

+

A verb the workbench refused, carrying its classification so a caller can tell "no such node" from "the workbench is closing" without matching prose.

+
new WorkbenchError(message, code)
+

Properties

+
  • name
  • code

Module functions#

-

defaultAddress()#

+

defaultAddress#

+
defaultAddress()

Where a workbench answers on this operating system unless told otherwise. Matches the Go and Python clients exactly, because the choice is by OS, not by language: all three must name the same address on one machine.

Constants#

-
  • PROTOCOL = 1 - The wire version this client speaks. A workbench answering anything else is refused rather than failing halfway through a script.
-
  • DEFAULT_CALL_TIMEOUT_MS = 300000 - How long a call waits for a reply before it gives up, unless a caller says otherwise. Matches the Python client's socket timeout, so a script ported between the two waits the same length of time before it hears about a verb the workbench never answered.
-
  • MAX_UNIX_PATH = 104 - The shortest sun_path any platform we run on allows: 108 on Linux, 104 on macOS and the BSDs. Matches the Go and Python clients exactly.
+

PROTOCOL#

+
export const PROTOCOL = class="tok-num">1;
+

The wire version this client speaks. A workbench answering anything else is refused rather than failing halfway through a script.

+

DEFAULT_CALL_TIMEOUT_MS#

+
export const DEFAULT_CALL_TIMEOUT_MS = class="tok-num">300000;
+

How long a call waits for a reply before it gives up, unless a caller says otherwise. Matches the Python client's socket timeout, so a script ported between the two waits the same length of time before it hears about a verb the workbench never answered.

+

MAX_UNIX_PATH#

+
export const MAX_UNIX_PATH = class="tok-num">104;
+

The shortest sun_path any platform we run on allows: 108 on Linux, 104 on macOS and the BSDs. Matches the Go and Python clients exactly.

MeshBench documentation. Built from the running application, not from mock-ups. Screenshots are window-only captures; see CLAUDE.md for the rule that keeps them current. Edit this page.
-