# AGENTS.md

Guidance for work in this repository.

## Start here

- [Current architecture](docs/architecture.md): implemented behavior and source map.
- [V1 contract](specs/2026-09-05-ironlint-v1-design.md): release requirements.
- [Active plan](plans/2026-09-05-ironlint-v1-implementation.md): resume block,
  current packet, dependencies, and evidence. Read only the relevant packet and
  contract sections before tracing code; do not load the whole documentation tree.

Keep these documents current when behavior or scope changes. There is one active
v1 plan and no historical planning archive. Do not revive removed roadmap items.

## Current implementation and target

IronLint is a Rust command-check evaluator for AI coding workflows. `version: 1`
policies use `run`, optional `files`, and `on: [accept]` or `[change, accept]`.
The versioned core/CLI, schema-7 output, bounded execution, local consent, and
read-only inspection are implemented. Unversioned configuration and the former
execution paths have been removed. `ironlint schema` and `init` both expose the
v1 format. Core release verification uses local Docker feature E2E tests.
Harness compatibility and external enforcement qualification belong to
adapter/integration owners, not the core release gate.

Pi is the only adapter available for new installation. Claude Code, Codex, and
OpenCode registrations remain recognizable only so `init --uninstall` can
safely remove owned artifacts while preserving user edits. The optional Git
pre-commit hook is installed only with `--git-hook` and runs complete v1
acceptance.

## V1 contracts

- `accept` runs every check once in check-ID order. `change` filters opted-in
  checks by known paths; unknown paths run all change checks. Bare globs such as
  `*.rs` match at any depth. Every check must include acceptance.
- Commands run via `sh -c` in the supplied root, with stdin closed. Only
  `IRONLINT_ROOT`, `IRONLINT_EVENT`, and `IRONLINT_BIN` are supplied as reserved
  variables. Retained environment, output caps, and deadlines are specified in
  the v1 contract. Paths are env values, never spliced into `run`.
- CLI exits: 0 successful evaluation (including empty change selection), 1
  config/input failure, 2 violation, 3 execution/incomplete error, 4 untrusted
  policy. Schema 7 distinguishes `pass`, `violation`, `error`, and `not_run`.
  An enforcing consumer requires a complete acceptance pass, not exit 0 alone.
- Trust is CLI execution consent, keyed by canonical config path and policy/script
  hash. Keep the core evaluator independent of consent. Read-only inspection never
  requires trust. Consent is not isolation or authority over repository acceptance.
- An integration claiming enforced acceptance must bind approved policy/evaluator and a complete
  result to the exact candidate, with publication authority inaccessible to checks.
- Binary name: `ironlint`. Commit `Cargo.lock`; build and test with `--locked`.

## Validation and working rules

- Bug fixes start with a failing regression, then the smallest shared root-cause
  fix and focused test. Trace direct callers before editing or deleting code.
- Request a separate agent review after an integrated implementation batch.
  Review concrete regressions; do not repeat broad reviews without new evidence.
- Rust source must meet at least 80% region coverage per file and cognitive
  complexity at most 15. Refactor rather than suppress the lints.
- Run one Cargo build/test/coverage operation per target directory at a time.
- Installation/adapter tests use a temporary home/config directory. Never exercise
  them against the developer's live trust store or hook installation.
- Remove transient build/scratch artifacts produced by the task; keep the normal
  iterative `target/`. Mutation testing is optional investigation, not a CI gate.
- Keep provenance-stamped Pi adapter fixtures intact while its suite is active.
  Run `adapter-drift-audit` when the Pi harness contract changes. Live harness
  verification is adapter-owned; the fixture suite tests only the stable CLI
  contract.

```sh
cargo test --locked
cargo clippy --locked --all-targets -- -D warnings
cargo fmt --all --check
bash scripts/ci-coverage.sh
bash scripts/ci-adapters.sh
bash tests/e2e/features/run.sh
```

The adapter script needs isolated `XDG_CONFIG_HOME` when run locally. Core release
validation requires the local feature suite, not a live harness or hosted repository.
Fixture tests cannot prove repository enforcement. Publishing requires task authorization.

<!-- graft:start -->
## Graft — repo context graph

This repo is indexed in `graft/`: small linked markdown nodes that explain each
system and carry exact file:line spans, kept in sync with the code through git.

For ANY task here — understanding how something works, finding where code lives,
or scoping a change — get context from the graph before grepping or opening
source files. Re-ask freely (it's cheap) and reuse literal identifiers you
already have (symbol, error string, file name) as the query. New to this repo?
Run `graft map` first — a token-budgeted orientation (dir clusters, hubs,
hotspots), no LLM, no key.

- Run `graft ask "<your question>" --source` → ranked nodes with the relevant
  code spans inlined (each hit's ≤8-line crux by default; `--full` for whole
  definitions when the crux isn't enough). Match the tool to the task shape:
  for understanding or editing, the top node IS the answer — cite its
  `covers:` file:line spans and edit straight from `--source`. For
  exhaustive tasks ("every occurrence / every caller of this pattern"), ranked
  results are top-N, not complete — run `graft grep "<literal>"` instead
  (exhaustive over indexed files, grouped by enclosing symbol), falling back
  to raw `grep -rn` only for unindexed files.
- `graft skeleton <file>` → every definition's signature + span, ~10× cheaper
  than reading the file; use it to skim an API surface.
- `graft callers <symbol>` gives precomputed, exact edges — who calls this.
  Add `--direction out` for what it calls, or `--depth N` to walk
  transitively for the full blast radius. For structural questions, skip
  ranking and use this directly.
- Or browse: `graft/INDEX.md` lists every node; follow the links.
- Monorepos and folders of multiple repos rank fairly across sub-projects —
  hits carry `[scope/]` labels naming which one they're from. Narrow with
  `graft ask "<task>" --in <scope>/` once you know where you're working.

If a returned span is truncated ("+N more lines"), open the file at that exact
range before finalizing. Only open source files when a node genuinely lacks a
needed detail, and then at the exact file:line the node points to — never
re-read whole files.

After big code changes, refresh the graph with `graft build` (deterministic,
no API key, $0).
<!-- graft:end -->
