The detector is open source. The rules it ships were already public in this repo's git history and in every npm tarball of the JS engine, so a closed binary bought nothing it could keep; the moat is the service (the catalog, the labs, the review pipeline), not the check functions. Keeping them behind a prebuilt archive cost a C-ABI, an exact toolchain pin, a build-time download, a second release to order ahead of every engine release, and a serde layer that had to serve two encodings. Deleted - crates/core/src/ffi.rs, crates/core/build.rs, crates/core/tests/boundary.rs and the shim modules under src/checks and src/browser. - crates/foundation/src/boundary.rs and the postcard dependency. - DETECTOR_VERSION, scripts/check-detector-release.mjs and its test, the check:detector-release script, the detector gate and IMPECCABLE_SKIP_DETECTOR_CHECK in scripts/release.mjs. - scripts/lib/detector-bundle.mjs and tests/detector-bundle.test.mjs (the vendoring path for the closed browser bundle). - scripts/build-browser-detector.js and the build:browser script (a stub since the JS engine left the tree). - xtask's detector-archive subcommand and its public-repo lookup. Came back - crates/core is now the rule logic itself: every check_* / scan_*, the browser adapters, the visual-contrast decisions. It re-exports foundation as before, so no consumer changed. Its vectors dispatcher is the union of both id tables again, and tests/vectors.rs replays the frozen vectors straight through it. - crates/wasm and crates/xtask join the workspace. cargo xtask bundle builds the in-page bundle from browser-bundle/ plus the wasm core, writes dist/, refreshes the tracked crates/live/assets/detect-antipatterns- browser.js, and writes extension/detector/. bun run build:extension runs it instead of downloading. - crates/live/assets/detect-antipatterns-browser.js is tracked again; live mode embeds it and serves it as /detect.js. - Serde is back to plain derives: no is_human_readable branch in js::json_number, derived Serialize for Rgba and BrowserFinding with their skip_serializing_if attributes. - profile.release has lto = "fat" again; rust-toolchain.toml is plain stable plus the wasm32 target. The rust, rust-windows and oracle CI jobs lose continue-on-error and can be required. Verified - cargo build --workspace --all-targets: clean, no warnings. - cargo test --workspace: 346 pass, 0 fail (the 8 boundary tests are gone with the boundary). - cargo build -p impeccable-wasm --target wasm32-unknown-unknown --release: ok. - cargo xtask bundle && cargo xtask bundle --check: reproducible; the regenerated bundle is committed (it differs from the archived one, which was built with a pinned rustc and lto = false). - cargo build --release -p impeccable: no linker warnings, 12.5 MB (the same source at lto = false is 13.1 MB). - oracle: 795 pass, 0 fail, 0 accepted deltas, 0 missing goldens. - bun run build, bun run build:extension, web-ext lint (0 errors, 8 warnings), bun run test: 363 + 80 + 1 + 1 + 133 + 180 + 4 pass, 0 fail. - impeccable detect --no-config --json tests/fixtures/antipatterns: 128.7 ms median of 5. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vau2X53xGTjjTCXWMVBoNY
6.3 KiB
The engine: the Rust runtime behind every skill verb
Every command the skill text runs is {{scripts_path}}/impeccable <verb>. The
launcher next to the skill (skill/scripts/impeccable, impeccable.cmd)
finds or downloads one static binary per platform and execs it. That binary
is built from this repo's Cargo workspace. There is no Node at runtime.
This page is the map for anyone building or changing the runtime. The
observable behavior of every verb is specified in CLI-CONTRACT.md and
pinned byte-for-byte by tests/oracle/.
Everything is in this repo, Apache-2.0, and builds offline from source. No part of the engine is fetched at build time.
Layout
Cargo.toml the workspace (crates/*), release profile
rust-toolchain.toml the channel plus the wasm32 target
ENGINE_VERSION which engine release the launcher / npm shim download
.cargo/config.toml the `cargo xtask` alias
browser-bundle/ the page JS the in-page bundle is built from
crates/
cli the `impeccable` binary: verb router, exit codes
common Io handle (stdout/stderr/stdin/env/cwd), path + process helpers
context context, doctor, staleness, signals, concept-seed, pin, ...
hook the design hook (hook, hook-before-edit, hook-admin)
live live mode: server, wrap, accept, manual edits, Svelte/Vue
skills install / update / check / link (the old npm CLI verbs)
comp comp-fidelity pure libs (raster, png, metrics, fonts)
comp-verbs build-phase, comp-diff, comp-spec, font-match
detect `impeccable detect`: file walk, config, ignores, output, regex engine
html the static HTML engine: parser, cascade, static DOM, rule adapters
browser the URL engine: Chrome discovery, CDP, snapshot, visual pass
foundation JS-semantics helpers, color, findings, the rule registry, inline
ignores, the Dom trait, SnapshotDom, and the plain-data types
every check takes in and hands back
core the rule logic: every `check_*` / `scan_*` and its heuristics,
the browser rule adapters, the visual-contrast decisions
wasm wasm-bindgen exports over `core` (the in-page bundle and the
extension's offscreen core)
xtask `cargo xtask bundle`: builds the in-page bundle and the
extension pieces
crates/core re-exports the foundation modules under its own paths, so every
consumer names one crate: impeccable_core::js, impeccable_core::color,
impeccable_core::checks::rules::check_colors,
impeccable_core::browser::driver::collect_browser_findings. The split
between the two crates is about what a check is written against, not about
who may see it.
Build and test:
cargo build --release -p impeccable # target/release/impeccable
cargo test --workspace
IMPECCABLE_BIN=target/release/impeccable node tests/oracle/run.mjs # the behavior gate
bun run test and the oracle find the binary through IMPECCABLE_BIN, then
skill/scripts/bin/<os>-<arch>/ (bun run fetch:engine downloads the pinned
release there; IMPECCABLE_BIN=target/release/impeccable bun run fetch:engine
copies a local build), then target/release/impeccable, so a plain
cargo build --release -p impeccable is enough.
The frozen function-level vectors in tests/oracle/vectors/calls/ replay
through impeccable_core::vectors::call (cargo test -p impeccable-core),
which is the union of foundation's dispatch arms and the core's.
The browser bundle
The same rules that run natively run in a page, compiled to WebAssembly.
cargo xtask bundle is the one command that produces every browser artifact:
wasm-pack build crates/wasm --target no-modules --releaseintotarget/wasm-bundle/(opt-levelz, thenwasm-opt).- Concatenate the page JS in
browser-bundle/*.jsin a fixed order with the wasm-bindgen glue and the.wasmembedded as base64. The page JS only implements theDomprobe, marshals JSON, and draws the overlay; no rule logic lives there. - Write
dist/detect-antipatterns-browser.jsanddist/antipatterns.json, and copy the bundle tocrates/live/assets/detect-antipatterns-browser.js. That copy is a tracked generated file:crates/live/src/browser_assets.rsembeds it withinclude_str!and the live server hands it to the browser as/detect.js, so the binary has to carry it. - Write the five extension pieces into
extension/detector/(snapshot.js,overlay.js,core.js,core_bg.wasm,antipatterns.json). That directory is gitignored;bun run build:extensionruns this task and then packages the zips.
cargo xtask bundle --check rebuilds and fails when the tracked live asset is
stale, which is the CI staleness gate. The build is deterministic: same
sources, same bytes.
wasm-pack is the one extra tool this needs (cargo install wasm-pack --locked) plus the wasm32-unknown-unknown target, which
rust-toolchain.toml requests. IMPECCABLE_XTASK_SKIP_WASM_PACK=1 reuses
whatever is already in target/wasm-bundle/, for iterating on the page JS
alone. IMPECCABLE_EXTENSION_SKIP_BUNDLE=1 lets bun run build:extension
skip the bundle step when extension/detector/ is already complete, for CI
matrices that pre-built it.
Run cargo xtask bundle after touching crates/core, crates/wasm, or
browser-bundle/, and commit the refreshed live asset.
Releases
Two release kinds touch the runtime, in this order:
- Engine (
engine-v<ENGINE_VERSION>):bun run release:engineverifies the version, the npm platform-package pins and a clean tree, then tags and pushes;.github/workflows/release-engine.ymlbuilds the five targets and publishes the binaries with.sha256sidecars. The launcher, the npm shim andimpeccable installdownload fromgithub.com/pbakaus/impeccable/releases/download/engine-v<X>/. - npm platform packages, then the skill and CLI releases, which
scripts/check-engine-release.mjsgates on the engine release.
The extension ships its own vendored WASM core and never execs the engine
binary, so bun run release:ext is exempt from that gate. It does need
bun run build:extension (and therefore a Rust toolchain and wasm-pack)
before the zip is attached.
CI runs the workspace build and tests (rust, rust-windows) and replays the
oracle against a release build from the checkout under test.