Skip to content
Open
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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## Unreleased

- Skill packs are host-universal: standard SKILL.md frontmatter (the
`compatibility: opencode` field is removed from universal packs;
`customize-labwired-agent` remains opencode-only).
- New `harness-portable` lane simulates a foreign install and a universality
gate in `skills-verify-all` prevents host lock-in regressions.
- Docs: "Use with other agents" (skills CLI + AGENTS.md + MCP snippet).

## 0.3.11 — 2026-08-12 — Tighten release paths

- Doctor only reports **signed in** after live hosted probe succeeds (no false green).
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ installations and data stay in place.
- [Embedder parity](docs/EMBEDDER_PARITY.md)
- [Security](docs/SECURITY.md)
- [Self-host / airgap](docs/SELF_HOST.md)
- [Use with other agents](docs/INSTALL.md#use-with-other-agents)


## Skills (domain packs)
Expand Down
2 changes: 2 additions & 0 deletions config/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ They **do not** mint `model_verified` and **do not** replace knowledge MCP.
`list` / `describe` → **`labwired_part`** → **`labwired_datasheet`**. Never invent.
Contract: `docs/KNOWLEDGE.md`.

Internal docs (specs, plans, product docs, QA reports) live in the private `LabWired/agent-internal` repository, not in this repository.

## Tool allowlist

### MCP tools (agent may call)
Expand Down
5 changes: 5 additions & 0 deletions docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ This test uses a temporary install prefix. It may download tools.

## Run a live twin test

The gate scripts (`scripts/live-gate1.sh`, `scripts/import-multi-smoke.sh`,
`scripts/ship-gate.sh`) run from a source checkout of the repository, not
from the installed npm package. They need `tests/` and `fixtures/`, which
the package does not ship.

```bash
bash scripts/live-gate1.sh
```
Expand Down
84 changes: 84 additions & 0 deletions docs/INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,87 @@ unknown files in the LabWired directory.
`PATH`. Add that directory manually if the command is still unavailable.
- Run `labwired agent doctor` and follow the first reported error.
- Run the install command again to repair or refresh the Agent.

## Use with other agents

The LabWired harness is a standard Agent Skills pack (SKILL.md format). Any
agent host that understands the format can load it.

### Skills

With the Vercel skills CLI (supports 40+ agents; needs Node.js 22.20 or
later):

```bash
npx skills add LabWired/agent
```

Or copy the `skills/` directory into your host's skills directory by hand;
that also copies `customize-labwired-agent`, which you can skip.

`customize-labwired-agent` documents the LabWired Agent's own runtime
configuration and is not useful to other hosts.

The `observe` and `desk-hw` packs additionally need the LabWired Agent CLI
installed (`labwired agent …` on PATH). Every pack's guidance loads in any
host, but firmware tool calls need either the hosted MCP endpoint (after
`labwired agent login`) or the `labwired` CLI on PATH (local MCP, see
below). The develop workflow's first tool call, `labwired_context`, is
served only by the hosted endpoint.

### Instructions

`config/AGENTS.md` is host-agnostic. Use it as your host's instruction file
(AGENTS.md, CLAUDE.md, or equivalent), or merge its claim-vocabulary section
into your existing one: twin verification comes only from `labwired_verify`,
desk hardware claims only from physical hardware with independently captured
evidence, and a build is never a behavior proof. The exact evidence statuses
are defined in `config/AGENTS.md` and [docs/VERIFY.md](VERIFY.md).

### MCP server

Firmware tooling is served over MCP, in two variants.

**Hosted (full tool surface).** Compile, twin verification, and the
knowledge tools (`labwired_context`, `labwired_part`, `labwired_datasheet`,
`labwired_import`) are served only by the hosted endpoint. Install the
LabWired Agent, run `labwired agent login`, then point your host at the
hosted endpoint:

```json
{
"mcpServers": {
"labwired": {
"url": "https://api.labwired.com/mcp?toolNames=unprefixed",
"headers": { "Authorization": "Bearer <token from labwired agent login>" }
}
}
}
```

Hosts differ in envelope: Claude Code expects `"type": "http"` on the entry,
and opencode uses its own top-level `mcp` key with the same URL. The token is
read from the session file at `~/.labwired/session/cloud.json` after
`labwired agent login`. It expires after about an hour, and a foreign host
cannot refresh it; re-run the LabWired Agent to renew.

**Local (artifact tools only).** This variant serves the artifact tools
(`labwired_search`, `labwired_list`, `labwired_describe`,
`labwired_validate`, `labwired_export`, `labwired_run`, `labwired_inspect`,
`labwired_verify`, `labwired_fuzz`, `labwired_ingest_svd`,
`labwired_validate_device`). It shells out to the `labwired` CLI, so the
LabWired CLI must be on PATH. It does not serve compile or knowledge tools.

```json
{
"mcpServers": {
"labwired": {
"command": "npx",
"args": ["-y", "@labwired/mcp"]
}
}
}
```

Hosts without MCP support can still use the skills and instructions, but no
firmware tool calls are possible there.
3 changes: 3 additions & 0 deletions docs/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ npx --yes node@18.0.0 tests/hardware-runner.test.mjs
- `run-bounded.sh` checks the bounded-run helper itself.
- Skill tests check the installed skill set and instructions
(`skills-inventory.sh`, `skills-verify-all.sh`, `develop-skill.sh`).
`skills-verify-all.sh` also gates pack universality (no host-locked
frontmatter or opencode-only references in universal packs), and
`harness-portable.sh` simulates a foreign install of the universal pack.
- Develop lanes check the `develop` workflow: `develop-acceptance-smoke.sh`
(npm `test:develop:acceptance`) and the grounded hosted-agent certification
`develop-agent-e2e.sh` (npm `test:develop:agent`; `test:develop:release`
Expand Down
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,8 @@
"docs/KNOWLEDGE.md",
"docs/GOLDEN_PATH.md",
"scripts/ship-gate.sh",
"scripts/live-gate1.sh",
"scripts/knowledge-mcp-smoke.sh",
"scripts/import-diagram-smoke.sh",
"scripts/import-multi-smoke.sh",
"scripts/desk-hw-smoke.sh",
"scripts/knowledge-top-parts.py",
"docs/LEGAL.md",
Expand Down
8 changes: 5 additions & 3 deletions scripts/ship-gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,11 @@ else
bad "golden-path pack"
fi
fi
if run_stage "develop-default" "$OUT/develop-default.txt" grep -qi \
'develop first\|load \`develop\` first\|START with skill develop' \
"$ROOT/config/AGENTS.md" "$ROOT/config/opencode.hosted.json"; then
if run_stage "develop-default" "$OUT/develop-default.txt" bash -c '
for f in "$@"; do
grep -qi '"'"'develop first\|load \`develop\` first\|START with skill develop'"'"' "$f" || exit 1
done
' _ "$ROOT/config/AGENTS.md" "$ROOT/config/opencode.hosted.json"; then
pass "AGENTS/opencode default develop first"
else
if [[ "$stage_timed_out" -eq 0 ]]; then
Expand Down
1 change: 0 additions & 1 deletion skills/bringup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ description: >-
(list/describe/part, then datasheet tool for grounded text). Never invent
pinouts or I2C addresses.
license: MIT
compatibility: opencode
metadata:
gate: "workflow"
labwired: "true"
Expand Down
1 change: 0 additions & 1 deletion skills/desk-hw/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ description: >-
Plan and execute confirmed physical hardware runs with explicit identities,
safe wiring, exact flash, and behavior-specific evidence. Prefer twin prove first.
license: MIT
compatibility: opencode
metadata:
gate: "workflow"
labwired: "true"
Expand Down
1 change: 0 additions & 1 deletion skills/develop/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ name: develop
description: >-
Default firmware workflow for greenfield and existing projects: inspect, ground hardware facts, edit, compile, check on the twin, repair, and report.
license: MIT
compatibility: opencode
metadata:
gate: "workflow"
labwired: "true"
Expand Down
1 change: 0 additions & 1 deletion skills/golden-path/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ description: >-
First-session guide that delegates firmware development to develop, with
optional circuit import, requested plots, and physical-board evidence.
license: MIT
compatibility: opencode
metadata:
gate: "1"
labwired: "true"
Expand Down
1 change: 0 additions & 1 deletion skills/import-circuit/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ description: >-
Ingest customer circuit sources (PDF schematic, KiCad, netlist, BOM, image,
diagram.json) into a catalog-honest LabWired twin diagram. Never invent pins.
license: MIT
compatibility: opencode
metadata:
gate: "workflow"
labwired: "true"
Expand Down
1 change: 0 additions & 1 deletion skills/observe/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ description: >-
One job: ask → recipe → compose → present. Never invent series; never claim
twin or desk green from a plot.
license: MIT
compatibility: opencode
metadata:
gate: "1"
labwired: "true"
Expand Down
1 change: 0 additions & 1 deletion skills/prove/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ description: >-
evidence report. model_verified ONLY from labwired_verify. On red fail-first
then repair; never soft-pass or invent green.
license: MIT
compatibility: opencode
metadata:
gate: "1"
labwired: "true"
Expand Down
1 change: 0 additions & 1 deletion skills/using-superpowers/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ description: >-
process skills (TDD, plans, debug method) AND LabWired domain packs. LabWired
claim rules and MCP tools always win over generic process advice.
license: MIT
compatibility: opencode
metadata:
gate: "process"
labwired: "true"
Expand Down
1 change: 1 addition & 0 deletions tests/all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ run "harness" "$ROOT/tests/harness.sh"
run "run-bounded" "$ROOT/tests/run-bounded.sh"
run "skills-inventory" "$ROOT/tests/skills-inventory.sh"
run "skills-verify-all" "$ROOT/tests/skills-verify-all.sh"
run "harness-portable" "$ROOT/tests/harness-portable.sh"
run "develop-skill" "$ROOT/tests/develop-skill.sh"
run_hermetic "develop-acceptance-smoke" bash "$ROOT/tests/develop-acceptance-smoke.sh"
run "hosted-config" "$ROOT/tests/hosted-config.sh"
Expand Down
67 changes: 67 additions & 0 deletions tests/harness-portable.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env bash
# Foreign-install simulation: the universal pack must be self-contained when
# copied into a bare directory with no opencode config (what `npx skills add`
# effectively does for a foreign agent host).
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
fail=0
pass() { echo "ok $*"; }
bad() { echo "FAIL $*"; fail=1; }

# shellcheck source=lib/pack-classification.sh
source "$ROOT/tests/lib/pack-classification.sh"

TMP="$(mktemp -d "${TMPDIR:-/tmp}/labwired-harness-portable.XXXXXX")"
trap 'rm -rf "$TMP"' EXIT

for s in "${UNIVERSAL_PACKS[@]}"; do
cp -R "$ROOT/skills/$s" "$TMP/$s"
done

# 1. opencode-only packs must not be in the universal set (disjoint lists).
for s in "${OPENCODE_ONLY_PACKS[@]}"; do
if [[ -d "$TMP/$s" ]]; then
bad "opencode-only pack leaked into universal set: $s"
else
pass "opencode-only excluded: $s"
fi
done

# 2. Every copied SKILL.md has a frontmatter block with name + description.
for s in "${UNIVERSAL_PACKS[@]}"; do
f="$TMP/$s/SKILL.md"
[[ -f "$f" ]] || { bad "portable $s missing SKILL.md"; continue; }
frontmatter="$(awk '/^---$/{c++; next} c==1' "$f")"
if grep -q '^name:' <<<"$frontmatter" && grep -q '^description:' <<<"$frontmatter"; then
pass "portable $s frontmatter name+description"
else
bad "portable $s frontmatter missing name/description"
fi
done

# 3. No symlink escapes the copied tree (a foreign host receives plain files).
escapes="$(find "$TMP" -type l | while read -r l; do
target="$(readlink "$l")"
case "$target" in (/*|*..*) echo "$l" ;; esac
done)"
if [[ -z "$escapes" ]]; then
pass "no escaping symlinks"
else
bad "escaping symlinks: $escapes"
fi

# 4. Universal + opencode-only lists together equal the shipped set.
shipped="$(find "$ROOT/skills" -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | sort)"
classified="$(printf '%s\n' "${UNIVERSAL_PACKS[@]}" "${OPENCODE_ONLY_PACKS[@]}" | sort)"
if [[ "$shipped" == "$classified" ]]; then
pass "classification partitions shipped set"
else
bad "classification mismatch: $(diff <(echo "$shipped") <(echo "$classified") | head -10)"
fi

if [[ "$fail" -ne 0 ]]; then
echo "harness-portable FAILED"
exit 1
fi
echo "ok harness-portable PASS"
exit 0
16 changes: 16 additions & 0 deletions tests/lib/pack-classification.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# shellcheck shell=bash
# Canonical pack classification for the labwired harness.
# Sourced by tests/skills-inventory.sh and tests/harness-portable.sh.
# UNIVERSAL_PACKS: SKILL.md-standard packs installable into any agent host.
# OPENCODE_ONLY_PACKS: packs about the opencode config surface; never
# installed by a universal/foreign path.
UNIVERSAL_PACKS=(
golden-path develop bringup prove observe desk-hw import-circuit
using-superpowers brainstorming test-driven-development systematic-debugging
verification-before-completion writing-plans executing-plans writing-skills
dispatching-parallel-agents subagent-driven-development requesting-code-review
receiving-code-review finishing-a-development-branch using-git-worktrees
)
OPENCODE_ONLY_PACKS=(
customize-labwired-agent
)
33 changes: 33 additions & 0 deletions tests/skills-inventory.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,39 @@ set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
fail=0

# shellcheck source=lib/pack-classification.sh
source "$ROOT/tests/lib/pack-classification.sh"

# Pack classification must partition the shipped skills exactly.
for s in "${UNIVERSAL_PACKS[@]}" "${OPENCODE_ONLY_PACKS[@]}"; do
if [[ -f "$ROOT/skills/$s/SKILL.md" ]]; then
echo "ok classified $s"
else
echo "FAIL classified pack missing: $s"
fail=1
fi
done
for d in "$ROOT"/skills/*/; do
s="$(basename "$d")"
found=0
for p in "${UNIVERSAL_PACKS[@]}"; do [[ "$p" == "$s" ]] && found=1 && break; done
for p in "${OPENCODE_ONLY_PACKS[@]}"; do [[ "$p" == "$s" ]] && found=2 && break; done
case "$found" in
1) echo "ok universal $s" ;;
2) echo "ok opencode-only $s" ;;
0) echo "FAIL unclassified skill dir: $s"; fail=1 ;;
esac
done

for p in "${UNIVERSAL_PACKS[@]}"; do
for q in "${OPENCODE_ONLY_PACKS[@]}"; do
if [[ "$p" == "$q" ]]; then
echo "FAIL pack in both lists: $p"
fail=1
fi
done
done

need_skill() {
local s="$1"
if [[ -f "$ROOT/skills/$s/SKILL.md" ]]; then
Expand Down
23 changes: 22 additions & 1 deletion tests/skills-verify-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,33 @@ deepinfra_base="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1])
[[ "$deepinfra_base" == "https://api.deepinfra.com/v1" ]] \
&& pass "DeepInfra OpenCode base URL" || bad "DeepInfra OpenCode base URL: $deepinfra_base"

# Universality gate: universal packs must stay host-neutral (SKILL.md standard).
# shellcheck source=lib/pack-classification.sh
source "$ROOT/tests/lib/pack-classification.sh"

for s in "${UNIVERSAL_PACKS[@]}"; do
f="$ROOT/skills/$s/SKILL.md"
[[ -f "$f" ]] || { bad "universal $s missing SKILL.md"; continue; }
frontmatter="$(awk '/^---$/{c++; next} c==1' "$f")"
if grep -qiE '^[[:space:]]*compatibility[[:space:]]*:' <<<"$frontmatter"; then
bad "universal $s has host-locked frontmatter (compatibility:)"
else
pass "universal $s frontmatter standard"
fi
if grep -rniE 'permission\.skill|default_agent|tui\.json|OPENCODE_CONFIG_DIR|\.config/labwired-agent' \
"$ROOT/skills/$s/" >/dev/null 2>&1; then
bad "universal $s references opencode-only mechanisms"
else
pass "universal $s host-neutral body"
fi
done

n=$(find "$ROOT/skills" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')
# 7 domain packs + customize-labwired-agent + 14 superpowers = 22
# golden-path is the entry router; develop is the firmware workflow it delegates
# to. Both ship — see config/AGENTS.md "Default loop".
if [[ "$n" -eq 22 ]]; then
pass "skill dir count $n (6 packs + customize + 14 superpowers)"
pass "skill dir count $n (7 packs + customize + 14 superpowers)"
else
bad "skill dir count $n expected 22"
fi
Expand Down