From bc0bd7b292be1fa86b8b684d1353113fbfe89743 Mon Sep 17 00:00:00 2001 From: Connorrmcd6 Date: Thu, 18 Jun 2026 07:03:23 +0200 Subject: [PATCH 1/2] docs: reconcile AGENTS.md guidance into one approaches section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guide had two adjacent, contradictory sections — "keep them separate" immediately followed by "you can fold claims in". Merge into one "Where claims can live" section that leads with the general rule (claims can live in any file added to the glob), then presents both approaches with the central hubs/ directory flagged as recommended. Also correct the frontmatter note: agent runners generally load AGENTS.md/CLAUDE.md as raw text and do NOT strip YAML frontmatter, so the anchors block is visible to the agent — framed as a real downside of the inline approach, not a non-issue. Co-Authored-By: Claude Opus 4.8 --- docs/guides/authoring-hubs.md | 54 +++++++++++++++++++-------------- docs/reference/configuration.md | 6 ++-- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/docs/guides/authoring-hubs.md b/docs/guides/authoring-hubs.md index ea780eb..6b7141e 100644 --- a/docs/guides/authoring-hubs.md +++ b/docs/guides/authoring-hubs.md @@ -127,11 +127,21 @@ surf verify --follow # renamed symbol OR moved file: re-point the anc Verifying without reading is the failure mode the whole tool exists to prevent. A green gate promises only "nothing anchored changed since last sign-off" — never that the prose is true. -## Hubs and `AGENTS.md` +## Where claims can live -Hubs are *declarative* domain briefings; `AGENTS.md` is *imperative* operating instructions for -coding agents. Keep them separate — don't copy hub prose into `AGENTS.md`. Instead, give -`AGENTS.md` a pointer block that sends agents to the hubs directory to search for what they need: +A hub isn't a special file *type* — it's **any file the `hubs` glob matches that parses as a hub** +(a `---`-fenced `anchors:` frontmatter block + a markdown body). Claims don't have to live under +`hubs/`: add any file to the glob in `surf.toml`, give it the frontmatter, and `surf` treats it +like any other hub. The same is true for `AGENTS.md` or `CLAUDE.md`. + +The common question is `AGENTS.md` — the *imperative* operating instructions for coding agents, +versus hubs, which are *declarative* domain briefings. There are two approaches, and **a central +`hubs/` directory is the recommended default.** + +### Recommended — keep hubs and `AGENTS.md` separate + +Keep the two concerns apart: don't copy hub prose into `AGENTS.md`. Instead, give `AGENTS.md` a +pointer block that sends agents to the hubs directory to search for what they need: ```markdown @@ -139,16 +149,17 @@ Context lives in [`hubs/`](./hubs/) — read only the hub(s) you need. ``` -When that block is present, `surf lint` checks it links the configured hubs directory and that -the directory exists. It deliberately does **not** enumerate individual hubs — that would push an -agent to read everything instead of the one hub it needs. +When that block is present, `surf lint` checks it links the configured hubs directory and that the +directory exists. It deliberately does **not** enumerate individual hubs — that would push an agent +to read everything instead of the one hub it needs. -## Claims in `AGENTS.md` / `CLAUDE.md` +This keeps `AGENTS.md` lean, stops the declarative and imperative content from drifting into each +other, and keeps verification metadata out of the file agents read as instructions. -A hub isn't a special file *type* — it's any file the `hubs` glob matches that parses as a hub -(frontmatter `anchors:` block + markdown body). So you *can* add `AGENTS.md` or `CLAUDE.md` to the -glob and give it hub frontmatter, making one file double as both agent instructions and verified -claims: +### Alternative — fold claims into `AGENTS.md` / `CLAUDE.md` + +If you'd rather keep the instructions and the verified claims about them in one file, add it to the +glob and give it hub frontmatter: ```toml # surf.toml @@ -169,16 +180,15 @@ anchors: ``` `surf verify` then hash-checks that claim like any other hub, and `surf for ` reports -`AGENTS.md` as anchoring into it. Two things to know before you do this: - -- **The whole file must parse as a hub.** The `---` frontmatter has to be the top block and - unknown fields are rejected — you can't sprinkle a claim mid-document. Confirm whatever consumes - `AGENTS.md`/`CLAUDE.md` tolerates YAML frontmatter (most agent runners ignore it). -- **It couples the file to code structure.** Renaming an anchored symbol will trip the gate on +`AGENTS.md` as anchoring into it. Three things to weigh: + +- **The whole file must parse as a hub.** The `---` frontmatter has to be the top block and unknown + fields are rejected — you can't sprinkle a claim mid-document. +- **The frontmatter is part of what the agent reads.** Most agent runners load `AGENTS.md` / + `CLAUDE.md` as raw text and don't strip YAML frontmatter, so the `anchors:` block lands in the + agent's context as a little extra noise. It's small and structured, but if you want `AGENTS.md` + to stay purely instructions, prefer the recommended approach above. +- **It couples the file to code structure.** Renaming an anchored symbol trips the gate on `AGENTS.md` — that's the point of Surface, but it means an agent-docs file now participates in CI. -This is a trade-off, not a recommendation. The default split above — declarative hubs, imperative -`AGENTS.md` with a pointer block — keeps the two concerns separate; folding claims into -`AGENTS.md` is there if you'd rather keep the instructions and the claims about them in one file. - See also: [CI integration](./ci-integration.md) · [Examples](../examples.md). diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index b949801..f52111f 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -21,8 +21,10 @@ hubs = ["hubs/*.md", "docs/hubs/*.md", "**/_hub.md"] ``` Any file a glob matches is treated as a hub if it parses as one (frontmatter `anchors:` block + -markdown body), so the list can also pull in files that aren't named like hubs — for example -`AGENTS.md` (see [Claims in AGENTS.md / CLAUDE.md](../guides/authoring-hubs.md#claims-in-agentsmd--claudemd)). +markdown body), so claims can live in *any* file — not just files under `hubs/`. The list can pull +in files that aren't named like hubs, e.g. `AGENTS.md` or `CLAUDE.md` (see +[Where claims can live](../guides/authoring-hubs.md#where-claims-can-live) for the recommended +layout and the trade-offs). > **`surf new` uses only the first glob.** When scaffolding a hub it writes into the directory > derived from the first pattern (e.g. `docs/hubs/*.md` → `docs/hubs/`); the other patterns are From a1708cd756b1d5b1da4c8039b0bce292bcc8ca51 Mon Sep 17 00:00:00 2001 From: Connorrmcd6 Date: Thu, 18 Jun 2026 07:08:28 +0200 Subject: [PATCH 2/2] docs: add "when not to anchor a file" caveat (README) "Any file can be a hub" invites anchoring marketing prose. Add a subsection explaining why a README is a poor fit: coarse claims that over-anchor, duplication of hub prose, and GitHub rendering frontmatter as a metadata table above the pitch. Co-Authored-By: Claude Opus 4.8 --- docs/guides/authoring-hubs.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/guides/authoring-hubs.md b/docs/guides/authoring-hubs.md index 6b7141e..3887cd4 100644 --- a/docs/guides/authoring-hubs.md +++ b/docs/guides/authoring-hubs.md @@ -191,4 +191,21 @@ anchors: - **It couples the file to code structure.** Renaming an anchored symbol trips the gate on `AGENTS.md` — that's the point of Surface, but it means an agent-docs file now participates in CI. +### When *not* to anchor a file + +"Any file can be a hub" doesn't mean every file should be. Pitch and marketing prose — a +`README.md` especially — is a poor fit: + +- **The claims are coarse.** A README describes behavior in broad strokes that span many symbols, + so an anchor either covers a near-whole-file span or trips on incidental edits — the + over-anchoring trap from [Choosing granularity](#choosing-granularity). +- **It usually duplicates hub prose.** The invariants a README restates should already be claimed + in the hubs anchored to the real code; anchoring the README just gives you a second copy to keep + honest. +- **On GitHub, frontmatter renders as a table.** GitHub displays a Markdown file's YAML frontmatter + as a metadata table at the top of the rendered page, so an `anchors:` block would sit above your + pitch on the repo's front page. + +Anchor the code, and let the README link to the docs instead. + See also: [CI integration](./ci-integration.md) · [Examples](../examples.md).