Skip to content

AGENTS.md: the absolute rules; delete CLAUDE.md #4862

Closed

Description

@cliffhall

Part of the agentic software factory tracker #4858, Wave 2. Proposed as S1 in docs/agent-guidance-inception.md (PR #4861) §9, from #4859.

Depends on

None beyond #4859. Can run in parallel with the rest of Wave 2.

Scope

  • Write AGENTS.md at the repo root, starting from the Inspector's AGENTS.md on v2/main. §6 of the inception doc lists what to keep, rewrite, drop and add.
  • It holds only rules that are true on the day it merges. If a rule's machinery doesn't exist yet (validate, local:gate, per-file coverage), the sub-issue that builds that machinery adds the rule.
  • Map every CLAUDE.md section per §4 of the doc:
    • project overview
    • monorepo structure
    • build and test commands for each language
    • code style for TypeScript and Python
    • contributing guidelines
    • the CI/CD pipeline (dropped as derivable)
    • the MCP protocol reference
    • key patterns: registerTools/registerResources/registerPrompts, tool annotations, transports (SSE deprecated), the PR template checklist
  • Map every Adopt AGENTS.md (with CLAUDE.md pointer) modeled on the inspector repo #4473 decision marked S1 in §5. The board ID table goes to board-ops (Part 6), not here.
  • Settle src/everything/AGENTS.md: fold its generic rules into the root file, keep its server-specific extension guidance next to the server, and check whether a nested AGENTS.md is loaded at all.
  • Adopt the Inspector's purpose-header rule without a bulk migration. New files, and files a PR substantially rewrites, open with a comment stating their purpose and rationale. That's the first comment in the file, after any shebang: #!/usr/bin/env node must stay on line 1 of an executable entry point.
  • State "every PR references an issue" with an explicit temporary exception for Dependabot PRs. Dependabot still opens issue-less PRs until Part 15 (Replace Dependabot PRs with issue-filing sweeps; SDK watch; the factory overview #4874) retires them, and Part 15 removes the exception.
  • Delete CLAUDE.md. No @./AGENTS.md pointer file stays behind.

Acceptance criteria

  • AGENTS.md exists at the root. CLAUDE.md is deleted and no pointer file remains.
  • Every §4 and §5 row marked S1 is present.
  • The PR shows a fresh Claude Code session following a rule that exists only in AGENTS.md.
  • The skills index lists only skills that exist; each later skill PR adds its own row.

Branch

Targets v2/main. Closes #N won't auto-close an issue on a non-default branch, so close this one by hand on merge and move the card to Done.

Activity

  1. added this to the v2.0.0 milestone on Sep 27, 2026
  2. self-assigned this
    on Sep 27, 2026
  3. cliffhall commented on Sep 29, 2026

    @cliffhall
    MemberAuthor

    Rules from #4865 (Python gate parity, PR #4893) for AGENTS.md, handed over per §9 since #4862 owns that file:

    Python instructions

    • uv, never pip. Every Python server (src/fetch, src/git, src/time) is a standalone uv project with its own pyproject.toml, uv.lock and .python-version. Install with uv sync --locked --all-extras --dev from the server's directory; run tools with uv run --frozen <tool>. Python ≥ 3.10.
    • npm run validate:py is the Python gate, and it runs every Python server. For one server: node scripts/validate-py.mjs <server>. Each server runs the same fail-fast chain: uv sync --locked, ruff check ., ruff format --check ., pyright, pytest, uv build. CI's python.yml runs the same script per matrix package, so the local command and CI cannot drift.
    • Run uv run ruff format . in each server you touched before committing. CI fails on a ruff check or ruff format --check finding exactly as on a failing test. Ruff has no warning tier: a finding is a finding.
    • Fix a finding; never suppress it through config. Do not add ruff ignore/exclude entries, pyright excludes or disabled rules to make a check pass. A genuinely necessary inline waiver (# noqa: <code>, # pyright: ignore[<rule>]) names the specific rule and carries a one-line justification.
    • A new Python server is gated by adding its pyproject.toml. validate-py.mjs discovers servers under src/ rather than listing them, and CI's matrix discovers them the same way; a new server must ship with tests (pytest exits non-zero when it collects none).
    • pyright-clean type hints on all new code; async/await with pytest-asyncio where a server is async; follow each server's existing module layout.
    • Build output is never a gate target: dist/ and .venv/ are excluded by ruff's and pyright's defaults — keep it that way rather than widening a scope to cover them.
  4. cliffhall commented on Sep 29, 2026

    @cliffhall
    MemberAuthor

    Rules handed over from #4863 (PR #4895) for AGENTS.md, per the wave-2 plan: #4863 does not create AGENTS.md. They belong under Maintenance Rules. Whichever PR merges second reconciles.

    Maintaining the skills

    Skills are conditional — a skill's body loads only when it is invoked — so a
    skill that stops being reachable loses behavior silently. These rules keep
    that from happening. How to write a description that fires, and eval cases that
    measure it, is docs/skill-authoring.md.

    1. npm run verify:skills must pass. CI runs it in the root-guards job of
      .github/workflows/typescript.yml (the root validate picks it up once
      TypeScript workspace gate: Prettier, ESLint, root validate, CI #4864 builds that). It parses each SKILL.md's frontmatter the way Claude
      Code does and fails on anything that would strip the metadata — most
      importantly malformed YAML, which loads the body with an empty
      description, so /skill-name still works and a manual spot check passes
      while the skill can never auto-fire again. Quote any description containing
      # or :.
      An unquoted colon is enough to break the YAML; an unquoted #
      is read as a comment and truncates the description silently from that
      point on, leaving a non-empty string every other check accepts.
      ⚠️ Frontmatter is only read when the opening --- is the file's first
      line
      — no BOM, no leading blank line.
      The authoritative schema check, claude plugin validate, runs as its own
      guaranteed step, npm run verify:skills:cli, in the same CI job (and in
      local:gate once Add local:gate, pre-push-gate skill #4871 builds it). It uses an installed CLI only when it
      matches the pin exactly
      , and otherwise fetches the pinned package with
      npx -y: a newer local CLI is a different schema from CI's.
    2. Every skill declares its invocation mode explicitly.
      disable-model-invocation is required on every skill (true = reachable
      only by name, false = the model may fire it). Default to false.
      Invoking a skill has no side effects — it loads instructions — and work is
      usually asked for in prose ("create a PR for #N"), where a true skill is
      absent from the listing and cannot be reached at all. Reserve true for a
      procedure that is only ever started deliberately (a release). A skill that is
      background knowledge rather than an action also sets user-invocable: false.
      ⚠️ A true skill cannot be reached from another skill's body either: a
      pointer to it from a model-invoked skill is a dead end for the model.
      ⚠️ paths is not a free win. Measured in the Inspector, adding it
      roughly halved how often the same skill fired from a conversational prompt,
      and a prompt-only eval can never exercise a path trigger, so shipping paths
      means shipping an untestable claim. Use it only when the skill is useless
      outside the matched files, and state that trade-off in the PR.
    3. A model-invoked skill carries committed eval cases at
      evals/evals.json — at least five positives, plus negatives
      ("expect": null). verify:skills enforces both. Five is a floor for
      breadth, not variance: each prompt is scored on its own passes / RUNS,
      so more prompts cover more of the ways someone reaches the skill rather than
      steadying any rate. npm run skills:eval runs the cases against Claude, and
      against GitHub Copilot with AGENT=copilot; it spends metered model calls
      and reports a hit rate, so it is deliberately not a gate. Run the whole
      suite after adding a skill, flipping an invocation mode, or editing a
      model-invoked description — adding a skill can lower the trigger rate of the
      ones already there. A prompt whose answer is already in AGENTS.md is not a
      trigger case: the model answers without the skill and the case reads as a
      miss.
    4. Re-check the listing budget when adding a skill. Claude Code loads the
      name and description of every model-invoked skill into context, truncates the
      listing when it overflows, and drops the least-invoked entries first —
      exactly the skills that must fire on their own. verify:skills prints this
      repo's share against the budget in scripts/lib/skill-manifest.mjs
      (LISTING_BUDGET, 4,000 characters) and fails when it is exceeded. Raise the
      budget deliberately or tighten a description; each entry is capped at 1,536
      characters regardless, so put the key use case first.

    ⚠️ Skills in nested .claude/skills/ directories below the working directory do
    not load at startup. Keep every skill in the repo-root .claude/skills/.

    ⚠️ Temporary, until the first skill lands: verify:skills passes an empty
    .claude/skills/ under the EMPTY_SKILLS_BOOTSTRAP allowance. The first PR
    that adds a skill (#4866, #4867, #4870, #4871 or #4872, whichever merges first)
    sets it to false, deletes .claude/skills/.gitkeep, and adds its row to the
    skills index; verify:skills fails until it does.

  5. cliffhall commented on Sep 29, 2026

    @cliffhall
    MemberAuthor

    AGENTS.md rules from #4864 (Part 4, TypeScript workspace gate), handed to #4862. PR #4896 builds the machinery. Per §9 S3, these rules belong in AGENTS.md, which this issue owns, so here is the text I would add. It fits as a new section after TypeScript instructions, or folded into the Inspector-template sections "Build output is never a gate target" and "Lint has no warning tier". Whichever PR merges second reconciles the two.

    Format, lint and validate

    • Run npm run format at the repo root before committing. It runs the root Prettier (format:root, the root tooling) and every workspace's format. format:check is the non-fixing form the gate runs, and CI fails on any unformatted file, so run the fixer first rather than letting the check catch it. Prettier is pinned exactly at the root, and the formatted surface is JS/TS source (*.{ts,mts,cts,js,mjs,cjs}); every prettier script passes the root .prettierignore with --ignore-path, so the workspaces (which run from their own directories) read the same ignore list as the root.
    • npm run validate is the inner loop. Each TypeScript server's validate runs format:check → lint → typecheck → build → test for that package only (npm run validate -w src/<server>). The root validate runs validate:guards (the root tooling's format and lint, verify:format-coverage, verify:skills, verify:typecheck-coverage, verify:dep-lockstep, test:scripts) and then npm run validate --workspaces. In CI, each typescript.yml matrix leg runs only its own package's validate, and the root-guards job runs validate:guards. test is the fast run; coverage is the instrumented one.
    • Lint has no warning tier. Every lint script runs ESLint with --max-warnings 0, so a warning fails the gate exactly as an error does. The one config is the root eslint.config.js: type-aware for the servers, with @typescript-eslint/no-floating-promises at error. Every promise is awaited, returned, ended with .catch(…), or discarded with void plus a one-line comment saying why the caller cannot hold it. A rule you intend to enforce is set to error, not left at warn, so the editor squiggle reads right too.
    • Build output is never a gate target. No gate (format, lint, typecheck) reads generated output: dist/, coverage/ and node_modules/ are excluded in eslint.config.js's globalIgnores and in the root .prettierignore, and each workspace's tsconfig.test.json excludes them. When a build starts writing somewhere new, add it to those ignore lists in the same change.
    • No suppression through config. A finding is fixed, not silenced: never turn a rule off, widen an ignore, or add a first-party path to an ignore list to make lint or format:check pass. If a rule genuinely must be waived on one line, use its inline disable comment with a one-line justification.
    • Every tracked TypeScript file gets a tsc pass. Each workspace's typecheck runs tsc -p tsconfig.test.json, which covers the whole workspace including the tests the build tsconfig.json leaves out. verify:typecheck-coverage fails when a tracked file falls outside it, and verify:format-coverage fails when a tracked source file matches no format:check glob. A new file that trips either goes into the right include or glob, never into an exemption.
    • Shared toolchain versions move in lockstep. typescript, vitest, @vitest/coverage-v8, prettier and @types/node are each declared with one identical range everywhere they are declared, or only once at the root (verify:dep-lockstep). Bump one in every manifest in the same change, then npm install at the root.
  6. changed the title [-]Agentic software factory Part 2: AGENTS.md: the absolute rules; delete CLAUDE.md[/-] [+]AGENTS.md: the absolute rules; delete CLAUDE.md[/+] on Oct 5, 2026
  7. added a commit that references this issue on Oct 11, 2026
    9511de6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

agent guidanceRules, skills, and quality gates for agentsdocumentationImprovements or additions to documentationv2

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions