Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Tooling and Git Hooks

The repository uses mise.toml to install the project hook runner and hk.pkl to define the hook behavior. criv.toml also declares project-level tooling files, including Hawk’s hawk.toml, as source roots. The decision record is ADR-0013.

Run the initial setup with:

mise install

The mise postinstall hook runs hk install --mise, so Git hook execution goes through mise x and uses the pinned tool versions, including hk, Rust 1.97.1, and actionlint. GitHub Actions security analysis is pinned here too: zizmor checks workflow, composite action, and Dependabot definitions for risky CI/CD patterns. Hawk uses the same Rust toolchain to check whether public Rust APIs are required by the shipped criv binary. Release tooling is also pinned here: Cocogitto calculates the next SemVer version from conventional commits, and cargo-release updates Cargo workspace versions. HK_PKL_BACKEND=pklr keeps hk self-contained by avoiding a separate pkl CLI requirement.

Workflow YAML under .github/workflows/ is checked with actionlint in pre-commit and the full check hook. zizmor runs in offline mode in the same hooks so local validation does not require a GitHub token or network access. This follow-up hook decision is ADR-0018.

Running criv from a hook runner

criv does not install Git hooks and does not set core.hooksPath, per ADR-0054. Setting that config replaces the hook directory wholesale, which silently disables whichever runner the repository already uses. Wire criv into your own runner instead.

The commands are stable:

StageCommands
commitcriv watch --once, criv check --changed, criv enforce --stage commit
pushcriv enforce --stage push

watch --once refreshes .criv/state.json before the changed check. The changed check is a staged, read-only partial fast path; plain criv check remains the full manual and hosted authority per ADR-0067. Run commit commands in the listed order.

With hk, as this repository does in hk.pkl:

local criv_check_changed = new Step {
  glob = List("**/*.md", "criv.toml")
  check_first = true
  check = "criv check --changed"
  fix = "criv check --fix"
}

local criv_enforce_commit = new Step {
  check = "criv enforce --stage commit"
}

hooks {
  ["pre-commit"] {
    fix = true
    steps {
      ["criv-check"] = criv_check_changed
      ["criv-enforce"] = criv_enforce_commit
    }
  }
}

With lefthook, in lefthook.yml:

pre-commit:
  parallel: false
  commands:
    criv-watch:
      run: criv watch --once
    criv-check:
      run: criv check --changed
    criv-enforce:
      run: criv enforce --stage commit

pre-push:
  commands:
    criv-enforce:
      run: criv enforce --stage push

Keep parallel: false for the commit stage: check reads the state watch writes, so they must not overlap.

Manual task entry points are:

mise run commit-msg -- .git/COMMIT_EDITMSG
mise run pre-commit
mise run pre-push
mise run fix
mise run perf
mise run vscode-build
mise run vscode-package
mise run release-plan
mise run release-auto

Pre-commit and pre-push are the automatic local validation boundary. Agents and contributors do not need to replay them with an aggregate command after each commit. The former local aggregate task has been removed.

Hosted CI invokes the complete hk check profile directly. Every step hk runs is defined in hk.pkl with its command inline, per ADR-0049. To inspect or debug one core step explicitly:

hk check --all --plan                        # list every step
hk check --all --step hawk

Pre-push runs Clippy, workspace tests, Hawk, and criv push enforcement. Hawk is kept in both pre-push and the hosted CI profile so unnecessary public Rust APIs fail before hosted CI during normal local development.

mise run perf generates isolated vaults from the checked-in barrs-small and criv-medium manifests, then measures an explicit release binary. Build that binary first and identify its profile at the command line:

cargo build --release
mise run perf -- --binary target/release/criv --profile release

The script records repeated samples for cold, warm, and changed watch --once, source-index file search startup, validation, CI enforcement, docs-only next-adr-id, orphan-docs, and nodes --kind doc queries, and a no-op snapshot diff. Five samples are collected by default; use --samples to change the count or repeat --case to select cases:

mise run perf -- --binary target/release/criv --profile release --samples 9
mise run perf -- --binary target/release/criv --profile release --case check

Every recorded sample receives a fresh generated vault. Warm cases perform an untimed state-building run inside that vault before the recorded command, and the changed case mutates the manifest’s declared number of supported source files without changing their sizes. A unique result directory preserves the run identity, exact manifest copies, raw JSONL samples, stdout/stderr, and a JSON summary with min/median/max and median absolute deviation. Failed samples remain in the raw evidence and make the harness fail.

mise run perf-container runs one ignored smoke case through Testcontainers. It builds criv and the harness inside a digest-pinned Rust Linux image. Docker is only an optional execution environment: the same checked-in manifests and generator define the vaults, results have their own container machine identity, and this task is not part of hooks or hosted CI. A Docker-API-compatible runtime is required. Large-workload measurement remains deferred.

Obsidian and VS Code validation are hosted-CI suites rather than automatic hk steps. Contributors changing a companion should run its package scripts directly. Run npm ci from the repository root first. The Obsidian companion commands are:

npm audit --workspace criv-obsidian-plugin --audit-level=high
npm --prefix .obsidian/plugins/criv run format:check
npm --prefix .obsidian/plugins/criv run lint
npm --prefix .obsidian/plugins/criv test
npm --prefix .obsidian/plugins/criv run build:wasm
npm --prefix .obsidian/plugins/criv run build:plugin
npm --prefix .obsidian/plugins/criv run build

The combined build preserves the release and manual entry point by running the Wasm build followed by the plugin build. The Wasm command invokes wasm-pack through mise exec cargo:wasm-pack@0.15.0, so the repository’s pinned tools are used rather than whichever Rust or wasm-pack appears first in the shell. It writes criv_wasm.js, criv_wasm_bg.js, and criv_wasm_bg.wasm under .obsidian/plugins/criv/pkg/. The plugin bundle keeps that runtime import external, so a distributable plugin must carry the generated pkg/ directory beside main.js.

If Obsidian reports that the criv Wasm runtime is unavailable, run the combined build, confirm those three runtime files exist, and reload the plugin. A state schema or parse error is separate: regenerate .criv/state.json with criv watch --once instead of rebuilding Wasm.

The VS Code-compatible extension lives in extensions/vscode-criv. Its local tasks are exposed through mise and npm:

npm audit --workspace vscode-criv --audit-level=high
npm --prefix extensions/vscode-criv run build
npm --prefix extensions/vscode-criv run test
npm --prefix extensions/vscode-criv run test:integration
npm --prefix extensions/vscode-criv run lint
npm --prefix extensions/vscode-criv run format:check
npm --prefix extensions/vscode-criv run package

The integration command launches the VS Code extension test host and fails on JSON diagnostics for extension manifest and language configuration files. Its short temporary user-data and extension-installation paths avoid macOS IPC path limits. Hosted CI runs this command under Xvfb; it is not part of local hooks.

The extension uses a read-only LikeC4 preview as the default .c4 editor. It selects the named view owned by the opened file. There is no fallback view: a file that owns none states so and points at a file that declares a view. Navigating inside the diagram opens the file that owns the target view in the same editor group, so the tab tracks the diagram. Use Reopen Editor With → Text Editor to edit the DSL. The preview webview uses packaged extension resources and does not use a CDN or a global LikeC4 command. Run criv watch --once after architecture or extension source changes. This keeps architecture state current. The repository Code model and views are hand-authored under docs/architecture/code/, so watch does not replace them.

npm --prefix extensions/vscode-criv run package and mise run vscode-package build a local vscode-criv.vsix without publishing. mise run vscode-install builds that VSIX and installs it into the local editor; reload the window afterwards. The prepublish hook builds the Node.js Wasm target into extensions/vscode-criv/pkg/; the VSIX includes criv_wasm.js and criv_wasm_bg.wasm from that directory. If the extension reports an unavailable Wasm runtime, rebuild build:wasm, recreate the VSIX, confirm those files are present in the package, and reload the editor window. Regenerate invalid state with criv watch --once; do not treat a state-validation error as a runtime packaging failure.

The optional viewer is local-only under ADR-0087. The project does not publish it to the VS Code Marketplace or Open VSX. Release archives put the one viewer package next to the criv executable. Install it with an explicit editor selection:

criv install-editor --editor code
criv install-editor --editor cursor

Use --dry-run to validate the bundled viewer and local editor command without changing editor state.

Use hk validate after editing hk.pkl. Prefer hk built-ins when one exists; the commit message hook uses Builtins.check_conventional_commit instead of a shell wrapper around hk util check-conventional-commit.

The hawk hk step runs Hawk with warnings denied against the shipped criv CLI. The criv-wasm crate is excluded because its exported functions are consumed by the separately built WASM artifacts, outside the CLI binary’s Cargo graph. The pre-push and full check hooks run Hawk whenever Rust sources, Cargo metadata, or its configuration change. Hawk uses target/hawk for its instrumented Cargo artifacts so its compiler work stays isolated from the other parallel checks. This policy is captured in ADR-0043 and the local/hosted validation boundary is ADR-0061.