Every PR ships the runnable evidence that the fix passes.
A cloud bot that fixes your GitHub issues and opens a proven PR. You review, you merge. The bot opens it on a heimdall/* branch as a scoped GitHub App — never as you, never on main, and it never self-merges. A human always gates the merge.
Once you've installed the Heimdall Maintainer GitHub App on your repo and run claude setup-token, it's two commands:
rr connect # registers your App install + captures your Claude cred
rr "fix the flaky test in payments and open a PR"What happens: rr signs your task with your own Ed25519 key and enqueues it. A gated worker clones your repo with your team's Claude subscription and your GitHub App installation, runs the issue-resolution loop until the fix passes the gates, and opens a heimdall/* PR on your repo. You review it. You merge it.
Nothing to paste — no token, no URL. The public control plane is baked in and enrollment is automatic: your first signed call registers this device on first use. Just rr connect and go. (Running your own deployment, or need to re-gate enrollment behind a bootstrap token? That's an operator concern — see OPERATORS.md.)
- Tenant isolation is a falsifiable oracle, not a promise. Every cross-tenant attack — IDOR by repo slug, cred read across teams, queue drain, installation-id swap, signed-request replay — has a named invariant and a red-line mutant test. Drop any gate and
test/heimdall-cp-authz-gate.test.shgoes red; the keystone suite passes only when every mutant is caught. Full invariant + attack matrix:docs/specs/2026-07-03-rr-isolation-invariants.md. - BYOC — no shared keys. You pay your own Claude tokens; your credential lands in your own per-team Secret Manager secret and is injected env-only into your job — never logged, never echoed, never readable by another tenant.
- Least-privilege bot. The App holds exactly Contents + Issues + Pull requests — no Administration, no Actions/Workflows, no merge capability. It can open a PR; it cannot touch branch protection or push to
main. - Honest bring-up. This loop was hardened over a live multi-tenant bring-up that shook out a run of production-only failures — Google's GFE rejecting GET-with-a-body, cold-start identity drift, jobs starving under scale-to-zero — each now documented as fixed in
deploy/cloud-run/README.mdand the runbook.
The bot is powered by Heimdall's local engine: a Claude Code plugin that turns one prompt into finished, proven work. Every plan wires an external, falsifiable oracle so the implementation can never grade its own homework, and the merge stays blocked until the work is proven correct. Install it to run the same gates on your own machine.
Three paths. Each is labelled with what it actually does to your machine — pick the risk you are willing to take, not the shortest command.
| # | Path | What it runs | Honest risk |
|---|---|---|---|
| 1 | One-liner (below) | Downloads install.sh, checks its sha256, then runs it as you |
Highest. A script fetched over the network, executing with your user's privileges — it can do anything you can do. The digest check is the only thing between you and whatever those bytes are. Verify it, or take path 3. |
| 2 | npm — npx runheimdall |
The same install.sh, fetched over https and sha256-checked against a digest baked in at publish time (bin/runheimdall.js), then handed to bash |
Same as path 1. The wrapper is thin and the script still runs as you. You gain not having to hand-copy a digest. You gain no isolation. |
| 3 | Docker sandbox — Dockerfile.install |
The same install.sh, copied from your own clone — nothing fetched, no digest to trust — run inside a container |
Lowest, with real caveats. Every $HOME change lands in a layer you delete. It does not isolate a repo you mount, and it is not a macOS sandbox. Caveats below. |
Whichever you pick, this is what the installer writes outside the repo. Measured from a real run against a throwaway $HOME, not asserted:
| Written | What it is |
|---|---|
~/.heimdall/ |
A full git clone of this repo — the installed checkout hmd runs from |
~/.local/bin/hmd, ~/.local/bin/heimdall |
Symlinks into ~/.heimdall/bin/heimdall |
~/.claude/settings.json |
Adds statusLine + subagentStatusLine, and registers the plugin under enabledPlugins / extraKnownMarketplaces. Honors $CLAUDE_CONFIG_DIR, and never clobbers a statusLine you set yourself |
~/.zshrc / ~/.bashrc / ~/.profile |
One appended export PATH=… line for ~/.local/bin — only when it is not already on PATH |
~/Library/LaunchAgents/com.heimdall.dream.plist |
macOS only — a nightly 03:00 LaunchAgent (com.heimdall.dream) that runs the overnight sweep with no session open and survives logout and reboot. Opt out with HEIMDALL_NO_DREAM_SCHEDULE=1 |
~/.heimdall/team.json |
An auto-minted solo team secret — a bearer capability. Treat it like a credential |
No sudo. Idempotent — re-run to upgrade. hmd uninstall reverses all of it.
Network posture is default-ON. Team presence and the cloud features reach the control plane as soon as you use them: a signed heartbeat carrying your handle, verdict, and current filename — scoped to your team, never your code or file contents. That is a feature, and it is on until you switch it off. hmd presence sever gives zero egress. Field-by-field contract: DATA.md. The precisely scoped claims are under Your code stays yours.
hmd's default module set includes a proxy that can read your prompts, and hmd wrap routes generation traffic through it. Headroom is default_included: true in modules/headroom/manifest.json: a local context-compression proxy which, once traffic is pointed at it, sits between your coding tool and the model provider, reads the prompts and context on their way out, and rewrites them to be smaller. hmd modules add headroom installs the package, then measures each wire the manifest declares and reports what it found: the wrap chain measures ROUTED, so a tool launched via hmd wrap <tool> has its GENERATION traffic carried through the proxy. Bare hmd does not route. The storage-codec wire still measures RECORDED, not routed. JUDGMENT never traverses it — every verdict-producing call is scrubbed back to the real provider, which test/gate-judgment-uncompressed.test.sh goes red on. It runs as a process you own and can inspect, and it introduces no Heimdall-operated destination — that traffic goes to the same provider it went to before. Five things to know before you install:
- Nothing installs it for you.
install.shhas no module code path at all, and the background updater refuses to acquire a consent-required class unattended — it names the module and hands you the command. Until you runhmd modules add headroom,hmd modules status headroomreportsNOT ATTEMPTED. - That command is a remote code install, and no digest is verified. It runs
uv tool install --python 3.13 "headroom-ai[all]==<pin>"against PyPI. hmd hashes nothing on that path and does not claim to: the lifecycle step is namedinstall + provenance, notdigest-verify, and the receipt recordsverified: falsealongside the pin it did not check. It also pulls an ML stack — Rust wheels, an ONNX runtime, HuggingFace tokenizers — so this is the one place hmd stops being near-stdlib. - The consent question is waived; the disclosure is not.
consent_waivedsits on that one module's manifest.modules/_classes/traffic-proxy.jsonstill readsconsent_required: true, so every other traffic-proxy module hmd ships still asks. The consent text still prints, both declared class contracts still run their invariants, and the receipt recordsgranted_via: manifest-waiver. This is a deliberate maintainer decision: disclosed, not asked. - Installing it is not wiring it, and hmd measures the difference out loud. The manifest declares two wires and hmd applies neither.
[6/7] wireprints each one asRECORDED, not routednext to the measurement that produced it —bin/heimdall-wrapholds no reference to the module, and the memory-codec seam reportsbackend=plain— andhmd modules status headroomreads back that same record. A declared wire whose kind the code has no handler for is refused at validate rather than quietly recorded, so a module cannot install while claiming a capability hmd does not deliver (test/wire-kind-dispatch.test.sh). - Gates read raw, and signed traffic steps around it. Route generation through a proxy — this one, or your employer's — and judgment still may not follow it. Verdict-producing commands run through
hmd_gate_exec, and control-plane, enrollment and presence traffic throughhmd_signed_exec(bin/lib/hmd-gate-endpoint.sh); both dropANTHROPIC_BASE_URL, the proxy pairs and the wholeHEADROOM_*namespace before pinning the endpoint to the real provider.test/gate-judgment-uncompressed.test.shgoes red the moment a gate request reaches the proxy.
hmd modules remove headroom returns the tree byte-identically. Threat model, the full reachability table and every way to decline it: SECURITY.md. Mechanics and honest limits: Modules.
curl -fsSL https://raw.githubusercontent.com/randomittin/heimdall/v2.3.9/install.sh -o heimdall-install.sh \
&& echo "73c89c9400205a7d32178f7e91854d836f9702708417079b08faea63fa301196 heimdall-install.sh" | shasum -a 256 -c - \
&& bash heimdall-install.shPinned to the v2.3.9 tag and checked against the sha256 of that tag's install.sh. The &&
chain is load-bearing: if the bytes do not match the digest, shasum -c prints FAILED and
nothing runs — so a tag moved under you, a CDN cache poisoning, or a truncated download stops
the install instead of executing. On a Linux box without shasum, sha256sum -c - takes the same
digest. Re-derive it yourself any time:
curl -fsSL https://raw.githubusercontent.com/randomittin/heimdall/v2.3.9/install.sh | shasum -a 256
# 73c89c9400205a7d32178f7e91854d836f9702708417079b08faea63fa301196No sudo. Idempotent — re-run to upgrade. Reversible:
hmd uninstall # removes everything; nothing else was touchedPrefer to inspect first? Same download, same check — just read it before you run it:
curl -fsSL https://raw.githubusercontent.com/randomittin/heimdall/v2.3.9/install.sh -o heimdall-install.sh
echo "73c89c9400205a7d32178f7e91854d836f9702708417079b08faea63fa301196 heimdall-install.sh" | shasum -a 256 -c -
less heimdall-install.sh # function-wrapped, no eval, no base64 — what you read is what runs
bash heimdall-install.shSignature (stronger than the digest). A digest you copy from this README only proves the bytes
match what this README says; a signature proves they came from the maintainer's key. Every release
signs install.sh with minisign and publishes install.sh.minisig as a release asset:
curl -fsSL -O https://github.com/randomittin/heimdall/releases/download/v2.3.9/install.sh.minisig
minisign -Vp release/heimdall-signing.pub -m heimdall-install.sh -x install.sh.minisigThe public key ships in this repo at release/heimdall-signing.pub, so that command assumes a
clone. SIGNING.md has the full model — including the bundled pure-python verifier
for machines with no minisign binary, and the fail-closed behavior of the auto-updater.
Prerequisites: Claude Code 1.0+ · Git · jq (brew install jq)
npx runheimdall — same pinned tag, same sha256 check, zero clone required. It fetches the pinned install.sh, verifies it against the digest baked in at publish time, and aborts before executing anything if the bytes disagree. Convenience, not containment: what finally runs is the same script, with the same privileges as path 1.
For a first look that does not touch your machine. Build from a clone, so the install.sh you read is byte-for-byte the one that runs — nothing is fetched, so there is no digest for you to trust:
git clone https://github.com/randomittin/heimdall && cd heimdall
less install.sh # what you read is what runs
docker build -f Dockerfile.install -t heimdall-sandbox .
docker run --rm -it heimdall-sandbox # hmd is already on PATH--rm discards every $HOME mutation in the table above the moment the container exits. The image also drops the auto-minted team.json during the build, so containers never share one team secret.
What the container does not isolate — a sandbox you misunderstand is worse than no sandbox:
- A mounted repo is not isolated.
-v "$PWD:/work"is a hole you punched on purpose: anythinghmdwrites under/worklands on your real disk. Mount:roif you only wanthmdto read your code. - It is not a macOS sandbox. The image is Linux, so launchd and the keychain do not exist inside it and the nightly LaunchAgent step is skipped as
unsupported. That is the container being a different OS — not a boundary defending your account.launchctland the keychain are account-scoped: a fake$HOMErelocates only the plist file, whilelaunchctl loadstill registers the job in your real per-user session. A sandboxed test in this repo learned that the hard way, by rewriting the developer's live LaunchAgent. The switch isHEIMDALL_NO_DREAM_SCHEDULE=1— never$HOME. - The network is open. The build clones from GitHub, and presence is on by default.
docker run --network nonegives the container zero egress;hmd presence severdoes the same at the application level.
Gates run 100% locally. Your code never leaves your machine.
Team presence is a feature you can see and switch off: it sends {handle, verdict, current filename} — never code, never file contents — to your team's endpoint. hmd presence off makes you invisible; hmd presence on --no-files hides filenames.
Telemetry is specified, minimal, and yours to kill: DATA.md documents every field. hmd telemetry off. hmd telemetry purge deletes the local spool — nothing is transmitted in this release.
Auto-update checks GitHub Releases for new signed versions. HEIMDALL_NO_AUTOUPDATE=1 (or ~/.heimdall/no-autoupdate) disables it.
rr is the one thing that sends on purpose, and only when you run it: your BYO Claude credential (write-only), your GitHub App installation id, and the literal task text you typed — because that text is the job. It never uploads your working tree; the worker clones your repo from GitHub.
Full data contract and every field collected: DATA.md.
hmd demo --runScaffolds a real full-stack task, builds it, ends with a summary card and a follow-up prompt. Safe to run sight-unseen — hmd demo (without --run) prints the plan and does nothing.
- Catches the silent failures — ordering races, whole-sequence invariants, missing subsystems that pass a naive green suite.
- Falsifiable gates — every gate is proven able to go red before it is trusted green. The corpus of real failure cases replays on every change; a regression that once shipped can never ship twice.
- Proof of correctness, not just generation — the delta Heimdall sells is the receipt that proves the proof can fail. Generalizes: 0.50 median reuse across 8 cold repos.
- Full audit trail —
hmd reportproduces a machine-readable telemetry report of every gate, mutation score, and corpus catch-rate from the last run.
| Capability | Command | Status |
|---|---|---|
| Verification gates (secret-scan, bloat, falsify) | /hmd:verify in Claude · bin/falsify |
Shipped |
| Demo task runner | hmd demo / hmd demo --run |
Shipped |
| Issue-resolution loop | hmd (auto-retries failures against corpus) |
Shipped |
| Telemetry report | hmd report |
Shipped |
| Design match (visual diff vs spec) | hmd designmatch |
Shipped |
| Redum / conformance checker | heimdall-redum · heimdall-check |
Shipped |
| Reuse engine (cold-repo analysis) | bin/lib/reuse_analyzer.py |
Shipped |
| Debloat scanner | heimdall-debloat --report-only |
Shipped |
| Parallel workers | hmd --team N "task" (N tmux panes, independent — no shared state) |
Shipped (no coordination layer) |
| Benchmark suite | heimdall-bench |
Shipped |
| Optional capability modules | hmd modules |
Shipped |
Optional capability modules. The base install ships the module system and zero module payloads — modules/ holds manifests and class contracts, never vendored code. The registry is this repo, so every pin is a reviewed commit rather than a network lookup, and there is no code path that resolves "latest". Nothing self-installs.
hmd modules # list — honest when nothing is installed
hmd modules add <name> # the full ordered pipeline
hmd modules remove <name> # total removal
hmd modules update [<name>] # move to the manifest's human-set pin
hmd modules status <name> # one module in detail
hmd modules verify [<name>] # re-run class invariants (the CI entry point)
hmd modules preflight <name> # can this install happen? read-only, asks nothing
hmd modules repair <name> # retry an install, recording which stage failed
hmd modules defer <name> # not now — hmd is ready, the module follows later
hmd modules pending # what is deferred or awaiting a retry
hmd modules optout <name> # decline; no install path may re-install it
hmd modules optin <name> # undo an optoutEvery verb accepts --json; add accepts --yes. --registry and --state relocate the registry and install state, which is how the suite runs hermetically.
A module declares its class — or several — in its manifest, and a missing or unknown class is refused, not defaulted: the class decides consent and which invariants are enforced, so picking one silently would be picking a security posture on the operator's behalf. When a module declares several, the union of their invariants runs, never the first match.
| Class | Consent | Invariants enforced |
|---|---|---|
traffic-proxy |
required | gates read raw · non-interactive passthrough · signed and control-plane traffic never routed through it |
tool-adapter |
required | wrap/unwrap byte-identical · hooksPath and AGENTS.md fences preserved |
storage-codec |
not required | round-trip fidelity · plain fallback when absent · never touches judgment inputs |
rule-pack |
not required | rules ship falsifiers · attribution preserved |
The two classes that mutate something you own — the wire, and your own config files — ask. The two that are transparent by contract do not, and their invariants are what make that transparency true rather than asserted: a codec that loses a byte, or a pack that flattens attribution, is rolled back rather than merely disclosed.
[1/7] validate → [2/7] class contract → [3/7] preflight → [4/7] consent
→ [5/7] install + provenance → [6/7] wire → [7/7] class invariants (module active)
Steps 1–4 are read-only, so anything rejected at validate, class, preflight or consent mutates nothing at all. Preflight sits before consent on purpose — nobody should be asked to agree to an install that cannot happen. Step 5 is the first mutation, and from there every failure unwinds through the same removal path remove uses, so a module that fails its own class test leaves a byte-identical tree. Wiring precedes invariants deliberately: the contracts assert behaviour with the module active, so a check run against an unwired module would prove nothing.
Step 5 is called provenance, not digest-verify, and the naming is load-bearing. A digest is verified on exactly one path: a local module, whose artifact ships in this repo and is hashed here against its pin. An upstream module is fetched from a package index and hmd hashes nothing — it records the pin and asks the installer whether the payload arrived. The receipt reads verified: false in every upstream state, and nothing re-checks that pin later: hmd modules verify re-runs the class invariants and reads no digest at all. Printing digest-verify over that would assert a check that never ran.
An install that required consent records how it was granted — granted_via is one of interactive, --yes, or manifest-waiver — alongside the exact consent_text shown and its consent_text_sha256. Consent required with a non-TTY stdin is refused, not prompted and not defaulted to yes; --yes is the operator saying so on purpose.
A module in the default set reaches machines the author will never meet, so default_included: true is refused at validate time if any class it claims requires consent and the manifest ships no consent_text. A disclosure that only materialises at a prompt somebody may never be shown is not a disclosure.
Two surfaces read opt-out signals, and they do not overlap — each verb honours the signals of the surface it belongs to:
| Signal | Read by | Effect |
|---|---|---|
HEIMDALL_NO_MODULES=1 |
bin/heimdall-autoupdate |
no module is acquired automatically, at all |
~/.heimdall/modules-optout |
bin/heimdall-autoupdate |
one module name per line; # starts a comment |
hmd modules optout <name> |
bin/heimdall-modules |
persisted; repair, defer and pending all refuse to act |
HMD_MODULE_OPTOUT=a,b |
bin/heimdall-modules |
the same, for one invocation |
install.sh acquires no modules at all — the installer has no module code path, so there is no installer flag to suppress and none is offered. Automatic acquisition lives in the background updater, which is why HEIMDALL_NO_MODULES=1 bites there. The consequence, stated plainly: every one of these signals governs acquisition that happens on your behalf. HEIMDALL_NO_MODULES=1 suppresses automatic acquisition and does not block an explicit hmd modules add <name> — an operator typing the command themselves is not what an opt-out for unattended installs is trying to stop.
modules/headroom/manifest.json pins Headroom (Apache-2.0), a local context-compression proxy. Depend, don't clone — none of its source is vendored here. The manifest is the single source of truth for the pin and its artifact digest; this page deliberately does not restate the version, because a second hand-maintained copy of a pin is how pins drift.
It is default_included, and its consent question is waived on the module itself. modules/_classes/traffic-proxy.json still reads consent_required: true, so every other traffic-proxy module hmd ever ships still asks; the waiver's blast radius is this one module. Waived is the question, never the disclosure — the consent text still prints, both declared classes still run their invariants with the module active, and hmd modules remove headroom still returns the tree byte-identically.
Being in the default set is a distribution fact, not a claim that it helps, and not an unattended install:
- The background updater will not install it for you. Acquisition reads the class contract, and
traffic-proxyrequires consent — soheimdall-autoupdatenames the module, states the class, and hands youhmd modules add headroomrather than acquiring it. Until you run that command,heimdall-autoupdate statusreports itabsentwith the reason andhmd modules status headroomreportsNOT ATTEMPTED. - It is the one place hmd stops being near-stdlib. The fetch is
uv tool install --python 3.13 "headroom-ai[all]==<pin>", which pulls an ML stack — Rust wheels, an ONNX runtime, HuggingFace tokenizers. Install size and time are materially larger than hmd's own. - A failed Headroom install does not fail
hmd install. hmd works; the module reports ABSENT with the blocker and the remedy named, and rolls back through the removal path. It is never silently assumed present. - The storage-codec half does not engage via the documented install.
uv tool installlands the package in an isolated per-tool venv that hmd'spython3cannot import, so the memory codec stays on itsplainbackend on every machine that installs it the sanctioned way. The manifest records that as a measurement rather than letting a greenaddimply otherwise. Treat compression on the storage seam as a contract the seam honours if a backend ever arrives — not as a description of any running machine. - The proxy half is declared, not applied — hmd points no traffic at it.
bin/heimdall-wrapcontains no reference to the module, so installing Headroom leaves your generation traffic going exactly where it went before. Thewrap-chainwire records the chain hmd would offer; wiring it up is yours to do, deliberately, by pointing your tool at the proxy. hmd measures that wire at everyaddand printsRECORDED, not routedwhile it stays unapplied, and a wire kind with no handler behind it is refused outright rather than recorded — an install may not imply a capability the code cannot deliver (test/wire-kind-dispatch.test.sh). - Gates read raw. Generation traffic may traverse a proxy once you route it through one; judgment traffic may not follow. Every verdict-producing execution runs through
hmd_gate_exec, which unsetsANTHROPIC_BASE_URL, the HTTP/HTTPS/ALL/NO_PROXY pairs and Headroom's ownHEADROOM_*namespace before pinning the endpoint to the real provider. A judge reading compressed context emits confident false greens, which is the failure this whole project exists to prevent.
Full manifest schema, class-contract details and the lifecycle rationale: modules/README.md.
Heimdall's status bar is a full-width, four-row watchman HUD. It renders entirely shell-side (zero model, zero context cost) and reads Claude Code's statusLine JSON on stdin. Three surfaces, by how far they spread:
Your sigil — the identity hook. Every Heimdall identity (HAID) gets a unique, deterministic pixel watchman: same identity, same sigil, forever. It anchors the left of the line, prints big on the install card, and shares as a postable block:
python3 sentinels/hmd-sigil.py --seed $HMD_HAID --size large # share/banner render
bash hooks/hmd-banner.sh --share # postable "my watchman" cardThe seed is your HAID by default — automatic, stable, no PII in the art. Works solo on day one, before any teammate shows up.
The team watch wall — the headline, and the moat. When teammates also run hmd in the same repo, the bottom row becomes a live wall of their watchmen and what each agent is doing — gate state colored in, a teammate's cell flashing red the instant their gate denies. Nobody else can render this; it needs Heimdall's coordination substrate. The wall is empty until your team joins, so the feature recruits your team for you.
Presence is opt-in per repo — each running hmd heartbeats <repo>/.heimdall/team/<haid>.json (TTL ~30s; a stale file means the agent left). Names live in the repo's team dir and never leave it; default off for non-team repos. The watchman watches your gates, not your team. At squad scale the wall caps at the ~6 most-recently-active teammates plus a +N more tail so a wide terminal never wraps.
The deny flash — the clip. When a gate blocks, hmd-gate-anim.sh redraws the big watchman inline: a scanning pulse settling to a green sparkle on pass, or three red beats and ✗ BIFRÖST CLOSED on deny. TTY-only — in CI or a pipe it collapses to one clean final frame so logs stay readable.
bash sentinels/hmd-gate-anim.sh deny "oracle/falsify" $HMD_HAIDWiring (settings.json):
{
"statusLine": {"type":"command","command":"bash ${CLAUDE_PLUGIN_ROOT}/hooks/statusline.sh"},
"subagentStatusLine": {"type":"command","command":"bash ${CLAUDE_PLUGIN_ROOT}/sentinels/hmd-subagent-statusline.sh"}
}install.sh wires this for you — it registers both entries into your ~/.claude/settings.json (honoring $CLAUDE_CONFIG_DIR) using the absolute installed path, idempotently and without clobbering a statusLine you set yourself. The ${CLAUDE_PLUGIN_ROOT} form above is the plugin-hook spelling; a user-level statusLine resolves no such variable, so the installer fills in the resolved absolute path — which is why the HUD now reaches every dev, not just whoever hand-wired it in dev setup.
hooks/statusline.sh drives the full-width watchman and falls back to the legacy single line if python3 is missing — it never errors, never blocks. Already a ccstatusline (9.2k★) user? Keep your line and drop the watchman in as a Custom Command widget:
python3 sentinels/hmd-statusline.py --widget # just the watchman + verdict segmentThe sigil ships solo-first (viral-cheap, no team required); the watch wall is the team-gated headline that lights up once presence is wired into your gate hooks.
cd /path/to/your/project
heimdall --auto "build a real-time dashboard with auth and charts"--auto runs a background safety classifier that blocks prompt injection and risky escalation. It is the default. --dangerously-skip-permissions exists but is not the default — only use it in a throwaway sandbox.
Live flagship status: evals/flagship/STATUS.md — the ❌ rows are kept in view. The corpus dip log and golden provenance are at evals/corpus/CORPUS-STATUS.md and evals/oracles/emulator-gb/fixtures/golden/VERIFICATION.md.
A verification system that can't show you its own failures can't be trusted with yours.
- Stack packs (
skills/stacks/) — teach Heimdall a framework's conventions and build commands. - Oracle packs (
evals/oracles/) — add a falsifiable external gate for a new domain.
See CHANGELOG.md for release history.
hmd keeps itself and its host current, in the background, on session start — both are throttled (~24h), detached (never block the session), idempotent, and opt-out:
- Plugin auto-update (
bin/heimdall-autoupdate): checks the installed version vs the latest GitHub release; if newer, re-runs the latest installer in the background (takes effect next launch; never hot-swaps the running session). Off:HEIMDALL_NO_AUTOUPDATE=1or~/.heimdall/no-autoupdate. - Claude Code self-heal (
bin/heimdall-cc-selfheal): on a NATIVE Claude Code install, auto-repairs the "✘ Auto-update failed" class — a stale npm-global@anthropic-ai/claude-codeconflicting with the native updater. It removes ONLY that conflicting package, ensuresautoUpdates:true, and re-runsclaude update. Never touches an npm/brew-managed install, never uninstalls anything else, never touches credentials. Off:HEIMDALL_NO_SELFHEAL=1or~/.heimdall/no-selfheal. Inspect:heimdall-cc-selfheal status. - Default module reconciliation (same updater): compares the installed modules
against the default set. A module whose class requires consent is never acquired here —
it is named, with the
hmd modules addcommand to run. Off:HEIMDALL_NO_MODULES=1or~/.heimdall/modules-optout. Inspect:heimdall-autoupdate status.