Thanks for helping make Vize sharper. This project is still moving toward v1 alpha, so small, focused changes with clear verification are the easiest to review.
Use the Node.js development runtime from package.json#devEngines.runtime and the Rust version from rust-toolchain.toml. The workspace declares a minimum supported Rust version (MSRV) of 1.99.0 in Cargo.toml ([workspace.package].rust-version); contributions must compile under that version.
Install dependencies from the workspace root:
vp install --frozen-lockfile --prefer-offlineIf vp is not available yet, install the package manager from package.json and use the workspace scripts through the local toolchain.
Run the narrowest check that covers your change, then broaden when you touch shared behavior.
vp check <changed-files>
node --test tests/tooling/<test-file>.test.ts
cargo fmt --all -- --check
cargo test -p <crate>Before opening a PR that changes shared tooling, release automation, native bindings, or compiler behavior, run the relevant workspace task from CI locally when practical.
The root build, test, and lint workflows are local by default and need no hosted credentials:
vp run --workspace-root build
vp run --workspace-root test
vp run --workspace-root lintInside the Nix development shell, vp build, vp test, and vp lint are shorthand for these
workspace tasks.
For one-command Linux CI parity, enter the dedicated Testbox shell. The default nix develop shell
intentionally omits Blacksmith and does not need its hosted artifact or credentials:
nix develop .#testboxThen run the guarded lifecycle below. It clears any old box ID before warmup, skips remote tasks if authentication, push, or warmup fails, and always attempts to stop a successfully warmed box even when a task fails:
run_testbox_checks() {
unset BLACKSMITH_TESTBOX_ID testbox_output
"$VIZE_BLACKSMITH_BIN" auth login || return
git push --set-upstream origin "$(git branch --show-current)" || return
if testbox_output="$(vp run --workspace-root testbox:warmup)"; then
BLACKSMITH_TESTBOX_ID="$(printf '%s\n' "$testbox_output" | tail -n1)"
else
warmup_status=$?
unset testbox_output
return "$warmup_status"
fi
if [ -z "$BLACKSMITH_TESTBOX_ID" ]; then
printf '%s\n' "Testbox warmup returned no box id." >&2
unset BLACKSMITH_TESTBOX_ID testbox_output
return 1
fi
export BLACKSMITH_TESTBOX_ID
if vp run --workspace-root build:testbox &&
vp run --workspace-root test:testbox &&
vp run --workspace-root lint:testbox; then
testbox_status=0
else
testbox_status=$?
fi
if vp run --workspace-root testbox:stop; then
stop_status=0
else
stop_status=$?
fi
unset BLACKSMITH_TESTBOX_ID testbox_output
if [ "$testbox_status" -ne 0 ]; then
return "$testbox_status"
fi
return "$stop_status"
}
run_testbox_checksFor Blacksmith Testbox job changes, also validate the workflow shape with
node --test tests/tooling/github-workflows.test.ts.
clippy.toml bans std::string::String, std::collections::HashMap/HashSet, Rc/Arc,
ToString::to_string, and std::format! across the workspace; the replacements live in
vize_carton (String, FxHashMap, FxHashSet, cstr!, append!, appends!). That policy is a
production invariant and is not relaxed for tests. CI enforces the workspace lint with
cargo clippy --workspace -- -D warnings -D clippy::wildcard_imports and the maestro test targets
with cargo clippy -p vize_maestro --tests -- -D warnings.
In test code, reach for the real replacement before an allow:
format!("…")→vize_carton::cstr!("…"). In a function returningResult<_, String>the?operator performs theCompactString→Stringconversion for you; at a barereturn Err(…)add.into().some_str.to_string()→some_str.to_owned().- A
lsp_typesfield you cannot retype: build the value withcstr!and.into()it at the boundary, or let inference name the type instead of writingStringout.
[workspace.lints] in the root Cargo.toml is the shared contract every crate joins with
[lints] workspace = true. tools/commands/ci/check-workspace-lints.rs --check runs in CI and keeps
the list of crates that have not joined yet from growing back.
- Panic-free shipped code. Library and binary targets must not abort on any input, so
unwrap,expect,panic!,unreachable!,todo!,unimplemented!,[]indexing and slicing, and&str[..]slicing are denied. Express the invariant by construction (iterators,match/let … else,get,split_at_checked,strip_prefix,char_indices), propagate with?, or degrade gracefully (skip the node, keep the input unchanged, emit a diagnostic). Do not suppress these lints in shipped code. Test code is exempt throughclippy.toml. - Reasoned suppressions. Use
#[expect(lint, reason = "…")]instead of#[allow]: a stale expectation fails the build, and each one records why it exists. When a lint only fires under somecfg, gate the expectation withcfg_attr. - Skeleton
todo!(). Davinci level skeletons may leave bodies astodo!()inside a module that starts with#![expect(clippy::todo, reason = "skeleton: #NNNN")], and only where no product path reaches them.tools/commands/ci/check-skeleton-todos.rs --checkcompares the per-crate count withtools/config/skeleton-todos.toml; counts only go down (--writelowers the baseline after you fill a module).
insta::assert_snapshot! and insta::assert_debug_snapshot! expand through std::format! inside
the insta crate, so no call-site rewrite can avoid the disallowed macro. Those assertions are the
one sanctioned exception. Put #[expect(clippy::disallowed_macros, reason = "…")] on the
#[cfg(test)] mod … item that hosts the snapshot assertions — never at the crate root, never on the
lint globally, and never on a module that also contains production code — and point the reason
back to this section.
When the test module is inline inside an over-budget file, split it out to the ordinary module path
next to its parent (for example, feature/tests.rs for mod tests;). Keep the corresponding
snapshots/ directory beside that module so insta still resolves the recorded snapshots. Do not
use #[path = "…"]; Vize modules must follow Rust's ordinary module discovery layout.
Vize follows compiler-project practice from rustc, TypeScript, TypeScript-Go, and Flow: classify the change, add the smallest meaningful fixture, review generated output as a contract, then broaden to parity, performance, or release gates when the touched surface needs it. See Language Engineering Practices for the full matrix.
Use one of these change classes in PRs when applicable:
- Parser or AST
- Compiler and codegen
- Semantic analysis, lint, and cross-file analysis
- Virtual TypeScript and type checking
- Formatter and LSP
- Runtime packaging, release, or docs
For language-facing changes, include the fixture or snapshot diff that proves the behavior. For snapshot refreshes, explain why the new output is correct and avoid broad baseline churn unless the PR is specifically about that output family.
When a compiler mismatch starts from an external repro or a local project file, use the playground
Compiler Inspector to inspect the official Vue output, Vize output, Virtual TS, VIR, and cross-file
graph. Add the inspector permalink to the PR body, then land the minimized fixture or full snapshot
that turns the output into a reviewed contract. Local batches can be packaged with
vize inspector <file-or-glob>, and agent handoff can use vize inspector --format agent.
- Use Conventional Commits for commit messages and PR titles, such as
fix(vite-plugin): surface SFC compile errors. - Keep PRs focused on one behavioral change or one documentation/governance change.
- Include verification commands in the PR body.
- Do not refresh large snapshot baselines unless the PR is specifically about those outputs.
- Do not include secrets, registry tokens, private vulnerability details, or machine-local paths in reports, commits, or PRs.
Use the fix report template for regressions, crashes, incorrect diagnostics, package installation problems, and release failures. Use the feature request template for new integrations, API changes, or workflow improvements.
Security reports should follow SECURITY.md instead of the public fix templates.
Prioritize reproducible reports from external users ahead of roadmap work. Carry each report through a minimized regression fixture, a reviewed fix, successful GitHub Actions checks, actual merge queue completion, and verified publication. A merged PR alone does not complete a report; verify the published package or release artifact against the reproduction before closing it.
Credit the reporter's reproduction or investigation with a Co-authored-by
trailer in the fix commit and preserve it in the squash commit. Verify the
reporter's public GitHub identity; use a publicly supplied email or the verified
ID+LOGIN@users.noreply.github.com address, never a guessed private email. Keep
report links and acknowledgements in the release notes. Add missing historical
credit in a new attribution change without rewriting published fixes or tags.
By participating, you agree to abide by the Contributor Covenant v2.1.
The governance model and decision-making process are documented in GOVERNANCE.md.
For help finding the right channel, see SUPPORT.md.