diff --git a/CHANGELOG.md b/CHANGELOG.md index a4b4a5e..ddcddba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ versions follow [Semantic Versioning](https://semver.org/). ## [Unreleased] ### Changed +- **`AGENTS.md` investigated and deliberately deferred to M2** ([ADR 0047](docs/decisions/0047-agents-md-deferred-to-m2.md); **nothing in the plugin changes**). The open spec — OpenAI-led, donated to the Linux Foundation, 60k+ repositories — is a single predictable file of project context for AI agents, and this plugin had **no mention of it anywhere** while `docs/ROADMAP.md` M2 states the harness-neutral direction outright. The spec says nothing about coexisting with tool-specific files, so the behaviour was **measured rather than read** (Claude Code 2.1.280, three throwaway directories, a fresh `claude -p` with read tools disabled so the answer comes from what was *loaded*): `AGENTS.md` alone is read; `AGENTS.md` **beside** a `CLAUDE.md` is **ignored entirely**; a `CLAUDE.md` containing `@AGENTS.md` loads both. **That kills the cheapest-looking option** — a pointer would be read by no Claude Code session while looking installed, the failure class logged twice this week. Two further measurements are recorded so M2 does not re-derive them: the generated `CLAUDE.md` is **~86% harness-neutral**, its Claude-bound lines clustering in three identifiable blocks rather than diffusing; and `CLAUDE.md` is referenced **77 times across 10 skills** plus 10 templates, which is what makes a rename disproportionate today. The recommendation carried forward is the **`@AGENTS.md` import**, not a pointer and not a rename. One argument found along the way **does not need M2**: the methodology's entry points are written *as commands* (*"decided → `/groundrules:add-adr`"*), meaningless to another tool or to a human without the plugin — splitting would force the portable half to state the **act and the place**, which is a better generated file regardless. - **The ADR index invariant becomes a check** (`test/check-adr-index.sh`, **dogfood only**). On 2026-09-21 an ADR landed on `main` without its row in `docs/decisions/README.md` and survived, because the only thing guarding that invariant was a line in the release checklist — read at release, days after the omission. A set comparison has exactly one correct answer, so it is a check, not a reminder, and not an eval case: an eval measures whether configuration changes behaviour, which is the wrong instrument for *is every file listed*. The script compares both directions — a file with no row, a row with no file — and flags duplicates, since a row listed twice reads as agreement until someone diffs them. Deterministic, offline, zero tokens; verified to fail as well as pass. **It runs on every pull request** (`.github/workflows/adr-index.yml` — one workflow, one job, one step; the stack is pure Markdown and JSON, so CI is a real surface and stays the size of the invariant it guards). The first placement considered was a git `pre-commit` hook, rejected on a mix of one sound reason and one that turned out false — **corrected the same day, see below**. `core.hooksPath` on the maintainer's machine does point at `ggshield`'s directory, and that setting *replaces* the hooks directory rather than adding to it, so **git itself** never looks in `.git/hooks`. But the occupying script **calls the repository's hook and fails the commit if it fails**, so a local hook would have run after all. CI is the better instrument rather than the fallback: it travels with the clone, `--no-verify` cannot walk past it, and it fires at the **pull request**, which is where the omission landed. **Proven both ways** before shipping — a throwaway PR removing an index row went red naming the file, restoring the row turned the same workflow green. A workflow whose red has never been seen is a badge. The lesson generalises in `docs/LEARNINGS.md`, sharpened by the correction: **read what occupies the slot — occupancy is not a second question, it answers the first.** Whether your control runs is a property of the occupant's *code*, not of the configuration pointing at it; here the configuration said *unreachable* and the script said *I call it myself*. Skipping that read cuts the other way too: writing into the `ggshield` directory itself, where the entries are symlinks into `dotfiles` beside a `.bak-` from a previous overwrite, **would** have destroyed a working guard while reading as installed. - **A false claim was recorded and corrected the same day, and the failure is logged** (`docs/AGENT-EVALS.md`, 2026-09-21). The *repository hooks are dead here* half came from a peer, asserted from one measurement and generalised past it. It was not accepted blindly — the redirect, the directory listing and `git rev-parse --git-path hooks` were all checked, and all three were true. **The conclusion drawn from them was not, and the file it was about was never opened.** That is a variant of *asserts without verifying* that survives the usual guard, because verification happened on the **premises**: checking every fact a peer measured and adopting their inference is agreement with extra steps, and it feels like diligence. The tell is that the conclusion concerns something none of the checked commands touched. **The decision did not move**: CI still wins for the two reasons that were never about the slot — `.git/hooks/` is not versioned and does not survive a clone, and `--no-verify` walks past any local hook including for whoever merges the PR that introduces the defect. - **[ADR 0045](docs/decisions/0045-the-lock-was-scoped-to-four-skills.md) states both of its conditions where they are read.** Its heading announced *additive, reversible, and under version control*, while the condition that actually made the cut — *does being locked block a methodology from starting* — appeared only in the exclusion sentence below. A reader applying the announced criterion to `close` reaches the opposite conclusion from the one the ADR reached. Both conditions are now stated together, with `close` kept as the worked example: it is the safest skill in the plugin, proposing and never writing without confirmation, so on safety alone it would be the strongest candidate of the fifteen — and it stays locked because a locked `close` blocks nothing from starting. That is what shows the second condition is doing work rather than decorating the first. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 14eba6c..796f74b 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -74,6 +74,13 @@ The strategic reason the repo is named `groundrules` (harness-neutral). Keep the which harnesses first, how the Markdown SKILL instructions port vs. need per-harness adapters, and the per-harness distribution model (there is no universal "plugin"). +> **Start here**: [ADR 0047](decisions/0047-agents-md-deferred-to-m2.md) parks the `AGENTS.md` +> question against this milestone with the groundwork already done — the spec read, the harness +> behaviour measured (a pointer `AGENTS.md` beside a `CLAUDE.md` is read by **nothing**; an +> `@AGENTS.md` import loads both), the generated file measured at ~86% harness-neutral, and the cost +> counted at 77 references across 10 skills. Re-run its three-directory test before acting: those +> numbers are a product behaviour, not a contract. + ### M3 — Cross-project dashboard (companion tool) A tool that scans a folder of projects, detects the groundrules ones (normalized structure + diff --git a/docs/decisions/0047-agents-md-deferred-to-m2.md b/docs/decisions/0047-agents-md-deferred-to-m2.md new file mode 100644 index 0000000..2f1dbc9 --- /dev/null +++ b/docs/decisions/0047-agents-md-deferred-to-m2.md @@ -0,0 +1,121 @@ + +# 0047 — `AGENTS.md`: deferred to M2, with the measurements taken now + +**Date**: 2026-09-23 +**Status**: Accepted (the decision is to defer; the trigger is named below) + +## Context + +[`AGENTS.md`](https://agents.md) is an open specification for a single, predictable file carrying +project context for AI coding agents — *a README for agents*. Formalised in August 2025 under +OpenAI's lead with Google, Cursor and Factory, donated to the Linux Foundation's Agentic AI +Foundation in December 2025, and reported at 60,000+ repositories and 20+ supporting tools. Plain +Markdown, **no required schema**: the spec says *"use any headings you like; the agent simply parses +the text you provide."* In a monorepo the nearest file in the tree wins. + +This plugin generates `CLAUDE.md` and has **no mention of `AGENTS.md` anywhere**, while +[M2](../ROADMAP.md) states the harness-neutral direction outright: *"keep the generated output +harness-agnostic; only the delivery of the skills changes per harness."* That is a gap between a +recorded direction and what the plugin ships, and it surfaced from a third-party guide that merely +named the file without arguing for it. + +**The spec does not settle the question**: it says nothing about coexisting with tool-specific files +like `CLAUDE.md`. Only the harness does, so the behaviour was measured rather than read. + +### Measured on 2026-09-23, Claude Code 2.1.280 + +Three throwaway directories, each with distinctive markers, queried by a fresh `claude -p` with all +read tools disabled — so the answer comes from what was *loaded*, not from what it could open: + +| Files present | Loaded | +|---|---| +| `AGENTS.md` alone | `AGENTS.md` | +| `AGENTS.md` **and** `CLAUDE.md` | **`CLAUDE.md` only** — the other is ignored | +| `CLAUDE.md` containing `@AGENTS.md` | **both** | + +**This kills the cheapest-looking option.** An `AGENTS.md` pointing at `CLAUDE.md`, sitting beside +it, is **read by no Claude Code session**. It would serve other tools only, while looking installed +to anyone reading the repository — the failure class this repository has logged twice this week. + +### The other two measurements + +**Content**: of the 199 lines of `CLAUDE.md.tpl`, **~86% owe nothing to Claude Code**. The +harness-bound lines are 9 citing a `/groundrules:*` command and 18 citing the interface or the +`.claude/` tree, and they **cluster** — `### Permissions and settings`, `## Claude Code workflow`, +and the `.claude/` subtree under `## Key files and folders`. It is not a diffusion. + +**Cost**: `CLAUDE.md` is referenced **77 times across 10 skills** (`bootstrap` 20, `adopt` 17, +`slim` 11, `migrate` 10…) and in 10 templates. A rename is invasive and would need a migration for +every existing project. The import pattern is additive, but not free: `slim` measures a 200-line +budget that would then span two files, and `verify-bootstrap` would need to know about the second. + +## Decision + +**Defer. Nothing changes in the plugin now.** No `AGENTS.md` is generated, no template splits, no +skill learns a second filename. + +**The trigger is [M2](../ROADMAP.md) — *support harnesses beyond Claude Code*.** M2's own ADR is the +place this belongs, because the question is only worth answering alongside *which harnesses first* +and *how the skills are delivered per harness*. Answering it alone would commit the output format +before the delivery model it is meant to serve exists. + +**What the measurements buy, and why they are recorded rather than re-derived**: they expire slowly +(a spec and a file layout), while the effort to obtain them is a guide-agent round trip plus three +`claude -p` runs. Re-deriving them at M2 would cost the same again and risk a different answer from +a different method. + +**The recommendation to carry into M2, should it be taken**: `CLAUDE.md` **imports** `@AGENTS.md`, +which holds the portable content; `CLAUDE.md` keeps the import line and the three harness-bound +blocks. Not a pointer — measured dead. Not a rename — cost without a matching benefit today. + +**One argument found along the way that stands on its own**, independent of any harness: the +methodology's entry points are currently written *as commands* (*"decided → `/groundrules:add-adr`"*), +which is meaningless to any other tool and to a human reading the repository without the plugin +installed. Splitting the file forces the portable half to state **the act and the place** — *record +the decision as an ADR in `docs/decisions/`* — with the command as a shorthand beside it. That is a +better generated file whatever happens to `AGENTS.md`, and it would probably never be written +without this constraint. **It does not need M2**; if M2 is never tackled, it is still worth doing. + +## Alternatives considered + +- **Generate a pointer `AGENTS.md` now** — rejected on measurement, not on taste: with a `CLAUDE.md` + present it is read by nothing, and a file that looks installed and is inert is worse than its + absence. +- **Rename the generated file to `AGENTS.md`** — rejected for now: 77 references across 10 skills, + 10 templates, and a migration for every bootstrapped project, to serve a portability nobody has + yet asked for. Reconsider at M2, where the delivery model makes it a real question. +- **Adopt the import pattern now** — the option this ADR recommends *for later*, rejected *now* for + lack of demand. Its cost is small but not zero, and `slim`'s budget across two files is a design + question that deserves M2's context rather than an afternoon's. +- **Say nothing and revisit from scratch at M2** — rejected. The three measurements are the + expensive part and they do not spoil; discarding them is how the same afternoon gets spent twice. + +## Consequences + +### Positive +- The gap between M2's stated direction and what ships is now **recorded rather than latent**. +- M2 starts with the harness behaviour already measured and the cost already counted. +- The command-versus-act wording problem is named, and can be fixed without waiting for M2. + +### Negative / Tradeoffs +- **A generated project stays Claude-Code-shaped** for anyone using another tool on the same + repository. That is the status quo, now with a reason attached instead of an omission. +- Deferred decisions rot: the spec may move, and the precedence rule measured here is a *product + behaviour*, not a contract. **Re-run the three-directory test before acting on this ADR** — the + numbers in it are a snapshot, not a guarantee. + +### Neutral +- No change to any skill, template, or generated file. + +## Notes + +- Spec: . Surfaced by a third-party guide that named the file without arguing it; + everything load-bearing here came from reading the spec and measuring the harness. +- **One claim in the investigation was not verifiable from this machine** and is therefore not + load-bearing: that `AGENTS.md` support depends on server-side feature flags and is absent on + Bedrock, third-party providers and telemetry-disabled sessions. It came from documentation via a + subagent. It would argue *against* making `AGENTS.md` the sole entry point — which the recommended + import pattern does not do, so the recommendation holds either way. Verify it at M2 if it matters. +- Related: [ADR 0023](0023-project-scope-for-team-portability.md) (portability of the output), + [ADR 0021](0021-context-economy-index-over-doc-search.md) (why duplicated context is a cost), + [`docs/ROADMAP.md`](../ROADMAP.md) M2. diff --git a/docs/decisions/README.md b/docs/decisions/README.md index 953c78a..cd6b710 100644 --- a/docs/decisions/README.md +++ b/docs/decisions/README.md @@ -76,3 +76,4 @@ No ADR needed for trivial choices or implementation details. | [0044](0044-runner-enforces-verifier-isolation.md) | The runner spawns the verifier separately; the loop's isolation becomes a fact rather than a request | Accepted | 2026-09-09 | | [0045](0045-the-lock-was-scoped-to-four-skills.md) | `disable-model-invocation` applied per skill: five methodology skills open, five destructive ones stay locked (refines 0003) | Accepted | 2026-09-21 | | [0046](0046-orchestrated-working-regime.md) | Orchestrated regime: the request for work carries the authorisation for the commit (amends 0036's commit rule) | Accepted | 2026-09-21 | +| [0047](0047-agents-md-deferred-to-m2.md) | `AGENTS.md` deferred to M2, with the harness behaviour measured now (a pointer is read by nothing) | Accepted | 2026-09-23 |