Imported from burin-labs/harn (
AGENTS.md). Install upstream withnpx skills add burin-labs/harn. Copyright stays with the author.
AGENTS.md
Harn is a programming language and runtime for orchestrating AI agents.
This file is the repository contract for Codex, Claude, and other coding
agents. CLAUDE.md must remain a symlink to it. The durable design principles
live in Engineering principles:
- ambitious outcomes behind boring seams;
- one owner with many projections;
- structure over prose;
- evidence matched to claims;
- canonical paths;
- controllable autonomy;
- cross-surface convergence;
- operationally complete launches.
Ownership
- Harn owns orchestration, transcript lifecycle, replay/eval, delegated-worker lineage, capability policy, and mutation-session audit metadata.
- Hosts own native presentation, approval UX, concrete file mutations, and undo/redo semantics.
- Prefer the Harn runtime and stdlib over new host glue.
- A semantic decision has one owner. CLI, TUI, IDE, headless, and cloud behavior should be projections or adapters, not parallel implementations.
- Prefer deep modules: small interfaces that hide substantial behavior.
- Make contracts structural with types, registries, events, generated projections, and drift checks. Prose should explain the contract, not enforce it.
Working agreement
- Research, implement, verify, recover, and ship autonomously inside the approved scope.
- Pause for genuine ambiguity, destructive or out-of-scope action, production impact, exceptional spend, or new authority—not routine reversible work.
- Treat stop, wait, stand down, and pivot as control events. Do not continue stale work after one arrives.
- Start from the claim and name a plausible falsifier. Match evidence to the claim; test counts alone are not proof.
- Exercise the canonical user path for product claims and progress, interruption, recovery, and terminal state for liveness claims.
- Use multiple trials and calibrated graders for stochastic quality claims.
- "Ship" means landed on the intended main branch with required post-merge or release checks complete.
Harn scripts
- Before writing or editing
.harn, runharn skill list --json. - Fetch the narrowest guide with
harn skill get <name> --full. - Use
harn-languagefor syntax, modules, types, andllm_call;harn-orchestrationfor triggers, workers, personas, and agent loops;harn-testingfor fixtures; andharn-product-qualityfor user-facing flows. - Fallback references are
docs/llm/harn-quickref.mdanddocs/llm/harn-triggers-quickref.md. - Default new scripts to
fn main(harness: Harness) { ... }. - Route capabilities through
harness.*. - Claude Code gets the same version-matched reminder through
.claude/skills/harn-scripting/SKILL.md.
Setup and resource isolation
- Run
make setupon a fresh clone and in every new worktree. It installs hooks and tools, configures sccache and a private target directory, then runs a locked build of the canonical Harn CLI so later product-path commands and focused tests reuse the same linked dependency graph. - A raw
git worktree addhas no.cargo/config.toml; without setup, cold Cargo probes can time out. Reuse an existing binary withHARN_BIN=<path> HARN_BIN_NO_BUILD=1or explicitly allow a cold probe withHARN_BIN_CARGO_TIMEOUT_SECONDS=3600. Only when the compiler wrapper is genuinely wedged, opt into one wrapper-disabled retry withHARN_BIN_RETRY_WITHOUT_WRAPPER=1. - Never share a mutable Cargo
target-dirorbuild-diracross concurrent worktrees. Every setup profile derives one stable per-worktree target under${XDG_CACHE_HOME:-$HOME/.cache}/harn/dev-setup/harn-target/and configures sccache for compiler-object reuse. Because released sccache versions include Rust's absolute compilation directory in the cache key, setup also restores an immutable toolchain-keyed Cargo target seed with filesystem copy-on-write. The seed keeps third-party dependency artifacts but removes workspace binaries, fingerprints, dep-info, and incremental state before publication and after restore, so every lane rebuilds Harn against its own source tree. Each lane gets private files; unsupported filesystems simply take the cold build path. A successful canonical CLI build publishes the seed once; an 8 GiB ceiling prevents an accumulated multi-profile target from becoming the permanent seed. HARN_DEV_TARGET_WORKTREE_PATHandCODEX_WORKTREE_PATHmust name the checkout being configured. Setup ignores stale sibling-worktree values.- Setup phases are fingerprinted under
.codex/dev-setup/; useHARN_DEV_SETUP_FORCE=1 make setuponly to refresh them. - Codex staging worktrees use
make setup-bootstrap; runmake setupormake setup-rustin the final task worktree. - Claude session startup delegates to the same setup path through
scripts/claude-dev-setup-once.sh. It blocks only on thebootstrapprofile, and warms the compiling phases in the background. An early build can therefore wait on that warm..claude/dev-setup/latest.logrecords it. - Keep installed hooks on. Use
HARN_HOOKS_FULL_LOCAL=1for build-backed local gates andHARN_PREPUSH_FULL_TESTS=1for the broader pre-push suite. - The shared Codex and Claude shell guard rejects raw Cargo build/test commands
and build output piped directly into filters. Use Make targets; redirect
output to a file before filtering.
HARN_ALLOW_RAW_CARGO=1is the explicit one-off escape. See Agent shell guard.
Repository map
crates/harn-lexer,crates/harn-parser: tokens, AST, parser, type checker.crates/harn-stdlib,crates/harn-vm: embedded stdlib, compiler, VM, providers, orchestration, transcripts, bridge, and ACP.crates/harn-cli: CLI, conformance, portal server, MCP/OAuth, A2A/ACP, replay, and eval tooling.crates/harn-lint,crates/harn-fmt,crates/harn-lsp,crates/harn-dap: language tooling and editor/debugger integration.crates/harn-cli/portal/: React/Vite persisted-run UI.conformance/tests/: executable language/runtime specification.spec/chapters/*.md: canonical spec sources.docs/src/,website/: documentation sources and harnlang.com.tree-sitter-harn/,editors/vscode/: grammar and VS Code extension.crates/harn-kernel: canonical compiler, versioned program artifact, deterministic portable runtime, and runtime type contract.crates/harn-wasm: workspace-owned browser adapter; verify its generated bindings, authority imports, and real worker path withmake wasm-check.
Source ownership and generated files
- Edit
spec/chapters/*.md, then runmake sync-language-spec; do not edit generatedspec/HARN_SPEC.mdordocs/src/language-spec.md. - Generate
docs/theme/harn-keywords.jswithmake gen-highlight. - Generate protocol artifacts with
make gen-protocol-artifacts; onlyspec/protocol-artifacts/*_test.gois hand-edited. scripts/generated_artifacts.tomlis the source of truth for every gen/check pair and everycheck-*preflight classification. A new artifact needs Makefile gen/check targets, a registry entry,make all, and CI wiring.- Classify checks as
source,binary, orexcluded. Source checks read committed files; binary checks require a fresh Harn executable. - Generated/local paths include
docs/dist/,.harn-runs/,.harn/,.harn/receipts/,.claude/,.burin/,target/, andnode_modules/. - The prompt-template engine is
crates/harn-vm/src/stdlib/template/mod.rs. Host and script rendering both userender_template_result; do not add another parser or evaluator. - Preserve pre-v2
{{name}}missing-identifier passthrough. New constructs fail with parse errors. Vocabulary lives incrates/harn-vm/src/stdlib/template/vocabulary.rs; regenerate withmake gen-prompt-grammar. - Keep stdlib registration authoritative. Register builtins with
#[harn_builtin]; linter and editor awareness derive from the live stdlib. - Public stdlib functions need explicit return types: named closed records,
Result<T, E>, or typed maps rather thananyor opendict.
Adding a model or provider to the catalog
- Model rows are
crates/harn-vm/src/llm/catalog_sources/; capability rules arecrates/harn-vm/src/llm/capability_sources/. Both aggregate intocrates/harn-vm/src/llm/providers.tomlandcrates/harn-vm/src/llm/capabilities.toml, which are generated along with everything underspec/provider-catalog/and the provider docs. Edit the source fragments only. - Regenerate with
make gen-provider-catalog,make gen-provider-matrix, andmake gen-provider-support, then verify with the matchingcheck-targets. All three generators read the current source fragments from the repository root. Catalog-only edits don't require rebuilding the CLI. Matrix and support use embedded defaults when source directories are absent;generaterequires sources. Invalid fragments fail explicitly. - Ship every route the model is served on. A model reachable both directly and through an aggregator needs a catalog row and a capability rule on each route. Adding only the direct route leaves the aggregated route falling through to a default rule, which usually downgrades it to text tools. Read how the neighbouring generation is carried before concluding one route is enough, and record the reason in a comment when it genuinely is.
- Give every catalog test a negative control. Provider inference routes any plausibly shaped model string, so "the id resolves" is also true of a model that does not exist. Assert that a neighbouring unserved id has no row, and pin an adjacent generation's contrasting capability value so a new rule that silently inherits the old one fails instead of passing.
- Back a capability claim with a live probe rather than a model card, and record what the probe returned beside the row. An endpoint that tolerates a field its own guidance says to strip has not honored it, so tolerance is not support.
Verification
Verify publishable cratesruns on every pull request and merge group that touches the package surface, butCI statusdoes not wait for it, so a PR can merge before it finishes. Its result lands on the merged commit: a red one onmainblocks the next release until it is fixed, so fix it forward at once. When you change a crate'sCargo.toml,includelist, build script, or dependency bounds, wait for that check on the PR before enqueueing.- Start with the narrowest check through the owning interface.
- Run one exact Rust test without unrelated nextest discovery with
HARN_TEST_ONE_NAME='module::tests::case' make test-one. SetHARN_TEST_ONE_PACKAGEonly when the test is outsideharn-cli, andHARN_TEST_ONE_BINARYwhen the test lives in the package'stests/directory rather than itssrc/. A name the requested target does not define is refused before the run; zero matches from one that does fail loudly. - Workspace tests:
make test(requires cargo-nextest;make setupinstalls it). - Full gate:
make all. - Before declaring a change clean, run
make check-driftand inspectgit status. After Rust registry or executable-semantics changes, rebuild and runmake check-drift-binary. - Syntax/parser/keyword changes need conformance coverage,
make conformance,make lint-harn,make fmt-harn, and tree-sitter tests. - Docs code blocks need
make check-docs-snippets. - Portal changes need
npm run portal:lint,npm run portal:test, andnpm run portal:build. - VS Code changes need
(cd editors/vscode && npm run compile). - Tree-sitter changes need
(cd tree-sitter-harn && npm test). - Read one head's CI check state with
./scripts/gh_check_state.sh --repo OWNER/NAME --sha <40-hex>: it reports the latest verdict per check name, counts queued work as pending, and exits 3 when an expected check never reported. Do not hand-roll agh pr checksparse. - Do not add real-time sleeps, wall-clock polling,
SystemTime::now(), or shortrecv_timeoutcalls to tests. Use paused Tokio time,EventLog::subscribe(), orOrchestratorHarness; seedocs/src/dev/testing.md.
Cross-surface changes
- Syntax changes usually touch lexer, parser, spec, tree-sitter, and conformance.
- Runtime/builtin changes usually touch VM, CLI, docs, README, changelog, and conformance.
- Public CLI, builtin, or host-capability changes require user-facing docs and help.
- Prompt syntax changes require template conformance fixtures, changelog, vocabulary regeneration, and VS Code grammar verification.
- For autonomous/background edits, prefer worktree-backed execution over ambient working-directory state.
Editing and changelog
- Use the simplest safe edit. Prefer
std/editwhen structural addressing, cross-file rename semantics, or staged hash-guarded preview reduces risk; normal patch tools are appropriate for ordinary maintenance. - Non-trivial PRs add one
changelog.d/<id>.<category>.mdfragment. Categories arebreaking,added,changed,deprecated,removed,fixed, andsecurity. Seechangelog.d/README.md. - Use
no-changelog-neededonly after the soft gate fires on a change with no user-visible impact.
Pull requests
- Title pull requests
[Area] Sentence case description, using one tag from the table in CONTRIBUTING.md. Release pull requests stay exactlyRelease vX.Y.Z;publish-release.ymlmatches that subject. Bot titles are left alone. - Keep the description to roughly five sentences: what changed in behavior terms, why, the one risk or blind spot, and how you verified the claim at the level of the claim. Do not restate the Files or Checks tabs.
- Use a closing keyword only when the pull request resolves the whole issue:
Closes #N items: 1, 2, ...for every enumerated sub-ask, orSingle-ask: #Nwhen the issue is not enumerated. For partial work, usePartial: #N items: 1, 3orRefs #N; never use a closing keyword. GitHub builds the closing link from the keyword and ignores the text after it, so a keyword on partial work closes the whole issue on merge however the line is worded. Verify withgh pr view <N> --json closingIssuesReferences, which is the check; the body text is not.
Release
- Read
harn skill get release-harn --full, or its version-matched source atcrates/harn-skills/src/corpus/release-harn/SKILL.md. Follow the linked maintainer release procedure for commands, admission, frozen candidates, recovery, and terminal publication evidence. - Keep release publication and downstream consumer convergence as separate proofs. The owning workflows control both; don't add another controller.
Merge overrides
- Rare founder overrides for CI incidents, merge-queue cost spikes, or
fix-forward lands use the org-admin labels
bypass-ci,bypass-merge-queue, andforce-merge. - Labels are a trigger only. The workflow re-checks organization or repository
admin permission and refuses fork PRs. See
Merge overrides and the
burin-labs/.githubREADME. - Land with
gh pr merge --squash --auto, which enqueues. Never usegh pr merge --admin. The labels are the only supported way to skip the queue, and themerge queueruleset allows no admin bypass. GitHub ignores-mergein.gitattributes, so the queue's generated-file check on the combined tree is the only guard against two regenerations merging into a stale file (#8817).
Ecosystem working agreement
- Build ambitious outcomes behind small typed interfaces; give behavior one owner and generate or parity-test projections instead of duplicating policy.
- Work autonomously within approved scope. Pause for destructive or production effects, exceptional spend, material ambiguity, or new authority.
- Treat stop, wait, stand down, pivot, and steer as control events.
- Use the smallest owning product-path check. Add a falsifier for contested, load-bearing, or potentially vacuous claims; record controls, recovery, and blind spots.
- Evidence follows source/artifact identity. Reuse proof when relevant code, build inputs, and dependencies are unchanged. Repeat affected checks for relevant changes, failures, deployment, or packaging differences. Do not rebuild or recapture solely for main.
- Ship means owning-main integration with terminal merge and applicable release/deploy checks. Confirm landed content and result; an open PR is incomplete.
- Use
shipwith a deployed Smart Ship caller; otherwise usegh pr merge --squash --auto. Never use--admin; incidents usebypass-ci,bypass-merge-queue, orforce-merge.
