This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
See docs/VISION.md for the long-term goals and direction behind each crate in this
workspace — read it when a task touches roadmap, priorities, or "why does this exist"
questions.
After cloning, activate the shared git hooks (one-time):
git config core.hooksPath .githooks# Format (required before every commit; enforced by pre-commit hook)
cargo fmt --all
# Build
cargo build --workspace
# Test (all, including doc tests)
cargo test --workspace
cargo test --doc --workspace
# Run a single test
cargo test --workspace <test_name>
# Lint (warnings are errors)
# --all-targets is required so tests, doctests, and benches are linted too —
# without it, clippy silently skips everything behind #[cfg(test)] and tests/.
# The begin crate is excluded from the workspace commands and checked separately:
# once with --no-default-features (to avoid platform-specific renderer dependencies)
# and once with its default features (desktop) so #[cfg(feature = "desktop")] code —
# the code path the app actually ships — is linted too. Neither of the other two
# invocations covers that code, so skipping this one lets desktop-only warnings
# through unnoticed.
cargo clippy --workspace --exclude begin --all-targets -- -D warnings
cargo clippy -p begin --no-default-features --all-targets -- -D warnings
cargo clippy -p begin --all-targets -- -D warnings
# The ez-adam crate is likewise checked separately: once with --no-default-features
# (to catch issues in the non-desktop build) and once with its default features
# (desktop) so #[cfg(feature = "desktop")] code -- the code path the app actually
# ships -- is linted too.
cargo clippy -p ez-adam --no-default-features --all-targets -- -D warnings
cargo clippy -p ez-adam --all-targets -- -D warnings
cargo clippy --fix --workspace --exclude begin --all-targets
# Docs
cargo doc --lib --no-deps --open --workspace
# Docs, as CI checks them (rustdoc warnings -- broken/private intra-doc links, bare
# URLs, etc. -- promoted to errors; plain `cargo doc` above exits 0 on these)
RUSTDOCFLAGS="-D warnings" cargo doc --lib --no-deps --workspaceSanitizer runs require nightly and a target triple (e.g. x86_64-apple-darwin or x86_64-unknown-linux-gnu):
RUSTFLAGS=-Zsanitizer=address cargo +nightly test -Zbuild-std --target <triple> --lib --workspace
RUSTFLAGS=-Zsanitizer=thread cargo +nightly test -Zbuild-std --target <triple> --workspace
RUSTFLAGS=-Zsanitizer=leak cargo +nightly test -Zbuild-std --target <triple> --workspaceWhen executing an implementation plan (from writing-plans or a design doc) that breaks down
into independent or semi-independent tasks, default to the subagent-driven-development skill
rather than executing-plans — dispatch each task to a subagent with its own context, review
each result, and update the plan/ledger as tasks complete. Only fall back to a single-session,
non-delegated execution when the user explicitly asks for it or the plan has no tasks that
benefit from separate context (e.g. a single tightly-coupled edit).
If a request is made that requires any modification, additions, or deletions to files in the project, stop and suggest the user create a worktree first.
Create project worktrees under .claude/worktrees/; do not place them outside the repository.
Never commit directly to main.
Before creating a PR, run the full check suite locally — every command in the Commands section above, including all five clippy invocations (workspace, and begin and ez-adam each with and without their default features).
cargo build --workspace and cargo test --workspace must produce zero compiler
warnings — clippy's -D warnings does not catch everything a plain build/test compile
can warn about (e.g. an unused mut). Read the build/test output and fix any warnings
before opening the PR.
For any multi-phase or multi-step piece of work (a design doc phased into several sub-plans, a
plan executed across multiple sessions), create or update a dated handoff document under
docs/superpowers/ (e.g. docs/superpowers/YYYY-MM-DD-phase-N-handoff.md) summarizing what's
done, what's deliberately deferred, and what's left, before opening a PR for that step — see
docs/superpowers/2026-07-18-phase-3-handoff.md for the established format. This is what lets a
new conversation/context pick up the remaining work without re-deriving status from git history.
When a review (self-review, subagent task review, or a final whole-branch/PR review) surfaces a finding, address it immediately rather than deferring it — this applies to Minor findings just as much as Critical/Important ones. "Minor, park it" is not a default; fix it in the same pass if the fix is small and in scope.
If a finding is genuinely large (a real design change, a non-trivial refactor, a new feature) or
out of scope for the current task/PR, don't just leave it noted in a ledger or PR comment —
open a GitHub issue for it (gh issue create) so it survives past the current session/worktree,
then reference the issue number in the PR/commit rather than silently dropping the finding.
This project has not been released yet and has no clients. The API is not stable and may change at any time. The project is in active development and is not yet feature-complete. Prefer redesigning any components rather than patching them or layering on top of them. The goal is to have a clean, correct, and efficient implementation.
cel-runtime, cel-parser, cel-rs-macros, adam-rs, and adam-lang are general-purpose
components meant to be consumed by other, future projects — not just this repository's own begin
UI. begin and its bundled examples/*.adm2 files exist to exercise and demonstrate these
libraries; they are a test harness, not the target audience.
When designing or implementing a feature in these library crates, solve the general problem the
feature actually describes — not just whatever narrower case a specific begin example happens to
exercise. Reaching for words like "the practical case" or "the case that matters here" to scope a
design down to what one example needs is a signal to stop and generalize instead. Where a fully
general solution genuinely isn't feasible in one pass, delineate the boundary explicitly (a
documented precondition, a clear error or diagnostic when it's crossed — never silent wrong
behavior) and track the remaining generalization as a GitHub issue, rather than quietly special-casing
behavior around whatever example is currently at hand.
This workspace is centered on cel-runtime and is split into libraries, a façade crate, and supporting tools:
cel-rs— root façade crate that re-exportscel-runtimeand depends oncel-parserandcel-rs-macroscel-runtime— core stack-based runtime; all evaluation and stack machinery lives herecel-parser— recursive-descent CEL parser, lexer, and parser error typescel-rs-macros— proc-macro crate for compile-time CEL expression validationadam-rs,adam-lang, andadam-lsp— supporting crates for the Adam property model system: the constraint-graph runtime, its DSL, and its language serverbegin— Dioxus-based UI applicationeditors/vscode-adam-lang— VS Code extension providing syntax highlighting and diagnostics for adam-lang, backed byadam-lspxtask— repository automation and maintenance tasks
The runtime is a stack-based expression evaluator built in four layers of increasing type safety:
| Layer | File | Role |
|---|---|---|
RawStack |
raw_stack.rs |
Byte-aligned unsafe stack; push<T> returns padding bool, pop<T> requires it |
RawSegment |
raw_segment.rs |
Op list + closure storage + per-op dropper functions |
DynSegment |
dyn_segment.rs |
Runtime type-checking wrapper; maintains stack_ids: Vec<StackInfo> |
Segment<Args, Stack> |
segment.rs |
Zero-cost compile-time phantom wrapper; Args: IntoList, Stack: List |
The compile-time type system uses cons-cell heterogeneous lists (CStackList<H,T> / CNil) defined in c_stack_list.rs and list_traits.rs.
&str → TokenStream (proc_macro2) → LexLexer (flatten + combine multi-char ops) → CELParser (recursive descent) → DynSegment
cel-parser/src/lib.rs contains the grammar entry points and parser pipeline. Function names mirror grammar productions directly (e.g. is_additive_expression).
cel-parser/src/op_table.rs implements OpLookup: a stack of custom ScopeFn scopes (LIFO) backed by static phf_map built-in ops. Overloading is by arity + TypeId.
cel-parser/src/error.rs defines CELError and SourceSpan, plus format_rustc_style() for caret diagnostics.
- Pass
&str/&[T]rather than cloning intoString/Vec<T> - Use generics or
fnpointers instead ofBox<dyn Trait>when the type set is statically known - Return
&[T]orimpl Iteratorover owned collections when the data already lives elsewhere - Borrow inside a block to release the borrow before the next mutable use rather than collecting into a
Vec
Every class, type, and function in every language used by the repository must have an adjacent contract written in the language's documentation or comment syntax. The contract lives adjacent to the declaration so it stays synchronized with the code. Each contract states type invariants at public boundaries, valid inputs, observable postconditions, errors where applicable, and non-constant complexity.
For Rust functions, use a /// doc comment written in contract style.
Required sections (include only those that apply):
- Summary — A concise present-tense sentence fragment describing what the function does or returns, ending with a period.
- Preconditions — Non-obvious preconditions not implied by the summary, as
/// - Precondition: <condition>bullets. Preconditions implied by the summary need not be restated. Violation has unspecified behavior (which may include a panic); do NOT document what happens on violation — instead usedebug_assert!()to check preconditions in debug builds.# Errors— conditions that cause anErrreturn (runtime errors, not precondition violations).# Safety— invariants the caller must uphold forunsafefunctions. This is the one place where the consequence of violation (undefined behavior) must be documented.
- Postconditions —
/// - Postcondition: <condition>bullet in the body, only when not implicit in the summary. - Complexity —
/// - Complexity: <description>bullet, required whenever the operation is not O(1). Default assumption is O(1) time and space.
If you cannot write a simple contract for a function, treat that as a signal that the design needs improvement.
Additional rules:
- For parser functions, the grammar production is the summary:
/// \additive_expression = multiplicative_expression { ("+" | "-") multiplicative_expression }.`` - Use
# Examplesfor all public APIs. - Modules use
//!with a usage tutorial.
Example:
/// Removes and returns the top element.
///
/// - Precondition: `padding` matches the value returned by the corresponding `push`.
///
/// - Complexity: O(1).
pub fn pop<T>(&mut self, padding: bool) -> TDerive tests from the contract and public interface only — do not read or consider the implementation. The test suite verifies observable behavior as specified by the contract. If an implementation detail is needed to write a test, clarify the contract or redesign the interface instead.
- Each
# Errorscondition should assert theErrvariant is returned. - Each postcondition should be asserted.
- Edge cases implied by the summary (empty input, single element, boundary values) should be covered.
Precondition violations have unspecified behavior and should not be tested. Tests written against the implementation risk encoding bugs rather than verifying intent.
Every component needs a contract and unit tests wherever one can be given, including UI/framework glue — "it's just glue code" is not by itself a reason to skip testing. When logic embedded in framework-coupled code (a Dioxus component, an event handler, a callback) amounts to more than a direct passthrough of an existing call — any branching, combining, or suppressing of multiple conditions — extract it into a small pure function with its own doc comment and contract-derived unit tests, and have the framework-coupled code call it. Reserve "no dedicated test" for code that is genuinely a trivial passthrough with no branching of its own (e.g. mapping one existing boolean straight onto one element attribute); anything with its own decision to make should have a contract, even if the surrounding component still can't be tested directly.
Operations that can fail use .op1r / .op2r variants (returning Result) rather than .op1 / .op2. Arithmetic on signed integers must use checked_* operations, not wrapping arithmetic.