Imported from streamer45/streamkit (
AGENTS.md). Install upstream withnpx skills add streamer45/streamkit. Copyright stays with the author.
StreamKit Agent Notes
These notes apply to coding agents (Claude/Codex/Devin/etc.) contributing to this repo. Agent-assisted contributions are welcome, but should be supervised and reviewed by a human before merge.
What is StreamKit
StreamKit is a self-hostable media processing server (Rust). A single binary
(skit) runs pipelines as a node graph (DAG) — via a web UI, YAML, or
WebSocket API. Two modes: dynamic (real-time, hot-reconfigurable) and
oneshot (stateless batch processing). See agent_docs/architecture.md for
the full architecture.
Codebase Map
| Directory | Purpose |
|---|---|
apps/skit/ |
Server binary — HTTP/WS handlers, config, auth, plugins |
apps/skit-cli/ |
CLI client binary (skit-cli) |
crates/core/ |
Shared traits/types — ProcessorNode, Pin, Packet, NodeRegistry |
crates/engine/ |
Pipeline executor — graph builder, oneshot engine, dynamic actor |
crates/nodes/ |
Built-in nodes: audio::, video::, transport::, core::, containers:: |
crates/api/ |
YAML pipeline parsing, WebSocket protocol, TS type generation |
crates/plugin-{native,wasm}/ |
Plugin host adapters (FFI and WASM) |
sdks/plugin-sdk/ |
Plugin SDKs for Rust, Go, and C |
ui/ |
React 19 web UI (Vite + Bun) |
plugins/native/ |
Official ML plugins (Whisper, Kokoro, NLLB, etc.) |
samples/ |
Example pipelines (dynamic/ and oneshot/), audio files, images, fonts, Slint files |
tests/ |
Pipeline validation tests (oneshot pipeline smoke tests) |
e2e/ |
Playwright end-to-end tests |
docs/ |
Astro + Starlight docs site (sidebar in docs/astro.config.mjs) |
Tech Stack
- Rust (version pinned in
rust-toolchain.toml), tokio, axum, wgpu - UI: React 19, TypeScript, Zustand, Jotai, React Query, Radix UI, React Flow
- Build/tooling:
just(task runner), Bun (UI), sccache (Rust build cache) - Testing:
cargo test(Rust), Vitest (UI), Playwright (E2E) - Platform: Linux x86_64
Workflow
- Keep PRs focused and minimal.
- Run
just testandjust lintbefore submitting (or explain why you couldn't). - Follow
CONTRIBUTING.md: DCO sign-off (git commit -s), Conventional Commits, SPDX license headers on all new files. - Linting discipline: Do not blindly suppress lint warnings with ignore/exception rules. Refactor instead. If a suppression is truly necessary, include a comment explaining the rationale.
- UI tooling: Use
bun install/bunx/bun run— never npm or pnpm.
Pull Request Descriptions
A reviewer should understand the PR in under a minute. Write a thoughtful human-style summary — not a machine-generated inventory.
The PR template (.github/PULL_REQUEST_TEMPLATE.md) defines the sections;
this section defines what to put in them.
Principle
The same philosophy behind the Comment Guidelines below applies to PR descriptions: say why, not how. The diff already shows what changed — the description should explain why it matters, what a reviewer should watch for, and anything that isn't obvious from the code.
Do NOT write
- File/function/test-case inventories — listing every touched file, helper, route, or test adds bulk without signal. The diff is one click away.
- "What's tested" exhaustive lists — enumerating every handler, error path, or assertion. Summarize the coverage intent instead: "Added unit tests for the plugin loader's error paths and the WS dispatch permission matrix."
- Coverage tables by default — include coverage numbers only when the PR's explicit purpose is raising coverage, and even then one compact table is enough. Never dump per-file coverage for a feature PR.
- Implementation narration — "Added a helper
foo()that callsbar()which returns …". This restates the diff. - Repeating the commit list — the commit history is already visible.
- Long "Production-code touchpoints" / "Conventions followed" sections — these belong in the code itself (or CONTRIBUTING.md), not in every PR.
DO write
- 2–5 bullets describing the meaningful change and why it was made.
- Behavior changes visible to users or downstream code — new API surface, changed defaults, removed features.
- Anything surprising or risky — concurrency changes, migration steps, FFI boundary shifts, known edge cases.
- Scope decisions — what was intentionally left out and why.
- Follow-ups — related issues discovered, future work, known limitations (with issue links when applicable).
- Review checklist scaled to risk — a docs-only PR needs 0–1 items; a concurrency fix might need 3–5.
Example (good)
## Summary
- Consolidate all agent skills into `.agents/skills/` per the
agentskills.io spec so any compliant agent discovers them.
- Add YAML frontmatter (name, description, license) to every SKILL.md.
- Handle SPDX compliance via REUSE.toml since inline headers break
frontmatter parsing.
- `.claude/skills/` replaced with symlinks for backward compat.
## Review & Validation
- [ ] Verify symlinks resolve: `cat .claude/skills/architecture/SKILL.md`
- [ ] `reuse lint` still passes
Example (too long — avoid)
## Summary
Phase 4 / Stream F of the coverage sprint …
### Coverage delta
| File | Before | After | Target |
| … 12-row table … |
### What was added
**`crates/plugin-wasm/`:**
- `src/conversions.rs` — extended `#[cfg(test)] mod tests` with …
- `src/wrapper.rs` — `#[cfg(test)]` tests for new/input_pins/…
(another 40 lines of per-file narration)
### Production-code touchpoints
(another 10 lines restating what the diff shows)
The first version says everything the second one does — in 6 lines instead of 60.
Comment Guidelines
"NEVER try to explain HOW your code works in a comment … just tell people WHY." — Linux kernel coding style, §8
"The best code is self-documenting. Giving sensible names to types and variables is much better than using obscure names that you must then explain through comments." — Google C++ Style Guide
Default is no comment. If you feel compelled to add one, first ask whether a better name or a small extraction would make it unnecessary. Comments that restate the how (what the code obviously does) add noise and drift out of sync; comments that explain the why (constraints, trade-offs, gotchas invisible from the code alone) prevent real bugs.
Do NOT write
These antipatterns accounted for ~3,500 lines removed in the v0.5 cleanup.
- Line narration — restating what the next line does
(
// Send packetbeforesend_packet(),// Check if emptybeforeif x.is_empty()). The Google C++ guide limits implementation comments to "tricky, non-obvious, interesting, or important parts"; obvious code needs nothing. // Helper: Xlabels on descriptively-named functions — the name already tells the reader; a label adds nothing.- Verbose JSDoc /
///on self-documenting items — an essay onPARAM_THROTTLE_MS = 33or per-field docs like/** Handler for slider onChange event */adds zero information. Rust RFC 505 and the Rust API Guidelines recommend a single-line summary as the first doc comment line; anything beyond that should earn its keep. - Section dividers —
// --- Public Modules ---,// State,// Handlers. Use blank lines or code structure instead. - Step-by-step numbered narration —
// 1. Validate,// 2. Connect. Extract named functions instead of numbering prose. - Complexity apologies — multi-paragraph justifications above lint suppressions. Keep the rationale to one line.
- Standard framework behavior —
"useState setters are stable","useMemo prevents re-renders". Any React/Tokio/Axum developer knows this. - Dead code "for reference" — git history preserves it. Delete it. As Google's documentation best practices put it: "Dead docs are bad. They misinform, they slow down, they incite despair." The same applies to dead code.
- Diff-oriented comments — comments whose only purpose is to explain your edit ("now we also check X", "previously this did Z"). Put that context in the PR description instead.
DO write
// SAFETY:onunsafeblocks and FFI boundaries — Rust convention and required byclippy::undocumented_unsafe_blocks.- Lint suppression rationales — required by the Linting discipline rule
above. Always include
-- reason(eslint) or// reason(#[allow]). - Non-obvious constraints invisible from reading the code — performance
contracts (
React.memoreferential-stability requirements), DOM-ordering guards required by Playwright selectors, protocol/codec quirks. The Google C++ guide calls these out: "tricky or complicated code blocks should have comments before them." - Design decisions that differ from the intuitive default — e.g. why session mode no longer implicitly enables publishing.
@publictags on APIs consumed by external tools (Playwright, MCP).- Concurrency invariants — lock-ordering, channel backpressure semantics, and synchronization assumptions. The Google C++ guide specifically requires documenting "the synchronization assumptions the class makes."
Rule of thumb: code tells you how, comments tell you why (Coding Horror, 2006, paraphrasing Kernighan). If a comment explains a constraint that isn't visible from reading the code alone, keep it. If it restates what the code obviously does, delete it.
Fix Root Causes, Not Symptoms
Prefer a clean change that takes longer over a brittle stack of patches that ships sooner. If a feature requires defending against the same race in three places, layering synchronous shadow refs over async state, or "preferring draft over live" because two event sources disagree on timing — stop and reconsider the contract, don't add a fourth patch.
Concrete signals you've crossed into workaround territory:
- You're adding a timeout to recover from a missing event.
- You need a synchronous shadow of state that already lives in an async store.
- You catch yourself writing "the X event doesn't actually mean X, it means attempted X, so we also have to listen for Y to know if it really happened."
- Tests are updated by adding sleeps or by relying on previously-broken behavior (e.g. an invalid value being silently accepted).
- The root cause is in a different layer than the one you're editing, and fixing it there would invalidate most of your patch.
When you spot this, surface the design issue to the user with a concrete proposal — even if it's more invasive — before writing the patch. State the tradeoff honestly: "this will take longer but produces something durable; vs. this short-term fix has these specific brittleness costs."
Past incident worth remembering: the WebSocket nodeadded event used to fire
before plugin construction had even started. The UI accumulated 13 commits of
draft-state machinery (state-watchers, debounce timers, topology priority
hacks) trying to reconstruct "did the node actually get created?" from
out-of-band signals. The actual fix was a small server change — emit
nodeadded from the engine actor's success path instead of the WS handler —
which collapsed the UI back to the obvious cleanup. Code that exists to
paper over a broken contract should be deleted, not refined.
Verification Commands
| Task | Command |
|---|---|
| All lints | just lint |
| Rust lint only | just lint-skit (fmt + clippy with per-crate feature flags + license check) |
| UI lint only | just lint-ui (prettier + eslint + tsc) |
| All tests | just test |
| Rust tests | cargo test --workspace |
| UI tests | just test-ui |
| Perf regression tests | just perf-ui |
| E2E tests | just e2e-external http://localhost:4545 (requires running server) |
| Unused code check | just knip-ui |
| Build everything | just build |
Docker
- Official images:
Dockerfile(CPU) andDockerfile.gpu(GPU) via.github/workflows/docker.yml. - Health endpoint:
/healthz(also/health). - Standard images do not bundle ML models or plugins — mount them at runtime.
Demo images (
Dockerfile.demo, tagged-demo) include bundled models and plugins.
MCP (Model Context Protocol) Integration
StreamKit embeds an MCP server (apps/skit/src/mcp/) that exposes the
control plane as MCP tools, prompts, and resources. Agents with MCP client
support can use this directly — no REST/WebSocket code needed.
- Endpoint:
POST /api/v1/mcp(Streamable HTTP) orskit mcp(STDIO) - Config:
[mcp]section inskit.toml— setenabled = true - Auth: HTTP transport uses bearer tokens (same as REST API). STDIO is unauthenticated (admin-level, local-only).
- Code:
apps/skit/src/mcp/mod.rs(tools + resources),apps/skit/src/mcp/prompts.rs(prompts) - Tests:
apps/skit/tests/mcp_integration_test.rs
See agent_docs/mcp.md for the full tool/prompt/resource
reference and usage patterns.
Detailed Guides
Read the relevant guide before starting work in that area:
| Guide | When to read |
|---|---|
agent_docs/architecture.md |
Understanding crate relationships, data flow, key abstractions |
agent_docs/mcp.md |
Using the MCP server — tools, prompts, resources, auth, permissions |
agent_docs/ui-development.md |
Working on React UI — state management, component patterns |
agent_docs/e2e-testing.md |
Running E2E tests, headless-browser pitfalls |
agent_docs/render-performance.md |
Compositor perf profiling, render regression testing |
agent_docs/adding-plugins.md |
Making a plugin official — full checklist |
agent_docs/common-pitfalls.md |
Known mistakes agents make — read this first if unsure |
agent_docs/coverage.md |
Adding tests — what to cover, what NOT to cover, the 80% practical standard |
agent_docs/skills-setup.md |
Install curated skills.sh packages for React, Playwright, etc. |
