The engine no longer lives in a separate repo. `crates/` is a snapshot of the
open crates (foundation, core, common, context, live, hook, skills, comp,
comp-verbs, html, browser, detect, cli) plus `Cargo.lock`, taken as a git
archive of the engine repo at the commit that finished the boundary split.
None of that repo's history comes with it, and none of it should: the closed
half stays private.
The closed half is the rule engine. It ships as a prebuilt native archive per
target, `libimpeccable_detector.a`, published as a `detector-v<X>` GitHub
Release on this repo. `crates/core/build.rs` resolves and links it three ways:
`IMPECCABLE_DETECTOR_LIB=<dir>` for a local detector build, else the
`~/.impeccable/detector/<version>/<target>/` cache, else a download verified
against its `.sha256` sidecar. `crates/core` is a thin shim over a three-symbol
C ABI; nothing above it knows the boundary exists.
What changed versus the engine repo copy:
- Every crate manifest moves from `license-file.workspace` to
`license.workspace` (this workspace declares Apache-2.0), and the workspace
gains the `postcard` dependency the boundary encoding needs.
- The launcher contract test reads `skill/scripts/impeccable{,.cmd}` instead of
a sibling `launcher/` dir, and `engine_binary` downloads from
`github.com/pbakaus/impeccable/releases/download/engine-v<version>/` instead
of the retired dist repo. No oracle golden carried the old URL, so no
re-recording was owed.
- The tests that hunted for a public repo through `IMPECCABLE_PUBLIC_REPO`,
`../impeccable-second` or a hardcoded home directory now resolve the root as
`CARGO_MANIFEST_DIR/../..`, because they are in it. The env var stays as an
override for an out-of-tree checkout.
- The in-page bundle (`detect-antipatterns-browser.js`, 2 MB of generated wasm
glue) is no longer tracked. `crates/core/build.rs` resolves it beside the
archive, hands the path to `impeccable_core::browser::IN_PAGE_BUNDLE_JS`, and
live mode serves that. `scripts/check-detector-release.mjs` now requires it
and its `.sha256` in a detector release.
- The live crate embeds `skill/scripts/live-browser*.js` and
`modern-screenshot.umd.js` directly rather than through vendored copies, so
the binary and the installed skill cannot drift.
- `crates/browser/assets/` (an unused second copy of the bundle) is gone.
- `tests/lib/engine-bin.mjs` also accepts `target/release/impeccable`, so a
plain `cargo build --release -p impeccable` is enough to run `bun run test`.
Verified with the archive from a local detector build: `cargo test --workspace`
267 pass, oracle 795 pass / 0 fail / 0 missing, `bun run build` clean, the
default suite green, and the launcher's `engine-probe` handshake answering
through `skill/scripts/impeccable`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vau2X53xGTjjTCXWMVBoNY
6.9 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/.
Layout
Cargo.toml the workspace (crates/*), release profile
rust-toolchain.toml EXACT rustc pin (see "The closed detector")
DETECTOR_VERSION which prebuilt detector release crates/core links
ENGINE_VERSION which engine release the launcher / npm shim download
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 OPEN helpers + boundary types: JS-semantics helpers, color,
findings, registry, inline ignores, the Dom trait, SnapshotDom
core the shim: re-exports foundation under the paths every crate
uses, and forwards the rule checks to the closed detector
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 closed detector
The rule engine itself (the checks, the browser rule adapters, the visual
contrast decisions) is proprietary and lives in a private repo. It ships as a
prebuilt native archive per target, libimpeccable_detector-<os>-<arch>.a
(impeccable_detector-windows-x64.lib), published as the GitHub Release
detector-v<DETECTOR_VERSION> on this repo, next to
detector-browser-bundle.zip (the same rules compiled to wasm for the
extension, the live overlay and the site). That release also carries
detect-antipatterns-browser.js and its .sha256: the in-page bundle
crates/core/build.rs resolves the same three ways and hands to
impeccable_core::browser::IN_PAGE_BUNDLE_JS, which live mode serves as
/detect.js. It is generated, so it is not tracked here.
crates/core/build.rs resolves the archive in this order and links it:
IMPECCABLE_DETECTOR_LIB=<dir>: a directory holding the archive for the current target (a local build of the detector repo).~/.impeccable/detector/<DETECTOR_VERSION>/<os>-<arch>/(IMPECCABLE_HOMEmoves the root).- A download from
detector-v<DETECTOR_VERSION>into that cache, verified against the.sha256sidecar.IMPECCABLE_DETECTOR_BASEoverrides the release root;IMPECCABLE_DETECTOR_OFFLINE=1refuses to download.
The in-page bundle follows the same order (beside the archive when
IMPECCABLE_DETECTOR_LIB supplies one, else the version cache, else a
verified download).
Three things follow from how that archive is made, and they are the reason for three otherwise odd-looking settings:
- The toolchain is pinned to an exact version. The archive is the closed
crates' rlib objects repacked with
llvm-ar, with no std inside. Its objects reference std by mangled symbol name, which only resolves against the same rustc build.rust-toolchain.tomlpins it; rustup installs it on firstcargoinvocation. A toolchain bump needs a new detector release. - The release profile has
lto = false. Fat and thin LTO internalize std symbols the opaque archive still needs and the link fails with "symbol(s) not found". Cargo's default thin-local LTO stays. crates/corekeeps the old paths. Nothing outsidecrates/coreandcrates/foundationknows about the boundary:impeccable_core::checks:: rules::check_colorsis a one-line shim that encodes its argument, calls the archive, decodes the result. The C-ABI is three symbols (det_abi_version,det_call,det_free) and a host vtable the closed side uses to call back into the openDom/StyleMapimplementations. Every id and every type that crosses is declared incrates/foundation/src/boundary.rs; the shim checks the ABI number once and panics with a clear message when the archive was built for another.
The frozen function-level vectors in tests/oracle/vectors/calls/ replay
through the shipped archive (impeccable_core::vectors::call forwards
unknown names to it), so the black box is verified the same way the open
code is.
Releases
Three release kinds touch the runtime, in this order:
- Detector (
detector-v<X>, published by the private repo's CI to this repo's Releases).DETECTOR_VERSIONhere pins it. - Engine (
engine-v<ENGINE_VERSION>):bun run release:engineverifies the detector release exists (scripts/check-detector-release.mjs), 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.
CI runs the workspace build and tests (rust, rust-windows) and the oracle
against a source build; both are warn-only until the first detector release
exists, then their continue-on-error flips to false.
Working on the detector
Changes to rule logic happen in the private detector repo. Point the shim at a local build while iterating:
# in the detector repo
cargo xtask detector-archive --out /tmp/det
# here
IMPECCABLE_DETECTOR_LIB=/tmp/det cargo test --workspace
Adding a function that the open crates call: add its id to
foundation/src/boundary.rs (never renumber), the shim in crates/core, the
dispatcher arm in the detector repo, and bump boundary::ABI if any existing
signature or type changed. The shim's test diffs the two id tables.