This file provides guidance when working with code in this repository.
(CLAUDE.md is a symlink to this file.)
vihaco is a framework for building small virtual machines for hybrid
analog/digital quantum control orchestration. You define an instruction set,
the components that execute it, the effects they emit, and optionally a
parser for source text — all as ordinary Rust — then compose them into a
machine. A component boils down to one method:
execute(&mut self, instruction, message) -> Result<Effects<Effect>>.
It is a Cargo workspace of focused crates with no umbrella crate; depend on
what you need (README.md has the table and a worked example).
Tasks are also wrapped by mise (mise run <task>), but
the plain cargo/tool commands are equivalent:
cargo test --workspace --all-targets # All tests EXCEPT doctests (mise run test)
cargo test --workspace --doc # Guide + example doctests (mise run doctest)
cargo test test_name # Single test by name
cargo fmt --all # Format
cargo clippy --workspace --all-targets -- -D warnings # Lint (mise run lint)
hawkeye check # SPDX license-header check (mise run license)
hawkeye format # Add/repair SPDX headers (mise run license-fix)
cd docs && pnpm install && pnpm dev # Preview the docs site (Astro; prefer pnpm)--all-targets deliberately excludes doctests — run --doc separately to
exercise the documentation (see vihaco-doctests under Architecture).
CI (.github/workflows/) runs these as separate jobs; run them before
committing. Tests run on Linux/macOS/Windows, so avoid platform-specific code.
cargo fmt --all -- --check # 1. Format
cargo clippy --workspace --all-targets -- -D warnings # 2. Lint (warnings are errors)
cargo test --workspace --all-targets # 3. Test
cargo test --workspace --doc # 4. Doctests (docs can't drift)
hawkeye check # 5. SPDX license headersEvery source file under crates/** carries a two-line SPDX header (see any
file's first two lines); hawkeye format adds it. Line-sensitive trybuild
fixtures under tests/ui/ and tests/compile_errors/ are intentionally
excluded from the header check.
The workspace lives under crates/. The standard virtual hardware
components are the vihaco-* crates there (e.g. vihaco-cpu) — each is a
self-contained component you can use directly or copy as a starting point.
| Crate | Role |
|---|---|
vihaco |
The framework. Core types + the module / syntax / runtime layers. Re-exports the derives, so most code depends only on this crate. |
vihaco-cpu |
A ready-made CPU/host component: a stack machine with a StepOutcome control-flow effect. Use directly or as a reference component. |
vihaco-abi |
ISA vocabulary: Instruction/Effects, Value/Type, encoding + host-VM traits. Re-exports #[derive(Instruction)] via its derive feature. |
vihaco-abi-derive |
#[derive(Instruction)]. Consumed via vihaco-abi. |
vihaco-bytecode |
Binary/SST container format: headers, sections, instruction (de)coding, the container/ codec. |
vihaco-module |
Loadable Module model, ProgramLoader, host-VM traits (ProgramCounter, …), assembly-style Display. |
vihaco-runtime |
Component/machine runtime: GeneratedComponent, Effects sinks, observation machinery, CompositeMetadata. Re-exports its derives via its derive feature. |
vihaco-runtime-derive |
The proc macros behind #[derive(Message/Machine)] and #[component] / #[composite] / #[observe]. Consumed via vihaco-runtime. |
vihaco-stdlib |
Standard-library components and observers, currently including StdoutObserver. |
vihaco-syntax |
Typed SST parsing and module construction (Resolve). |
vihaco-parser |
The Parse<'src> and SurfaceInstruction traits plus lexical, primitive, and collection impls shared by the parser derive. |
vihaco-parser-derive |
#[derive(Parse)] — turns instruction, value, and type enums or structs into chumsky parsers via #[syntax_class] and #[pattern] (see attr.rs/codegen.rs). |
vihaco-doctests |
Dev-only, not published. include!s docs/examples/*.rs and runs every ```rust block in docs/src/pages/guide/*.md as a rustdoc doctest, so the public API and the docs can't drift. Editing the public API often requires updating these. |
- Instruction (
#[derive(Instruction)]): an enum where each variant is an opcode and its tuple fields are the payload. The derive generates the bytecode traits intraits/instruction.rs—OpCode(opcode byte +width),FromBytes/FromBytesWithOpcode,WriteBytes(little-endian viabyteorder) — plus the syntax descriptors ininstruction_syntax.rs(CanonicalInstructionSyntax, sugar forms). Scalars (u32/u64/i64/f64/bool/()) already implement these traits, so they nest as instruction payloads. - Message (
#[derive(Message)]): runtime-supplied input that is not encoded in the instruction stream. - Effects (
effect.rs):None | One | Many(SmallVec)— whatexecutereturns. Composable viaappend/extend/map/flat_map;expect_exactly_one_effectis the common extractor. - Dispatch (
#[dispatch(instruction=, message=, effect=, outcome=)]): wraps animplblock with anexecute(&mut self, inst, msg)method into an impl of theGeneratedComponenttrait (runtime/generated.rs). - Composite / Machine (
#[composite], which is#[derive(Machine)]plus field-attr stripping): a struct of devices. Each#[device(code, alias=...)]field becomes a variant of generated runtime and syntax instruction enums; one field may be#[program]to delegateProgramCounter. Seevihaco-runtime-derive/src/attr_composite.rs.
module.rs—Module<I, V, Ty, Info>, the loadable program:code,functions,labels,constants,strings,source_symbols, plus pluggableextrametadata. ItsDisplayprints an assembly-style dump (.text/.const/.string/.machine/.feature).loader.rswraps aModule+ program counter as aProgramLoader.syntax/— typed SST parsing and module construction. The parser yields aParsedModule/ParsedFunctionwhose function bodies areVec<I>. Pattern-derived instruction enums implementSurfaceInstructionautomatically; consumers useResolveto turn the typed parsed module into their runtime module.runtime/+traits/— the host-VM interfaces a CPU-like component implements:ProgramCounter,StackMemory,StackFrame,FrameMemory,GetProgramInfo,Stdout(traits/machine.rs), plusResetand theEffectSink/Observemachinery (#[observe]). Standard-library observers live invihaco-stdlib.value.rs— the runtimeValueenum (I64/U64/F64/Bool/FunctionRef/HeapRef/internedString…) andType.
lib.rs uses extern crate self as vihaco; so the derives can emit
::vihaco::… paths even within the vihaco crate itself.
- Error handling: this codebase uses
eyre(eyre::Result,eyre::eyre!) throughout — notanyhow/thiserror. Match the surrounding code. - Rust edition: every crate is edition 2024 (rustc ≥ 1.85) EXCEPT
vihaco-parserandvihaco-parser-derive, which are edition 2021 withrust-version = 1.75— keep their code within that MSRV. - Macro changes need trybuild coverage: compile-fail behaviour is pinned by
trybuild fixtures (
crates/vihaco/tests/ui/,crates/vihaco-parser-derive/tests/compile_errors/). These are line-sensitive; when you change a diagnostic, update the matching.stderr(TRYBUILD=overwrite cargo test).
This repo is managed by ion — it owns the agent setup, cloud
initialization, and the skills available here. Ion.toml is the source of
truth (Ion.lock pins versions/checksums):
[agents]uses thebuiltin:rusttemplate.[skills]enablesion-cliandagents-update(bothlocal).[options.targets]emits skills to.claude/skills.
.claude/ and .agents/ are generated by ion — don't hand-edit files
there; change Ion.toml and let ion regenerate them (e.g. via the
agents-update skill). This AGENTS.md is the human-authored guidance and is
not ion-generated, so edit it directly.
The site under docs/ is an Astro app. Use pnpm for Node dependency
management (pnpm install, pnpm dev, pnpm build). Content lives in
docs/src/pages/guide/*.md and docs/examples/*.rs; both are compiled/run by
the vihaco-doctests crate, so keep code snippets in sync with the API (see
Architecture).
- Conventional commits:
feat:,fix:,docs:,test:,ci:,refactor:,perf:,build:,chore:. Releases are automated from these via release-plz (seeRELEASING.md) — don'tcargo publishby hand. - Breaking changes: use
feat!:/fix!:(note the!) or aBREAKING CHANGE:footer.