AGENTS.md · git:20260914.fa13cc9 · 2026-09-14 · sha256 0e084690c73b822b

AGENTS.md git:20260914.fa13cc9A

Immutable. This exact content never changes and is served at /api/v1/blob/0e084690c73b822b.

# Repository Guidelines

Canonical instructions for every coding-agent harness: Claude Code imports
this file through `CLAUDE.md`, Codex reads it directly. Edit this file, not
`CLAUDE.md`. This is the **public** Rust scanner for Carrick; the private
companion `carrick-cloud` holds Lambdas, Terraform and the dashboard.

## Carrick

**If the answer is defined by another service — its endpoints, its real
request/response types, who consumes it, or any local type, constant, or
SDK helper that mirrors it — ask Carrick before you rely on anything
local.** Local copies of another service's contract drift and lie; the
producing service defines the contract, and if two definitions disagree,
the producer's definition wins. Load the Carrick tools now so they are ready at that
moment.

**Read the file you are editing. For anything that lives elsewhere in the
workspace (a helper, an endpoint, a consumer, a type), ask Carrick first,
then grep to confirm a location.** Run `search_by_intent` before writing
any helper, parser, validator, or domain function, even when you are sure
it is new. Grep can prove a name is absent; it can never prove the
behaviour is.

Then, before you write new code, state one line naming what you asked and
what came back:

`Carrick checked: <query> -> <result count>`

The loop for cross-service work: `get_service_graph` (who calls whom) →
`search_by_intent` (prior art) → `get_endpoint_types` (real contracts) →
build → `check_compatibility` (each consumer, before changing a response
shape, removing an endpoint, or renaming a path). Before adding or bumping
an npm package, `get_service_dependencies` surfaces cross-service version
conflicts at design time. For audit-shaped work, such as inventorying what
a service calls outside itself, `list_external_calls` enumerates the
recorded outbound-call candidates (SDK calls, external domains, env-var URL
bases) with their file and line.

Carrick indexes every service in the project **scanner-evals**
(workspace **daveymoores**): every function with an intent
description, its dependencies, and API endpoints with their real
request/response types, on each repo's main branch, exported or not. Pass
`project: "scanner-evals"` (or `repo: "<owner/repo>"` from the git
remote) on every call. Carrick is read-only; data reflects the most recent
scan of each repo. The index at main is what production integrates
against, so build against a sibling's contract from the index and merge —
your unmerged local state does not need to be visible to Carrick.

The index cannot see the Rust scanner as a service (no HTTP surface), so a
nil result for scanner-internal behaviour is expected; say so rather than
skipping the call.

Write correct, idiomatic, explicitly typed code; never contort code so the
scanner can read it. If Carrick fails to extract something written
normally, that is a Carrick bug to report, not a constraint to code
around.

### Connect the agent

```
claude mcp add --scope user --transport http carrick https://api.carrick.tools/mcp
codex mcp add carrick --url https://api.carrick.tools/mcp && codex mcp login carrick
```

One install per harness serves every project in the workspace.


## Core Goals (Read First)
- Build a live, type-aware, intent-aware index of TypeScript services across a GitHub org. The index is the product surface; AI coding agents query it over MCP.
- The Rust scanner in this repo is the index-population component. Per scanned function it extracts structural facts, request/response types, and intent.
- The GitHub Action wraps the scanner and runs per repo. PR comments showing cross-repo drift are the demoable proof point of the type analysis, not the headline product.
- Build framework- and library-agnostic REST extraction for TypeScript services. The structural layer of the index depends on this; correctness in cross-repo matching takes priority over framework-specific heuristics.
- Remove legacy code paths; backwards compatibility is not required and dead code should not be kept.

## Project Structure & Module Organization
- `src/` is the main Rust crate (engine, parser, analysis, cloud storage).
- `crates/carrick-match/` is the path-matching crate (`paths_match`, `match_agreement`, `path_literal_specificity`, `is_catch_all_path`, `is_unknown_call_path`, `is_param_segment`, `match_verdict`, `reportable_agreement`) — the single source of matching semantics for both sides of Carrick; never fork matching logic elsewhere. A surface deciding whether to REPORT a pair as a match calls `reportable_agreement` (or `match_verdict` for the reason); `paths_match` answers routing truth only and `match_agreement` returns `Some(0)` for pairs that route without corroborating anything. The scanner depends on it natively. Every release also compiles it to wasm (feature `wasm`) and attaches `carrick_match.js` / `carrick_match.d.ts` / `carrick_match_bg.wasm` / `carrick_match.sha256` as release assets via `.github/workflows/wasm-artifact.yml` (also runnable by `workflow_dispatch` to backfill a tag). The companion cloud repo pins those assets by hash and runs the same matcher at query time, so semantics changes here propagate on the next release. The crate's `wasm-bindgen` dependency is pinned exactly; the same version is pinned as the `wasm-bindgen-cli` install in BOTH `.github/workflows/ci.yml` and `.github/workflows/wasm-artifact.yml` — bump all three together.
- `tests/` holds Rust integration/unit tests; fixtures live in `tests/fixtures/`.
- `src/sidecar/` is the TypeScript type-extraction sidecar with tests in `src/sidecar/test/`.
- Cross-repo type compatibility runs through the sidecar's capture_v2/check_v2 actions ("tsc as serializer/judge"): each service's scan emits a compiler-emitted declaration stub, and cross-repo analysis typechecks generated probes in a synthetic workspace (`src/engine/type_compat_v2.rs` drives it). The legacy `ts_check/` comparison layer was deleted in the v2 flip; `docs/reference/type-checking-flow.md` describes it for history only.
- `scripts/` contains developer tooling (pre-commit hook installer).
- `docs/` — consult `docs/README.md` (the map) before reading or placing any doc; `docs/evals.md` covers running every eval.
- `action.yml` defines the GitHub Action entrypoint.
- `examples/` contains reference Express services used as e2e fixtures by CI and as user-facing demos.

Lambdas, MCP server, AWS infrastructure, and the web dashboard live in the companion `carrick-cloud` repo, not here.

## Build, Test, and Development Commands
```bash
# Build and run
cargo build
cargo build --release
cargo run -- /path/to/repo
CARRICK_MOCK_ALL=1 cargo run -- examples/express-single

# Tests and checks
cargo test
cargo test --test integration_test
cargo fmt
cargo clippy

# Evals (Tier-A, cross-repo, OSS runs): see docs/evals.md

# Type sidecar (Node)
cd src/sidecar
npm install
npm run build
npm test
```
Install hooks once per clone: `./scripts/install-hooks.sh`.

## Coding Style & Naming Conventions
- Rust is formatted with `rustfmt`; keep code `cargo fmt` clean and `clippy`-warning free.
- Use `snake_case` for modules/functions/files, `CamelCase` for types, and `SCREAMING_SNAKE_CASE` for constants.
- TypeScript follows standard `camelCase`/`PascalCase` conventions; mirror nearby files.

## Testing Guidelines
- Rust tests live in `tests/*.rs` (files typically end in `_test.rs`); fixtures are under `tests/fixtures/`.
- Use `CARRICK_MOCK_ALL=1` for tests that should avoid real cloud storage.
- Sidecar tests are authored in `src/sidecar/test/` and run via `npm test` after `npm run build`.
- Follow TDD: add/adjust tests first, then implement changes.
- Tests, formatting, and build steps must pass after each phase of work.

## Commit & Pull Request Guidelines
- Follow Conventional Commits with optional scopes: `feat(sidecar): ...`, `fix: ...`, `docs: ...`, `refactor(phase4.1): ...`, `chore: ...`, `test: ...`.
- PRs should include a short summary, rationale, test commands run, and links to relevant issues. Include sample Action output when it changes.

## Ownership Boundary (who does what)
Agents own the delivery loop end to end: branch, PR, review of the full diff, reading the real CI check conclusions, squash-merge, and cutting the release by merging the release-please PR. The release PR opens as a draft (`draft-pull-request` in `release-please-config.json`), so cutting a release is `gh pr ready <n>` and then the merge — the draft is the pause in which the deploy-order check happens, since a blob shape the deployed reader drops must not ship before the cloud side of it is live (carrick#840). Nothing in that loop waits on the owner. Exactly two actions are the owner's and must be requested, never performed by an agent:
- **Cloud deploys**: `terraform apply` and the lambda/app zips it ships, in `carrick-cloud`. Merged is not deployed; say so explicitly when a change needs a deploy before it can be measured.
- **Paid eval spend**: any run that makes real LLM calls (Tier-A, cross-repo, OSS evals, corpus sweeps, fleet re-scans). State the estimated cost and get an explicit yes on that number before launching.

## Estimation Conventions
- Effort estimates in issues, plans, and design docs use S/M/L defined in agent execution time. Claude agents execute this repo's work, so estimates are not in human developer time.
  - S: one agent, merged PR in under about an hour of wall clock. Implementation is minutes; CI (~5-10 min per push) dominates.
  - M: one agent working session, roughly 1-3 hours to a merged PR, usually one or two review/rebase cycles.
  - L: a sustained orchestrated session (parallel agents where the work decomposes), roughly a day.
- Converting older docs or human-calibrated estimates: a human-week of focused work is roughly one L session; a human-month is roughly 3-5 sustained sessions spread over about a week of elapsed time.
- Elapsed calendar time is set by the serialization points, not implementation speed: CI runs, review latency, dependency chains between work packages, same-release bundles, and the two owner-gated actions (cloud deploys via terraform apply, and paid eval spend including fleet re-scans; see Ownership Boundary). Merges and releases are not serialization points: agents perform them. Estimates should name their serialization points.
- Calibration (2026-07-18/19): nine scoped S/M tickets merged in one overnight session; a six-item tier including an L-sized prototype spike completed in about three hours of wall clock.

## Configuration & Infrastructure Notes
- `carrick.json` is resolved by `Config::load_services` into one `Config` per service. A flat config (or none) is a single service rooted at the repo root; a `services` array fans out per directory (`directory`, `include`, `tsconfig` + the call-classification fields). The engine runs the analysis pipeline, per-service type extraction, and upload once per service. Multi-service index upload is gated on `CloudStorage::supports_multi_service`, driven by the cloud's `multiService` capability flag. See the README "Monorepos" section for the user-facing shape.
- Runtime env vars: `ACTIONS_ID_TOKEN_REQUEST_URL` / `ACTIONS_ID_TOKEN_REQUEST_TOKEN` (auto-set by GitHub Actions when the job grants `id-token: write`; the scanner mints an OIDC token from these and sends it as the `X-Carrick-OIDC` header — the cloud derives repo identity from the signed claims, so no API key is needed), `CARRICK_MOCK_ALL` (test-only, returns canned responses without hitting the cloud), `CARRICK_API_ENDPOINT` (override the default `https://api.carrick.tools` endpoint at build time; optional).
- Terraform, Lambdas, and dashboard code live in `carrick-cloud`. No infrastructure or server-side code belongs in this repo.

## Hard rules

- **Never run `terraform` shell commands.** Terraform and the rest of the AWS infrastructure live in `carrick-cloud`, not here. If a task needs infra changes, switch to that repo.
- **No LLM system instructions in this repo.** Per the public/private split, system-prompt strings live in `carrick-cloud/lambdas/*/system_prompt.txt`. User-message templates that interpolate scan-time data may live in Rust because they need access to the data structures the scanner produces (e.g. `src/agents/file_analyzer_agent.rs`). CI workflow `prompt-leak-guard.yml` enforces this as a ratchet against `.github/prompt-leak-baseline.txt`: counts may shrink but never grow. It scans every `*.rs` file under `src/`, `build.rs`, and `tests/` (excluding `tests/fixtures/`) for the patterns `You are `, `You describe `, `You analyze `, `Extract ONLY`, `responseSchema`, `system_instruction`, `prompt:[[:space:]]*"`, `Identify all frameworks`, and `"frameworks":`.
- **No backwards compatibility / no users.** When refactoring, ship the new shape and delete the old shape in the same commit. No feature flags, no deprecation cycles, no parallel old/new code paths.

## Boundary

- Public (this repo): Rust scanner, AST/parser, agent orchestrators (thin), `src/sidecar/`, GitHub Action.
- Private (`carrick-cloud`): all Lambdas, MCP server + tools, Terraform, prompts, wrapper-rule generation, future web dashboard.

MCP is exposed exclusively as an HTTP endpoint at `https://api.carrick.tools/mcp`. Users add Carrick to their AI agent via `claude mcp add --scope user --transport http carrick https://api.carrick.tools/mcp`. There is no local-stdio install — the MCP tool implementations live in `carrick-cloud/lambdas/mcp-server/`.

If you need to touch a Lambda, Terraform, or a prompt, the change goes in `carrick-cloud`.

## Where things are

Reading or writing documentation, or running evals? Start at `docs/README.md` — the map. It says where everything lives and where new docs go; don't place a doc without it. Otherwise ignore `docs/`.

The Carrick → carrick-cloud split landed in 2026-05. Follow-up work (OAuth dashboard, ELv2 relicense + flip public, etc.) is tracked as GitHub issues.