They describe the binary's environment contract and the parity method every crate here was ported with; both belong next to the crates now. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vau2X53xGTjjTCXWMVBoNY
7.0 KiB
Porting guide
Conventions every porting task follows. The bar is byte-equal behavior against the JS goldens; read this before touching a crate.
Sources of truth
- JS source: the public repo checkout (
../impeccable-secondlocally,../impeccablein CI; envIMPECCABLE_PUBLIC_REPOoverrides). - Contract:
docs/CLI-CONTRACT.mdin the public repo. Every verb's argv, env, stdout/stderr, exit codes, files, network. - Goldens:
tests/oracle/in the public repo.node tests/oracle/run.mjswithIMPECCABLE_BIN=<path to our binary>replays every case and diffs. - Function vectors:
tests/oracle/vectors/calls/<module>/<fn>.jsonl, generated bynode tests/oracle/vectors/record-calls.mjs(needs the JS engine present).crates/core/tests/vectors.rsreplays them.
Rules
- Port the behavior, including bugs. Mark a knowingly-odd port with a
// JS-PARITY:comment naming the JS function and what it does. Never change a threshold, message string, or ordering while porting. - Improvements go through
tests/oracle/DELTAS.mdin the public repo, one line per case id, after review. Until then the golden wins. - JS number and string semantics live in
impeccable_core::js(number_to_string,to_fixed,parse_float,parse_int,trim,to_lower_case). Use them everywhere output text is produced; neverformat!("{}", f64)a value that ends up in stdout or a snippet. - Regexes:
regexcrate. JS features it lacks (lookbehind, backreferences,\bon unicode) get hand-written equivalents with a test. - Field order in emitted JSON matters. Use serde structs with the JS field
order, or
serde_json::Mapbuilt in order (preserve_orderis on). - Every crate that produces stdout does so through the
clicrate's writer so trailing newlines and stdout/stderr split match the contract exactly. - Exit codes are part of the contract.
std::process::exitonly incli. - No panics on user input.
panic = "abort"is set in release; a panic is a crash for the user. - Keep
corefree of I/O andstd::process; it compiles to wasm.
Crate boundaries
| crate | owns | must not |
|---|---|---|
| core | pure rules, color, registry, findings, inline ignores; the browser rules over the browser::dom::Dom probe trait |
touch fs/process/network |
| html | static engine: DOM model, cascade, adapters, text/regex engine, design system, visual | spawn browsers |
| browser | CDP client, browser discovery, page injection | parse HTML itself |
| context | context/doctor/pin/briefs/critique/palette/concept-seed/question/image/embed/signals/csp | |
| hook | hook, hook-before-edit, hooks admin | |
| live | live server and all live verbs | require Node except the documented Svelte/Vue exception |
| cli | argv router, exit codes, stdout writer | business logic |
| wasm | wasm-bindgen exports over core; JsDom (the probe over JS imports); see docs/WASM-BUNDLE.md |
Workflow per module
- Read the JS file top to bottom. List exports and internal helpers.
- Port helpers first, then exports, keeping names (
snake_case) and a doc comment/// JS: <file>#<name>. - Run the vector test for the module until green.
- If the module feeds a verb, build the verb, run the oracle for that verb's prefix, fix until green.
- Commit with a message that names the JS module ported and the vector/oracle
result. End with
Prepared with AI assistance (Claude Code).
Windows
The JS ran on Node, which picks path.win32 on Windows and path.posix
elsewhere. impeccable_common::jsp does the same: the top-level functions
(join, resolve, relative, dirname, basename, extname,
normalize, is_absolute, SEP) dispatch on cfg!(windows); the two
implementations sit under jsp::posix and jsp::win32, each with tests whose
expected values came from node -p "path.win32.X(...)". Rules:
- Use
jsp::*wherever the JS usedpath.*. Usejsp::posix::*only where the JS wrotepath.posix.*(tanstack-adapter's dirname, live'sposix_normalize). - Where the JS did
.split(path.sep).join('/')(display paths, glob matching, manifests another platform may read), the port callsjsp::to_posix. Where the JS built a prefix withpath.sep, usejsp::SEP/SEP_CHAR. Where the JS split on a literal'/', keep the literal: that is the behavior on Windows too, bugs included. resolve/relativetake an explicit cwd where Node readprocess.cwd(). Callers that only pass absolute paths may hand in"/"; on Windows that is the drive-less root, exactly whatpath.win32.resolve('/')yields with no cwd for the device.- Process plumbing that differs per OS lives in
impeccable_common::proc:kill0/pid_reachable(process.kill(pid, 0); OpenProcess on Windows),terminate(process.kill(pid)),detach(spawn({ detached: true }):setsidon unix,DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUPon Windows, so no console window),hide_windowfor short-lived helpers,on_interrupt(process.on('SIGINT'|'SIGTERM'); a console control handler on Windows),shell({ shell: true }:/bin/sh -corcmd.exe /d /s /c),node_exe(node.exe),tool_on_path(which/where). - Chrome discovery already carries the Windows candidate list; the launcher
for Windows is
launcher/impeccable.cmd.
Cross-checking from macOS or Linux: rustup target add x86_64-pc-windows-msvc
then cargo check --workspace --target x86_64-pc-windows-msvc. ring
(under ureq's rustls) compiles C for the target, so point cc at a clang
with the Windows CRT/SDK headers (cargo install xwin && xwin --accept-license splat --output ~/.xwin), e.g.
CC_x86_64_pc_windows_msvc=clang \
CFLAGS_x86_64_pc_windows_msvc="--target=x86_64-pc-windows-msvc -Wno-everything -I$HOME/.xwin/crt/include -I$HOME/.xwin/sdk/include/ucrt -I$HOME/.xwin/sdk/include/um -I$HOME/.xwin/sdk/include/shared" \
AR_x86_64_pc_windows_msvc=<a lib.exe stand-in: llvm-lib, or a script mapping "-out:X objs" to "ar crs X objs"> \
cargo check --workspace --target x86_64-pc-windows-msvc
CI runs cargo test --workspace on windows-latest (.github/workflows/ci.yml),
which is the only real Windows execution the project has. What a check
cannot show and only that job (or a Windows machine) can: the live server's
--background spawn surviving the parent's console, Ctrl-C reaching
on_interrupt, cmd.exe quoting for proc::shell scripts, and the Svelte
bridge finding node.exe.
The oracle goldens in the public repo were recorded on macOS: <WS> /
<HOME> masks assume /-separated paths, tests/oracle/lib.mjs compares
stdout byte-for-byte, and several cases stage posix-only workspaces
(core.hooksPath=/dev/null, sh steps). Running the oracle on Windows would
need the harness to normalize \ to / inside <WS>/<HOME>-masked paths
before diffing, drive-letter-aware masks, and a Windows recording pass for
the verbs whose output embeds path.sep (hook file lists, live manifests).
That work belongs to the public repo and is out of scope here.