AGENTS.md · git:20261009.a0bddb3 · 2026-10-09 · sha256 1a99ef013d13dc34
AGENTS.md git:20261009.a0bddb3A
Immutable. This exact content never changes and is served at /api/v1/blob/1a99ef013d13dc34.
# AGENTS.md — working in the expectation-fit repo
Guidance for any AI agent (or human) contributing to this repository. Read this
before editing. `CLAUDE.md` points here; this is the single source of truth for how
the repo is laid out and the rules that keep the plugin installable and internally
consistent.
> This repo *develops* the **expectation-fit** Claude Code plugin. The plugin's
> own end-user docs live in [`plugins/expectation-fit/README.md`](plugins/expectation-fit/README.md);
> the design lives in `PLAN.md`, current state in `STATUS.md`, and change history in `CHANGELOG.md`.
---
## What this repo is
A Claude Code **plugin** that enforces *expectation fit*: software that behaves the
way a reasonable developer or user already expects. It applies established design
principles as review **lenses** (parallel sub-agents) across four contexts — planning,
plan validation, code review, codebase audit — producing scored, deduplicated,
confidence-gated findings with concrete fixes.
The plugin is **data + prompts, not executable code.** Skills and agents are markdown
that the model follows; the knowledge base and contract files are markdown/JSON/YAML.
There is no build step and no runtime. "Correctness" here means the prose contracts,
schemas, and cross-references stay mutually consistent.
---
## Load-bearing rules (break these and the plugin breaks)
1. **`agents/` gitignore trap.** Some environments globally ignore `agents/` *everywhere*
(several AI coding tools add it to a global gitignore). The repo `.gitignore` MUST
keep the `!plugins/expectation-fit/agents/` + `!plugins/expectation-fit/agents/**`
negation, or on such a machine all five lens agents silently leave git and a cloned
plugin has **zero agents**. It's defensive — keep it so the plugin is portable
regardless of a contributor's global config. After touching anything under `agents/`,
verify with `git ls-files plugins/expectation-fit/agents/` (expect 5 files). The
same lesson is why `.idea/` is listed in the repo `.gitignore` too.
2. **Self-contained plugin.** Everything the installed plugin needs lives **under**
`plugins/expectation-fit/` — `agents/`, `skills/`, `references/`, `config/`,
`resources/`. A Claude Code plugin only ships what's inside its plugin dir. Never
move `resources/` or `references/` to the repo root. Edit them in place; they are the
single source of truth. Optional Grok runtimes live in repo-root `.grok/workflows/`
and must not be moved into the plugin dir. Shipped files must not cite repo-only
paths, such as `docs/`, `wip/`, `scripts/`, `.grok/`, `PLAN.md` or `STATUS.md`. An
installed plugin does not have them. Skills and references may still name
`docs/expectation-fit/` as the report folder in the user's repo.
3. **Runtime paths use `${CLAUDE_PLUGIN_ROOT}`.** Every cross-file reference inside a
skill or agent must address shipped files as `${CLAUDE_PLUGIN_ROOT}/<dir>/<file>` —
never a bare filename or repo-relative path. A lens runs in isolation and can only
resolve `${CLAUDE_PLUGIN_ROOT}` paths.
4. **Read-only by default; never push.** Lenses are read-only except for the Layer A
run-scratch JSON write. Orchestrators write the report under the resolved
`artifacts.report_dir` (and clean up run scratch). In `mode:agent` they write no
report file unless the caller passes `out:`; the JSON reply is the deliverable. Only `/fit-review` (interactive)
mutates product code — applies safe fixes, commits on a clean tree — and `/fit-setup`
writes under `.expectation-fit/` (optional `.gitignore` append for runs). Nothing ever pushes,
opens PRs, or files tickets.
5. **Two-layer artifacts, out of git by default.** Run scratch →
`.expectation-fit/runs/<run-id>/` (lens JSON; deleted after the report step when
`cleanup_runs: true`). Report → `.expectation-fit/reports/<stamp>-<skill>[-scope].md`.
Both default folders ignore themselves. Analysis and run files never go to a tracked
path by default; a project opts in with `artifacts.report_dir`. Configure via
`artifacts.*` in `ways-of-working.yaml`. Do not reintroduce plugin defaults under
`wip/`, `.wip/` or `docs/`.
6. **`ie-` prefix** for every skill and agent. Project config dir is **`.expectation-fit/`**.
---
## Repo map
```
expectation-fit/ dev repo + marketplace
AGENTS.md CLAUDE.md README.md LICENSE
PLAN.md design + phase detail
STATUS.md current-state snapshot
CHANGELOG.md dated change history + decisions
.claude-plugin/marketplace.json marketplace entry (install from repo root)
scripts/check-contracts.rb contract-integrity check (the one automated check)
docs/intent-engineering/ this repo's committed dogfood reports (runs pass out:)
docs/index.html GitHub Pages site
.expectation-fit/runs/ + reports/ self-ignoring run scratch and local reports
.grok/workflows/ optional Grok runtime (not shipped in the plugin)
plugins/expectation-fit/ THE INSTALLABLE PLUGIN (self-contained)
.claude-plugin/plugin.json name, version, keywords, license
README.md end-user usage + lens details
agents/ ie-{predictability,convention,simplicity,experience,architecture}-reviewer.md
skills/ ie-{init,plan-assist,validate-plan,review,audit,from-pr-learnings}/SKILL.md
references/ findings-schema.json, subagent-template.md, lens-catalog.md,
stack-catalog.md, report-template.md, scoring-rubric.md,
principle-index.md, config-resolution.md (the shared contract layer)
config/defaults/ ways-of-working.yaml, patterns.yaml, thresholds.yaml
resources/ principles/ frameworks/ agnostic/ patterns/ (knowledge base)
```
---
## How a run works (skills → lenses → report)
`fit-validate-plan`, `fit-review`, and `fit-audit` are orchestrators with the same shape:
1. **Load resolved config** (`references/config-resolution.md`): discover nearest
`.expectation-fit/` via walk-up (or `config:` / `EXPECTATION_FIT_CONFIG_DIR`), then deep-merge over
`${CLAUDE_PLUGIN_ROOT}/config/defaults/`. Always before lens selection; always record
the Config source in Coverage.
2. **Select lenses** (`references/lens-catalog.md`): predictability + simplicity are
always-on; convention is on for essentially all code; experience only on user-facing
surfaces; architecture only when `stack-catalog.md` has Arch pack ✅ for a detected
stack, code/audit only.
Resolved `lenses:` toggles (`on`/`off`/`auto`) override the defaults.
3. **Dispatch** each lens in parallel using `references/subagent-template.md`, binding
`run_artifact_dir = $RUN` (Layer A). Each lens reads its `resources/` heuristic docs,
returns compact JSON per the schema, and writes full detail to `$RUN/{lens}.json`.
4. **Merge / dedup / gate** (`references/report-template.md` "Merge and gate"): repair
off-schema findings, merge findings on one defect within 3 lines (titles may differ),
note agreeing lenses (agreement does not raise confidence), apply `severity_align`
then explicit severity overrides and pattern policy (incl. `preferred`), then suppress
below the confidence gate (default anchor 75; P0 survives 50+). Every dropped finding
is listed under Rejected with its reason.
5. **Report** to `$REPORT_PATH` under the resolved `artifacts.report_dir` (or a JSON
reply in `mode:agent`, written to a file only with `out:`), then delete `$RUN` when
`cleanup_runs` is true.
`fit-plan-assist` is the lightweight exception: inline advisory checklist, no sub-agents,
no artifacts, prose (not findings JSON). `fit-setup` is the setup/upgrade wizard for
`.expectation-fit/` (fresh, upgrade, calibrate; multi-repo placement) and may offer a
`.gitignore` line for `.expectation-fit/runs/`.
**Grok optional runtime.** `.grok/workflows/fit-review.rhai` is a Grok script, not a
Claude skill. It is a subset: agent-resolved config, selected lenses, skeptics,
script-owned verdict, report in run scratch. It must not move into
`plugins/expectation-fit/`. See `.grok/workflows/README.md`.
**Shared tokens** (review/audit/validate-plan): `mode:agent` (JSON, and for review skips
the apply stage), `out:<path>` (override the report path; pass a folder),
`lenses:<list>` (run only the listed lenses; overrides auto-selection and config
toggles; Coverage marks the others `not_selected`), and `prior:<report-path>` (an
earlier report of the same target; its `fixed` and `declined` rows reach every lens
through the `<prior>` slot in `references/subagent-template.md`, and it never changes
the diff range). Path resolution:
`references/config-resolution.md` → Artifact paths. Defaults: run
`.expectation-fit/runs/<run-id>/`, report `.expectation-fit/reports/<stamp>-<skill>[-scope].md`.
Run-id format is identical across the three: `$(date +%Y%m%d-%H%M%S)-<4-byte hex>` —
keep them in sync if you change one.
---
## The five lenses (`agents/`)
| Agent | Principles | Model | When it runs |
|-------|-----------|-------|--------------|
| `fit-predictability-reviewer` | least-astonishment, DWIM, WYSIWYG | inherit | always-on |
| `fit-simplicity-reviewer` | Occam, KISS, YAGNI | inherit | always-on |
| `fit-convention-reviewer` | convention-over-config, framework idiom | sonnet | code (almost always) |
| `fit-experience-reviewer` | HIG, look-and-feel, UX | sonnet | user-facing surfaces |
| `fit-architecture-reviewer` | structural quality, design patterns | sonnet | supported framework, code/audit only |
**Agent contract (every lens):**
- Frontmatter: `name` (= filename stem **and** the `lens` enum in `findings-schema.json`
**and** the `lens-catalog.md` row), `description`, `model`, `tools`, `color`.
- `tools` is uniformly `Read, Grep, Glob, Bash, Write`. `Write` exists **only** to emit
`{run_artifact_dir}/{lens}.json`. `Bash` is for measurement (architecture metrics;
optional read-only `reek`/`flog` probes). Adding `Edit`/`MultiEdit` breaks
the read-only contract.
- `model: inherit` for the two always-on lenses (session model for high-stakes
reasoning); `model: sonnet` for the three conditional lenses. Don't "standardize" all
five to one model without updating `subagent-template.md` — it changes cost/quality for
the always-on lenses.
- Findings conform to `references/findings-schema.json`. `principle` is one of the schema
enum (17 values incl. `architecture`); `severity` ∈ P0–P3; `confidence` ∈ {0,25,50,75,100}.
- **Confidence anchors are fixed and shared**: 100 = provable from code/doc alone;
75 = traceable end-to-end and a normal user/dev hits it; 50 = depends on unseen context
(advisory); ≤25 = speculative, suppress. Reuse this exact language.
- Every finding names the **broken expectation** — a surprise you can't tie to a specific
expectation is not a finding. When two principles conflict, or a principle conflicts
with a settled decision (name it, for example plan KTD-6), set the `tension` field and
present the trade-off; don't dogmatize. A tension finding is never auto-applied.
- `smell` and `pattern` fields are **architecture-lens-only**. `scores` are returned only
in audit/plan contexts, keyed by the canonical snake_case ids in `scoring-rubric.md`.
- Local conventions win: every lens reads repo `CLAUDE.md`/`AGENTS.md` paths and the
resolved `conventions.notes` first. `conventions.sources` and `conventions.auto` go to the
convention lens only. Authority order is fixed in `config-resolution.md`:
`.expectation-fit/*.yaml` > repo `CLAUDE.md`/`AGENTS.md` > sibling code > plugin defaults/framework docs.
---
## The contract layer (`references/`)
Single source of truth, read at runtime by skills and lenses. Do not duplicate these
rules into skills/agents — reference them.
- `findings-schema.json` — the machine-checkable finding contract. The `lens` enum must
equal exactly the five `agents/fit-*-reviewer.md` basenames.
- `subagent-template.md` — the dispatch prompt skeleton + the shared confidence rubric.
- `lens-catalog.md` — the five lenses, their resource docs, and selection rules.
- `stack-catalog.md` — the **stack registry**: every known stack, its detection signals,
the packs it loads (convention doc, architecture doc, pattern catalog, threshold
namespace), and whether the architecture lens supports it. Skills + the architecture lens
+ `fit-setup` read this instead of hardcoding detection, so adding a stack is data + a
catalog row, not skill edits.
- `scoring-rubric.md` — audit/plan posture dimensions per lens (canonical snake_case keys).
- `report-template.md` — synthesized output shape (markdown tables + `mode:agent` JSON).
- `principle-index.md` — maps each principle/topic to its resource doc and owning lens.
- `config-resolution.md` — how `.expectation-fit/` config merges over defaults; authority order.
Note: findings cite **principles** (the schema enum) while posture rates **dimensions**
(the scoring-rubric keys). These are two intentionally distinct taxonomies — don't assume
one should mirror the other.
---
## Config system (`config/defaults/` + `.expectation-fit/`)
The plugin works out of the box with defaults; a repo tunes it via committable
`.expectation-fit/*.yaml` at its root.
- `ways-of-working.yaml` — lens toggles (turn an agent `off`), external-tool preference
(`tools.architecture`: `enrich`/`prefer`/`report`/`off`), severity overrides,
**`severity_align`**, local conventions (`notes`, `sources`, **`auto`** discovery),
confidence gate, and **`artifacts.*`** (`run_dir`, `report_dir`, `cleanup_runs`;
see rule 5 and `references/config-resolution.md`).
- `patterns.yaml` — design-pattern policy: `preferred` (A instead of B) / `allowed` /
`blocked` (no new use) / `approved` (grandfathered paths). Keys reference
**snake_case pattern ids** from `resources/patterns/<stack>.yaml`.
- `thresholds.yaml` — architecture metric limits, namespaced `rails.<unit>.<metric>`.
**Merge rule:** project overrides global. Nested maps merge recursively at every depth. Only lists replace, unless the block sets `extends: true`.
An `extends: true` list appends to the global list. The `conventions` block carries an `extends` flag; the `patterns.*` lists are
**replace-only** (no `extends` knob today). A threshold is a *signal*, not a verdict — the
lens judges responsibilities, not just the number.
**Two id namespaces, deliberately distinct casing:** structural-smell ids are
**kebab-case** (`fat-model`, `god-object`, `callback-hell`, …); design-pattern ids are
**snake_case** (`interactor`, `service_object`, …). Pattern ids are an API — keep them
stable; anything in `.expectation-fit/patterns.yaml` must exist in the matching catalog.
---
## Knowledge base (`resources/`)
Researched, citation-backed docs the lenses read at runtime. Three families plus the
pattern catalog:
- `principles/` — one doc per design principle (definition, origin, core tenets,
**violation smells**, good/bad examples, how-to-apply, **Sources**).
- `frameworks/` — per-stack convention docs (rails, ruby, react, typescript, python,
laravel, express, phoenix, swift-ios) plus `rails-architecture.md`, `python-architecture.md`,
`laravel-architecture.md`, `express-architecture.md`, `phoenix-architecture.md`, and
`react-architecture.md` for the architecture lens. Variant headings: "Convention violation
smells", "Least-astonishment traps".
- `agnostic/` — cross-cutting topics (naming, error-handling, api-design,
defaults-and-configuration, accessibility, information-architecture). Heading:
"Detectable smells".
- `patterns/` — YAML design-pattern catalogs (`rails.yaml` 14, `python.yaml` 13,
`laravel.yaml` 15, `express.yaml` 12, `phoenix.yaml` 11, `react.yaml` 11 patterns) + format
README.
**The "Violation smells" / "Detectable smells" section is load-bearing** — it *is* the
lens's detection checklist. Every doc a lens reads must have one. Every doc ends with a
**Sources** section of real links — never ship a doc without citations.
A guard in a smell card may qualify a finding or cap its confidence. It must not
suppress a whole class of findings.
---
## How to extend (wiring requirements)
Adding anything means updating its references in lockstep, or it's orphaned:
- **New lens** → `agents/ie-<x>-reviewer.md` (with the full agent contract above) **and**
add it to the `findings-schema.json` `lens` enum, a `lens-catalog.md` row,
`scoring-rubric.md` dimensions, and `README.md`. Wire its selection into the skills.
- **New framework doc** → `resources/frameworks/<stack>.md` (with a smells section +
Sources) **and** wire it into `principle-index.md`, the `lens-catalog.md` resource
column, and a `stack-catalog.md` row (Arch pack ⬜). Seed a stack only when there is a
real consumer (dogfood targets first).
- **New architecture stack** → ships only when **all** of
`resources/frameworks/<stack>-architecture.md`, `resources/patterns/<stack>.yaml`, and a
`<stack>.*` namespace in `config/defaults/thresholds.yaml` exist; then flip the stack's
`stack-catalog.md` row to **Arch pack ✅** and add a `principle-index.md` row. The
architecture lens + `fit-setup` read the registry, so **no skill edits are needed** — the
registry is the only detection wiring. The contract check (section 10) enforces that a ✅
row, its files, and its threshold namespace all agree. Also add the stack to the lists in
both READMEs, STATUS.md, the marketplace.json description and docs/index.html.
- **New design pattern** → add to `resources/patterns/<stack>.yaml` with all required
fields (`id`, `name`, `intent`, `recognition`, `good_use`, `misuse`). Ids are snake_case,
stable, and may be referenced by `.expectation-fit/patterns.yaml`.
- **New principle doc** → write the doc (full structure + Sources) **and** wire it into
`principle-index.md` and the owning lens's "Read first" list. Cross-link related
principle docs with `[[wikilink]]` to the bare filename.
---
## Conventions & quality bar
- Plain-language **What / Why / How** opens every PR and the top of PLAN.md.
- **Least astonishment applies to this repo too** (it's the product). Names match
behavior; one concept = one meaning; no surprising side effects. The plugin is
dogfooded on itself — keep it self-consistent.
- **Validate before committing: `ruby scripts/check-contracts.rb`** — the contract-integrity
check. It asserts JSON/YAML parse, lens-identity 4-way agreement (schema enum == agents ==
lens-catalog == scoring-rubric), agent frontmatter (name == filename, in the enum, tools +
model present), that every `${CLAUDE_PLUGIN_ROOT}/...` path resolves, the pattern-catalog
schema, that emitted `principle:` ids are in the enum, cross-references (per stack with a
`<stack>.*` threshold namespace, its `<stack>-architecture.md` exists and every metric it
cites is defined in `thresholds.yaml`; every pattern id in policy/README exists in some
catalog; unreferenced metrics warned), resource-doc
structure (each principle/framework/agnostic doc has a detection "smells" section + a
Sources section with ≥2 links; no orphan docs missing from `principle-index.md`/
`lens-catalog.md`), that the five agents are git-tracked (the gitignore trap), and
**stack-registry consistency** (every `stack-catalog.md` Arch-pack-✅ row has its
architecture doc, pattern catalog, and threshold namespace; no threshold namespace or
pattern catalog exists without a registered ✅ row). Exits non-zero on any breakage. Add new invariants there as the plugin grows — it is the
plugin's one automated check. It also runs in **CI on every PR**
(`.github/workflows/contracts.yml`), so a contract break fails before merge.
- **PR review feedback is a learning loop.** On every open PR with review comments
(cubic, Devin, Qodo, human, or other bots): address real issues with code; for every
thread, post a short reply stating what changed (with commit) **or** why no change
(false positive / intentional / already fixed), then resolve the thread. Never resolve
silently — reviewers (and their self-learning) need the reply. Prefer a PR + required
checks over direct pushes to `main` when branch protection expects CI.
- **Dogfood as you go.** Run the plugin from this checkout against your change with the
dev loop below, fix surfaced P1/P2, then commit. The audit→fix→re-audit loop is expected.
Contributor step: file each confirmed plugin gap as an issue in this repo before the
session ends. This repo is public, so remove private repo names, paths and findings
first.
- Clean git history is preferred; the owner may ask to squash to a single commit.
## Dev loop (run this checkout)
```
cd <fixture or target repo>
claude -p "/fit-review mode:agent <target>" \
--plugin-dir <absolute path to this checkout>/plugins/expectation-fit \
--allowedTools <the Bash and Write grants that the run needs>
```
This copy replaces the installed plugin of the same name, for this session only. The
run returns the `/fit-review mode:agent` JSON. Keep the tool grants: a `-p` run cannot
answer permission prompts. Use the same command when the owner asks for the latest
plugin. The other skills (`/fit-setup`, `/fit-audit`, `/fit-validate-plan`,
`/fit-plan-assist`, `/fit-from-pr-learnings`) run the same way.