Rust TDD Workflow
rtk-ai/rtk
Enforces red-green-refactor for Rust work, with idiomatic test patterns, a naming convention and a pre-commit gate of cargo fmt, clippy and test.
Port a Node-RED node into EdgeLinkd the way this repo does it: implement the node in Rust under crates/core/src/runtime/nodes, mirror Node-RED's mocha spec as pytest tests under tests/, register the…
$ npx skills add oldrev/edgelinkd --skill port-node-red-node -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install oldrev/edgelinkd port-node-red-node --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/oldrev/edgelinkd.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/port-node-red-node .claude/skills/port-node-red-node && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "port-node-red-node" agent skill from https://github.com/oldrev/edgelinkd/tree/master/.agents/skills/port-node-red-node into .claude/skills/port-node-red-node/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "port-node-red-node", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/oldrev/edgelinkd/tree/master/.agents/skills/port-node-red-nodeType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add oldrev/edgelinkd --skill port-node-red-node -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install oldrev/edgelinkd port-node-red-node --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/oldrev/edgelinkd.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/port-node-red-node .agents/skills/port-node-red-node && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "port-node-red-node" agent skill from https://github.com/oldrev/edgelinkd/tree/master/.agents/skills/port-node-red-node into .agents/skills/port-node-red-node/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "port-node-red-node", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add oldrev/edgelinkd --skill port-node-red-node -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install oldrev/edgelinkd port-node-red-node --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/oldrev/edgelinkd.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/port-node-red-node .cursor/skills/port-node-red-node && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "port-node-red-node" agent skill from https://github.com/oldrev/edgelinkd/tree/master/.agents/skills/port-node-red-node into .cursor/skills/port-node-red-node/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "port-node-red-node", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/oldrev/edgelinkd.git --path .agents/skills/port-node-red-node--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add oldrev/edgelinkd --skill port-node-red-node -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install oldrev/edgelinkd port-node-red-node --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/oldrev/edgelinkd.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/port-node-red-node .gemini/skills/port-node-red-node && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "port-node-red-node" agent skill from https://github.com/oldrev/edgelinkd/tree/master/.agents/skills/port-node-red-node into .gemini/skills/port-node-red-node/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "port-node-red-node", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install oldrev/edgelinkd port-node-red-nodeInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add oldrev/edgelinkd --skill port-node-red-node -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/oldrev/edgelinkd.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/port-node-red-node .github/skills/port-node-red-node && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "port-node-red-node" agent skill from https://github.com/oldrev/edgelinkd/tree/master/.agents/skills/port-node-red-node into .github/skills/port-node-red-node/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "port-node-red-node", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add oldrev/edgelinkd --skill port-node-red-node -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install oldrev/edgelinkd port-node-red-node --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/oldrev/edgelinkd.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/port-node-red-node .opencode/skills/port-node-red-node && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "port-node-red-node" agent skill from https://github.com/oldrev/edgelinkd/tree/master/.agents/skills/port-node-red-node into .opencode/skills/port-node-red-node/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "port-node-red-node", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
port-node-red-nodePort a Node-RED node into EdgeLinkd the way this repo does it: implement the node in Rust under crates/core/src/runtime/nodes, mirror Node-RED's mocha spec as pytest tests under tests/, register the…
Port Node Red Node is an agent skill from oldrev/edgelinkd. Port a Node-RED node into EdgeLinkd the way this repo does it: implement the node in Rust under crates/core/src/runtime/nodes, mirror Node-RED's mocha spec as pytest tests under tests/, register the pair in scripts/specsdiff.json, then prove it with scripts/specsdiff.py. Also use it to finish, extend or debug an existing node, or to report which Node-RED spec tests are still missing.
Its SKILL.md is about 3k tokens, which your agent loads only when the skill is triggered. The skill folder holds 6 other files, including scripts and reference files (for example `references/python-spec-tests.md`, `references/rust-node-authoring.md` and `references/spec-coverage-audit.md`).
It sits in Testing & QA, covering Unit testing. It works with Rust and pytest. The repository describes itself as: Node-RED Reimplemented in Rust. The licence is Apache-2.0.
7 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit adde082. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Ships 1 file in scripts/ (Python), which the agent can run.
Shell commands in SKILL.md call:
cargopytestpythongitnpmpipFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use git, npm and pip, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Port Node Red Node loads about 3k tokens when it runs, and up to ~11k if it reads all its reference files. Until then it costs about 102 tokens; SKILL.md has 1,241 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); the scripts in this folder are not scanned.
The full file from oldrev/edgelinkd at commit adde082, republished under its Apache-2.0 licence (© oldrev). 1,241 words, ~2,978 tokens.
.claude/skills/port-node-red-node/SKILL.md (or your agent's skills folder). This skill also uses 4 other files; get the full folder from GitHub.EdgeLinkd re-implements Node-RED nodes in Rust, and mirrors Node-RED's own mocha spec suite as pytest tests. For the behaviour we support, a node is only done when all three artifacts exist and the coverage checker agrees:
| # | Artifact | Location |
|---|---|---|
| 1 | Rust node implementation (self-registering) | crates/core/src/runtime/nodes/<category>/<name>.rs |
| 2 | Ported spec tests (one pytest test per upstream it(), skipped with a reason when out of scope) | tests/nodes/<category>/test_<name>_node.py |
| 3 | Audit entry mapping the two | scripts/specs_diff.json |
EdgeLinkd is embedded-first, so a node may deliberately support only part of its upstream
behaviour. Behaviour we support must match Node-RED exactly; out-of-scope behaviour is still
ported as a title and marked @pytest.mark.skip(reason=...) (see step 4). Never ship a
half-working option that looks supported — see the design philosophy in AGENTS.md.
Verification is the [✓] "<name>" (n/n) line for the node you touched in
python scripts/specs_diff.py <absolute path to 3rd-party/node-red>, plus
cargo fmt --check, clippy and the Rust tests. (The checker's own exit code is global —
the repo still has nodes with unported specs, so it can be non-zero even when your node is
complete.)
git submodule update --init --recursive # populates 3rd-party/node-red (v4.0.9)
(cd 3rd-party/node-red && npm install) # mocha, needed by scripts/specs_diff.py
pip install -r ./tests/requirements.txt # pytest, pytest-asyncio, pytest-it, pytest-json-report, ...
cargo build --all # also builds the edgelink_pymod Python extensionThe pytest suite does not run the edgelinkd binary: tests/__init__.py loads
target/<EDGELINK_BUILD_TARGET>/<EDGELINK_BUILD_PROFILE>/edgelink_pymod.{pyd,dll,so},
so those two env vars must match the way you built:
$env:EDGELINK_BUILD_TARGET=""; $env:EDGELINK_BUILD_PROFILE="debug" # default: target/debug
pytest ./tests/nodes/<category>/test_<name>_node.py -vCI uses cargo build --profile ci --workspace --features full with
EDGELINK_BUILD_PROFILE=ci; see .github/workflows/CICD.yml.
For a node whose spec lives in test/nodes/core/<nr_category>/<nn>-<name>_spec.js:
| Upstream file | Path (relative to 3rd-party/node-red) |
|---|---|
| JS implementation | packages/node_modules/@node-red/nodes/core/<nr_category>/<nn>-<name>.js |
| Behaviour + palette HTML | packages/node_modules/@node-red/nodes/core/<nr_category>/<nn>-<name>.html |
| mocha spec (the contract) | test/nodes/core/<nr_category>/<nn>-<name>_spec.js |
Read the spec first: its describe('...') and it('...') titles are the
acceptance criteria and their exact text must be reproduced in Python. Then read the
JS implementation for the behaviour (defaults, property names, error paths, edge cases).
The <name> part of the filename is usually the flow-JSON "type" you must register
in Rust (16-range.js → "type": "range"); check the .js RED.nodes.registerType(...)
call when it differs.
Full skeleton, APIs and conventions: references/rust-node-authoring.md.
crates/core/src/runtime/nodes/<category>/<name>.rs (use a directory with
mod.rs when the node is big, like function/, trigger/, switch/).mod.rs (mod <name>;, wrapped in #[cfg(feature = "...")]
if the category is feature-gated). Registration itself is automatic: the
#[flow_node] macro inventory::submit!s a MetaNode, and
RegistryBuilder::with_builtins() picks it up — there is no central node list.with_uow for the
receive→process→fan-out loop, fan_out_one/fan_out_many for output,
evaluate_node_property_value for typed properties, json::deser::* for config
fields, report_status/report_error for observability.crates/core/Cargo.toml when the
node needs one (see nodes_xml, nodes_mqtt, ...).Put them in the same file under #[cfg(test)] mod tests, driven by
build_test_engine(json!([...])) + engine.run_once_with_inject(...) /
run_once(...). Use #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
whenever the flow touches the JS function node or context.* (those paths use
block_in_place), plain #[tokio::test] otherwise. Details in the Rust reference.
Rust unit tests are for logic that is awkward to express through flow JSON. The spec port below is what proves Node-RED compatibility — do not skip it.
Full harness reference: references/python-spec-tests.md.
Create tests/nodes/<category>/test_<name>_node.py:
import pytest
from tests import *
@pytest.mark.describe('range Node') # == the JS describe(...) title
class TestRangeNode:
@pytest.mark.asyncio
@pytest.mark.it('ranges numbers up tenfold') # == the JS it(...) title, character for character
async def test_0001(self):
...Rules that the coverage checker enforces:
@pytest.mark.describe text and @pytest.mark.it text must be identical to the
JS ones (mocha fullTitle is describe + " " + it, and specs_diff.py compares
those strings, so a typo, a difference in spacing or a smart quote shows up as a gap).it(). Keep the upstream order and number the test
methods (test_0001, test_0002, ...) as the existing files do.it() gets a title, including the ones we do not support. Mark those
@pytest.mark.skip(reason="<feature> is out of scope: <why>"). specs_diff.py collects
with -p no:skip, so a skipped test still counts as covered — the reason= string is the
only written record of the gap, which is why it is mandatory and has to name the
unsupported feature. A skip never means "not fixed yet": never skip a spec for behaviour
we claim to support.pass body.Drive the node with the helpers from tests/__init__.py
(run_single_node_with_msgs_ntimes, run_with_single_node_ntimes,
run_flow_with_msgs_ntimes); they build the inject → node → test-once flow for you
and return the messages that reached the end of the flow.
scripts/specs_diff.jsonAdd [<display name>, "nodes/<category>/test_<name>_node.py", "test/nodes/core/<nr_category>/<nn>-<name>_spec.js"]
to the matching category (paths are relative to tests/ and to the Node-RED root).
cargo build --all
pytest ./tests/nodes/<category>/test_<name>_node.py -v
# authoritative coverage check for every registered node; writes the report and
# exits 0 only when the Python suite covers every upstream it().
# Use an ABSOLUTE Node-RED path: the script chdir()s into it before resolving specs.
python scripts/specs_diff.py "$PWD/3rd-party/node-red" -o tests/REDNODES-SPECS-DIFF.md
cargo fmt --check
cargo clippy --all-features --tests --all
cargo test -p edgelink-core # or: cargo test --workspace --features fullRead the checker's output for your node: [✓] "range" (13/13) means complete, [×]
plus - lines list the upstream tests you still owe. Triage rules, the JSON format
and the offline fallback script are in references/spec-coverage-audit.md.
Follow CONTRIBUTING.md: present tense, imperative, subject ≤ 72 characters, English.
Keep the Rust node and its spec port in the same commit, and never commit generated
artifacts (tests/REDNODES-SPECS-DIFF.md is regenerated by the script and is
tracked — refresh it deliberately only when it is part of the change you intend).
tests/REDNODES-SPECS-DIFF.md is produced by
specs_diff.py; regenerate, don't patch.it() in Python breaks coverage silently; the
checker only reports a -/+ pair, not a rename.describe blocks. Stack one @pytest.mark.describe per level, outer first,
or the fullTitle misses the prefix and the checker reports every test of that block as
missing. See references/python-spec-tests.md.Variant::Bytes has no JSON representation,
so binary-payload spec tests are unportable; skip them with
reason="binary payloads cannot cross the pytest bridge". nexpected=0
returns without running the flow, so "should emit nothing" cannot be observed that way.todo!(), or a fabricated value is worse than an honest gap, because the user
cannot tell the difference. When a code path reaches something we do not support, return
EdgelinkError::NotSupported (or a node error/status) — and port the corresponding spec as
a skip carrying the reason.ElementId is a u64 written in hex
(1..16 digits), so a copied id (n1, splitNode1) — and even a hex-encoded long name —
is rejected with "failed to parse ElementId". Convert with the harness helper red_id(),
which digests the name into 16 digits, together with every reference to it — z,
wires, scope, injection targets — or the flow silently splits into disconnected nodes
and the test just times out. See references/python-spec-tests.md.nexpected mismatch. The Python helpers wait for exactly nexpected messages and
then fail with a timeout; assert on fewer messages by splitting into several tests.mod declaration. The macro self-registers, but the file still has
to be compiled in via the category mod.rs; an unreferenced file is silently dead.red_name vs type. The first macro argument is the flows.json "type" and must
match Node-RED exactly, otherwise existing flows.json files won't bind to your node.[features] list in
crates/core/Cargo.toml and reachable from the app's default features, or the node
will be missing at runtime while everything still compiles.SyncWaitableFuture for the one sanctioned
exception used by the JS context bridge).| File | Contents |
|---|---|
references/rust-node-authoring.md | Rust node skeleton, runtime APIs, config/msg handling, error handling, unit tests |
references/python-spec-tests.md | pytest harness helpers, markers, title matching, running the suite |
references/spec-coverage-audit.md | specs_diff.json format, running/extracting the audit, triage, offline fallback |
scripts/spec-gaps.py | Fast offline approximation of the audit (no mocha/pytest needed) |
© oldrev, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 4 other files (scripts, references) in .agents/skills/port-node-red-node of oldrev/edgelinkd.
Open the folder on GitHubat commit adde082
Port Node Red Node next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Port Node Red Node this skilloldrev/edgelinkd | 121 | — | ~3k | Automated safety check: Pass | Apache-2.0 | |
| Rust TDD Workflowrtk-ai/rtk | 83k | — | ~753 | Automated safety check: Notes | Apache-2.0 | |
| Adk Verify Snippetsgoogle/adk-python | 22k | — | ~1.4k | Automated safety check: Pass | Apache-2.0 | |
| RTK Filter TDD in Rustrtk-ai/rtk | 83k | — | ~1.9k | Automated safety check: Notes | Apache-2.0 | |
| Hermetic Python Unit TestsdimensionalOS/dimos | 4.6k | — | ~1.4k | Automated safety check: Pass | Custom licence | |
| Test GuardamElnagdy/guard-skills | 1.3k | 2 repos | ~2.1k | Automated safety check: Pass | MIT |
rtk-ai/rtk
Enforces red-green-refactor for Rust work, with idiomatic test patterns, a naming convention and a pre-commit gate of cargo fmt, clippy and test.
google/adk-python
Checks that every Python code block in a Markdown file actually compiles and runs, by extracting each block to a temporary file, executing it in an isolated subprocess, and writing a pass/fail…
rtk-ai/rtk
Enforces red-green-refactor for new RTK output filters in Rust, using real captured fixtures, snapshot tests with insta and token-savings assertions.
dimensionalOS/dimos
Rules for writing, fixing and reviewing pytest unit tests that are hermetic: behavior-focused, deterministic, isolated and cheap to run.
amElnagdy/guard-skills
Reviews newly written or edited tests against nine rules that cut test bloat, such as mock-heavy checks and near-duplicate cases, before they are committed.
fla-org/flash-linear-attention
Disciplined, reproducible loop for making an FLA kernel faster (Triton, Gluon, TileLang, CuTe) without ever breaking or gaming correctness.
Categories
Port a Node-RED node into EdgeLinkd the way this repo does it: implement the node in Rust under crates/core/src/runtime/nodes, mirror Node-RED's mocha spec as pytest tests under tests/, register the…. Port Node Red Node is an agent skill from oldrev/edgelinkd.py.
Port Node Red Node fits situations like: debug an existing node; report which Node-RED spec tests are still missing.
Run `npx skills add oldrev/edgelinkd --skill port-node-red-node -a claude-code`. Or copy the skill folder (.agents/skills/port-node-red-node in oldrev/edgelinkd) into .claude/skills/port-node-red-node in your project. Claude Code loads it when a task matches its description.
Run `npx skills add oldrev/edgelinkd --skill port-node-red-node -a codex`. Or copy the skill folder (.agents/skills/port-node-red-node in oldrev/edgelinkd) into .agents/skills/port-node-red-node in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add oldrev/edgelinkd --skill port-node-red-node -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/port-node-red-node, .gemini/skills/port-node-red-node, .github/skills/port-node-red-node and .opencode/skills/port-node-red-node in your project.
Going by SKILL.md and its folder, Port Node Red Node needs Python for the scripts in its folder and the command-line tools its instructions call (cargo, pytest, python, git, npm and pip). Our summary lists: Python 3; Node.js.
SKILL.md contains no URLs. Its commands use git, npm and pip, which can reach the network depending on how they are called. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. The check reads SKILL.md only: the scripts in the folder are not scanned, so read them before running anything.
Port Node Red Node is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 3k tokens (SKILL.md is roughly 12k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 7.9k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Port Node Red Node: Rust TDD Workflow (rtk-ai/rtk, 83k stars), Adk Verify Snippets (google/adk-python, 22k stars), RTK Filter TDD in Rust (rtk-ai/rtk, 83k stars) and Hermetic Python Unit Tests (dimensionalOS/dimos, 4.6k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
oldrev (a GitHub user) maintains it in oldrev/edgelinkd, which has 121 GitHub stars. The repository was last updated on October 6, 2026.
Source: oldrev/edgelinkd on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.