# 0.1.0 — 2026-05-14

Release-candidate binary. The CLI covers the full subcommand surface the specs describe (§2.7) — including the opt-in grounding floor ([§FS-check.3.6](../functional-spec/FS-check.md#36-ungrounded-source-file-opt-in)) and the coverage index ([§FS-cover](../functional-spec/FS-cover.md#fs-cover-grund-groups-citations-by-scanned-file)); the crates.io publish is gated by the release workflow's live [§RM-distribution-naming](../roadmap.md#rm-distribution-naming-verify-package-names-before-first-publish) check, while the larger distribution arc — the `grund-core`/`grund-cli` workspace split, the npm/PyPI bindings, the optional `grund-lsp` server, and `grund check --watch` — plus the diff-aware co-change gate ([§RM-cochange-gate](../roadmap.md#rm-cochange-gate-a-pre-commit--ci-recipe--no-impl-change-without-spec-and-test)) are tracked in `docs/roadmap.md`.

### 2.1 Baseline

- Origin: the scheme, the `e2e/cases/*` fixtures, and the test harness landed in the initial commit (2026-05-08, formerly tagged `0.0.0`); no binary was published at that point. `0.1.0` is the first published release and folds that history in.
- Initial scheme in place: `grund_config_version = 1` ([§FS-config.5](../functional-spec/FS-config.md#5-schema-versioning)), `AGENTS.md` init block at **v1** ([§FS-init.2](../functional-spec/FS-init.md#2-outputs)).
- `grund check` implemented against the canonical grammar ([§FS-check](../functional-spec/FS-check.md#fs-check-grund-validates-every-reference-in-a-repo)), with the full e2e corpus and the byte-for-byte determinism sweep ([§FS-non-goals.13](../functional-spec/FS-non-goals.md#13-anything-that-would-let-two-grund-installs-disagree)).
- Decision records in scope: [§DA-reference-checker-name](../decisions/architectural/DA-reference-checker-name.md#da-reference-checker-name-name-for-the-spec-reference-checker-tool) (superseded by [§DA-rename-to-grund](../decisions/architectural/DA-rename-to-grund.md#da-rename-to-grund-rename-gnd-to-grund-before-first-publish)) and [§DF-reference-marker](../decisions/functional/DF-reference-marker.md#df-reference-marker-use--as-the-reference-marker-with--as-the-typing-trigger).

### 2.2 Changed

- [§FS-init.2.3.4.4](../functional-spec/FS-init.md#2344-project-map) / [§FS-config.3.4](../functional-spec/FS-config.md#34-kinds--recognized-prefixes): render the generated `AGENTS.md` project map as a compact link list (`- [KIND](home): description`) instead of a Markdown table, and make the default kind titles descriptive enough to read as human guidance rather than labels repeated from the kind names. Content change within `AGENTS.md` block v1; no schema bump.
- [§FS-init](../functional-spec/FS-init.md#fs-init-grund-bootstraps-a-new-grund-conformant-repo): drop `docs/state-and-direction.md` from the `--docs` scaffold; the soft direction folds into `docs/roadmap.md` and the project-specific change rules move to `AGENTS.md`. The `AGENTS.md` v1 block's `docs/` table is updated to list `roadmap.md` and `changelog.md`. Content change within v1; no schema bump.
- raison-detre: reframe around the polyglot pitch and the three pillars (verify in source comments, refactor-safe IDs, extract). No surface change; sharpens what `grund` is for vs. off-the-shelf Markdown link checkers (`lychee`, `markdown-link-check`).
- [§FS-non-goals.6](../functional-spec/FS-non-goals.md#6-decision-database-audit-log-history-tracking): drop the `grund graph` forward-reference (it was "planned in roadmap" but never was); state that ID-graph visualisation is not a committed feature, and point the reverse-lookup need at the now-specified [§FS-refs](../functional-spec/FS-refs.md#fs-refs-grund-lists-every-citation-of-an-id). The §6 non-goal (no decision database / no history view) is unchanged.
- [§FS-config.3.4](../functional-spec/FS-config.md#34-kinds--recognized-prefixes): enumerate the canonical `[[kinds]]` tables in full (G/FS/AR/DF/DA/E2E/RM with `folder` and `title`) instead of showing only `FS` — [§FS-init.2.4](../functional-spec/FS-init.md#24-generated-agentsgrundtoml) says the generated `.agents/grund.toml` matches this section exactly, so it had to be exact. Drop the `grund new` (future) reference; clarify `title` is metadata surfaced in `--format=json` and hover, not injected into `grund show --format=md` text.
- [§FS-config.3.4](../functional-spec/FS-config.md#34-kinds--recognized-prefixes) / [§FS-distribution](../functional-spec/FS-distribution.md#fs-distribution-grund-distribution-targets): add `RM` ("Roadmap milestone") to the canonical `[[kinds]]` set (now seven, `folder = "docs"`) — `RM` declarations are H2 headings inside the single file `docs/roadmap.md`, parallel to how `G` is declared inside `docs/goals.md`, so `grund check` validates `§RM-…` citations like any other reference instead of silently ignoring them. This is an *additive* grammar change (a repo that overrides `[[kinds]]` is unaffected; a default-config repo with a `§RM-…` token in prose now gets it checked, which is the intent of the marker); `grund_config_version` and the `AGENTS.md` block version are unchanged. `templates/grund.toml` (and therefore the embedded `grund init` config) gains the `RM` table.
- [§FS-show.1](../functional-spec/FS-show.md#1-inputs) / [§FS-show.2.1.1](../functional-spec/FS-show.md#211-brief---brief): document that `grund show <ID>` also takes a positional `[<path>]` (with `--path` as an accepted alias), and that `--head` combined with a section path prints the lead prose of *that* section — previously the spec listed neither.
- [§FS-errors.3](../functional-spec/FS-errors.md#3-message-text): the worked example for the "lowercase first letter" rule used `dangling citation: <ID>`, which is not the text `grund` emits (`unknown reference <ID>`); the example now matches the shipped message.
- [§FS-distribution.1](../functional-spec/FS-distribution.md#1-targets): stop claiming the PyPI name `gnd` is "free" — [§DA-reference-checker-name](../decisions/architectural/DA-reference-checker-name.md#da-reference-checker-name-name-for-the-spec-reference-checker-tool) records it as a dormant squat; the text now matches that record and points at [§RM-distribution-naming](../roadmap.md#rm-distribution-naming-verify-package-names-before-first-publish) as the gate that re-verifies every name (and picks an explicit alternate if a name assumed dormant turns out live) before the first publish. *(Mooted later this release by the `gnd` → `grund` rename — see Renamed below; `grund` is clean on crates.io and PyPI.)*
- [§FS-distribution.1](../functional-spec/FS-distribution.md#1-targets) / [§DA-pypi-package-name](../decisions/architectural/DA-pypi-package-name.md#da-pypi-package-name-pypi-uses-gnd-cli-as-the-package-name): the final registry check found PyPI `gnd` unavailable to this project, so the planned PyPI package was set to `gnd-cli`; the installed binary remained `gnd`, and the Python import module remained intended as `gnd`. *(Superseded by the rename — see Renamed below.)*
- README: drop `--watch` from the `grund check` synopsis row (it is specified in [§FS-check.6](../functional-spec/FS-check.md#6-watch-mode---watch) but not yet implemented — `grund check --watch` errors as an unknown flag); add `docs/discussions/` to the project-layout list.
- `.agents/grund.toml` (this repo): add the `[fmt.cross_refs]` block so the on-disk config matches what `grund init` now writes ([§FS-config.3.7](../functional-spec/FS-config.md#37-fmtcross_refs--cross-reference-emission) / [§FS-init.2.4](../functional-spec/FS-init.md#24-generated-agentsgrundtoml) require the generated config to carry every documented key).
- [§FS-config.3.5](../functional-spec/FS-config.md#35-scan--what-gets-walked) / 3.6: note that the default `comment_prefixes` set is broader than the [§AR-scanner.4](../architecture/AR-scanner.md#4-inline-declarations-in-language-doc-comments) doc-comment table (`;`, `--`, `*`, `/*`); define `relative_paths = false` (paths relative to the path argument / cwd, never absolute — keeps [§FS-errors.4](../functional-spec/FS-errors.md#4-determinism) intact); cross-reference `color` to [§FS-errors.3](../functional-spec/FS-errors.md#3-message-text).
- [§AR-scanner.2.1](../architecture/AR-scanner.md#21-declaration-detection)–2.3: pin the heading grammar that section resolution depends on — declaration heading level `L` is recorded; a depth-`d` section heading is `#{L+d}` followed by a `d`-component dotted number with an optional trailing `.`; section heading text is recorded alongside the path. Add the string-literal carve-out for **bare** citations in source files (mirrors `grund fmt` [§FS-fmt.2.3.1](../functional-spec/FS-fmt.md#231-string-literal-exclusion-rule)); marker-prefixed citations are still recognised everywhere.
- [§FS-check.1.1](../functional-spec/FS-check.md#11-recognized-citations) / 2: document the source-file string-literal carve-out for `check`, and the partial-scan semantics — a per-file read/decode failure mid-walk is reported `error: <path>: <reason>`, the walk continues, collected findings still print, and the run exits `2` (incomplete view).
- [§FS-id](../functional-spec/FS-id.md#fs-id-grund-proposes-ids-for-new-declarations): fix the §2.1/§2.2 examples to use the documented default `[id] format` (`FS-008-…`, with a parallel `{kind}-{slug}` example matching grund's own repo); add §4.1 defining `grund id` under number-less and slug-less ID formats (`--width` ignored, JSON `number` is `null`, collision check matters more).
- [§FS-show](../functional-spec/FS-show.md#fs-show-grund-reads-a-single-declaration-body-by-id): clarify `text` vs `md` vs `json` output (§3.1 — `md` includes the heading verbatim, `title` is not injected; `json` is one object, `section` is `null` for a whole declaration); §2.3.4 defines `show` on a broken stub (exit 1, bare query line); §2.3.3 spells out relative section depth inside doc-comments.
- [§FS-init.2.1](../functional-spec/FS-init.md#21-files-written-updated-or-left-in-place) / 2.3: add `docs/roadmap.md` and `docs/changelog.md` to the `--docs` scaffold (the generated `AGENTS.md` block links to them); restate that the canonical `AGENTS.md` block text is embedded in the binary (reference copy `templates/AGENTS.md`), versioned by the `vN` marker — `grund check` validates the markers and version, not a byte-diff.
- [§FS-errors](../functional-spec/FS-errors.md#fs-errors-grund-emits-messages-in-fixed-shapes): add [§FS-refs](../functional-spec/FS-refs.md#fs-refs-grund-lists-every-citation-of-an-id) and [§FS-cli](../functional-spec/FS-cli.md#fs-cli-grunds-command-line-surface-conventions) to the cross-cutting list; §2.1 now covers `grund refs` lines, §2.2 covers [§FS-cli](../functional-spec/FS-cli.md#fs-cli-grunds-command-line-surface-conventions)'s unknown-subcommand / bad-flag errors.
- [§FS-config.3.7](../functional-spec/FS-config.md#37-fmtcross_refs--cross-reference-emission): add the `[fmt.cross_refs]` block (`enabled`, `anchor_format`) to the documented schema. The detailed contract still lives in [§FS-fmt.6.7](../functional-spec/FS-fmt.md#67-configurability) and [§DF-md-link-anchor-strategy](../decisions/functional/DF-md-link-anchor-strategy.md#df-md-link-anchor-strategy-heading-text-slugs-re-derived-on-every-fmt-pass); [§FS-config.3.7](../functional-spec/FS-config.md#37-fmtcross_refs--cross-reference-emission) exists because [§FS-init.2.4](../functional-spec/FS-init.md#24-generated-agentsgrundtoml) writes every key in it, so the generated `.agents/grund.toml` (and `templates/grund.toml`) now includes it.
- [§FS-init.2.1](../functional-spec/FS-init.md#21-files-written-updated-or-left-in-place): rewrite the `--docs` scaffold description to match what `grund init` actually emits — richer starter files embedded in the binary (`raison-detre.md` with its three H2 sections, the spec READMEs with an empty ID/Subject table, `e2e/README.md` with its one-line note, `roadmap.md` / `changelog.md` with the H1 plus a placeholder line) rather than bare H1-plus-placeholder stubs. The "byte-identical at the same `grund` version" guarantee ([§FS-non-goals.13](../functional-spec/FS-non-goals.md#13-anything-that-would-let-two-grund-installs-disagree)) is unchanged.
- [§FS-init](../functional-spec/FS-init.md#fs-init-grund-bootstraps-a-new-grund-conformant-repo): the scaffolded `functional-spec/` and `architecture/` README templates no longer claim "`grund check` enforces it; missing links are errors" about the index — `grund check` has no such rule (it is not in [§FS-check.3](../functional-spec/FS-check.md#3-errors-detected)); the templates now state README linkage as a convention.
- [§FS-id](../functional-spec/FS-id.md#fs-id-grund-proposes-ids-for-new-declarations): `grund id --help` no longer presents `G, FS, AR, DF, DA, E2E, RM` as *the* kind list — it labels them as the *defaults* and points at `grund config show` for the repo's effective `[[kinds]]` set, so a repo that adds a kind (this one adds `DISC`) isn't contradicted by its own `--help`. Text-only change to the help page; the `unknown kind` error already listed the effective prefixes. `cli-help-id` e2e fixture updated.
- [§FS-id](../functional-spec/FS-id.md#fs-id-grund-proposes-ids-for-new-declarations): an unknown `<KIND>` is now an exit-`2` CLI-level error (an `error:` line naming the kind, plus a `known kinds: …` line), matching `grund list --kind <unknown>` — previously it exited `1`. The remaining query failures (empty slug, collision) keep exit `1` but drop the `error:` prefix, so an `error:`-prefixed stderr line reliably means an exit-`2` launch-time failure ([§FS-cli.4](../functional-spec/FS-cli.md#4-errors-with-no-source-location)). [§FS-id.6](../functional-spec/FS-id.md#6-exit-codes), [§FS-id.3](../functional-spec/FS-id.md#3-slug-derivation), and [§FS-id.5](../functional-spec/FS-id.md#5-collision-check) updated.
- [§FS-show.2.4](../functional-spec/FS-show.md#24-e2e-cases): the `grund show E2E-<name>` manifest is documented as it is emitted — the invocation line, an `expected exit: <code>` line, then a `fixtures:` line followed by one `- <path>` line per fixture file (sorted); `--head` prints the invocation line only. JSON shape `{"id","kind","path","args","expected_exit","fixtures"}` confirmed.
- README: add the `AGENTS.md` init-block check ([§FS-check.3.5](../functional-spec/FS-check.md#35-invalid-agent-entrypoint-init-block)) to the "What it Checks" list; add `RM` to the `[id]` kinds list; refresh the Subcommands table so it lists `show --section`/`--full`/`--format`, `fmt --check`/`--write`/`--cross-refs`, and the `[path]`/`--format` arguments other commands take; replace the `src/scanner.rs` / `{kind}-{number}-{slug}` worked examples in "Verifying what a file refers to" with ones that run against this repo's actual layout and slug-only `[id] format`.
- README / [§FS-cli](../functional-spec/FS-cli.md#fs-cli-grunds-command-line-surface-conventions) / [§FS-errors](../functional-spec/FS-errors.md#fs-errors-grund-emits-messages-in-fixed-shapes): align the launch-facing command catalog with the shipped surface (`config`, `completions`, and `agent-setup-instructions` included) and narrow the `--format` contract to the commands that currently expose JSON (`check`, `show`, `list`, `refs`, `cover`, `id`) instead of implying it is a universal global flag.
- Packaging: `Cargo.toml` `description` rewritten to the README's pitch ("the polyglot reference checker …") instead of the stale "league-of-code style docs" phrasing; `keywords` and `categories` added for the crates.io listing. The `examples/parallel-spec-review-example/` directory (an unrelated workflow demo, never referenced from `examples/README.md`) is removed so it no longer ships in the published crate.
- [§FS-init.2.3](../functional-spec/FS-init.md#23-generated-agent-entrypoints): the generated `AGENTS.md` v1 block now follows the repo's effective config — the ID-shape line, the `## References` ID-scheme line, the worked declaration heading, the `KIND ∈ {…}` set, and the marker / `$$`-trigger / strict-vs-bare-tokens notes are rendered from `.agents/grund.toml` (or the defaults `init` is about to write) instead of a fixed `<KIND>-<NNN>-<slug>` boilerplate. A `{kind}-{slug}` repo (like this one) now gets a `<KIND>-<slug>` description. Content change within v1; no schema bump (`grund check` still validates the block markers and version, not a byte-diff). `templates/AGENTS.md` carries the new placeholders.
- [§FS-init.2.3](../functional-spec/FS-init.md#23-generated-agent-entrypoints): `grund init` now re-renders any supported existing `AGENTS.md` managed block, including a same-version block, from the current template and effective `.agents/grund.toml`. Template/config changes therefore propagate on the next `grund init` without `--force`, while user-authored content outside the markers is still preserved.
- [§FS-init.2.3](../functional-spec/FS-init.md#23-generated-agent-entrypoints): the generated `AGENTS.md` v1 block now explicitly tells agents to run `grund show <ID>` or `grund show <ID>.<section>` before editing code that carries a corresponding spec citation. Content change within the existing v1 block; no schema bump.
- [§FS-init.2.1](../functional-spec/FS-init.md#21-files-written-updated-or-left-in-place) / [§FS-check.3.5](../functional-spec/FS-check.md#35-invalid-agent-entrypoint-init-block): `grund init` now also appends or updates the managed block in existing standalone companion agent entrypoints (`AGENTS.override.md`, `CLAUDE.md`, `GEMINI.md`, and `.github/copilot-instructions.md`), while skipping any companion that is a symlink to `AGENTS.md`; `grund check` validates discovered standalone companion entrypoints alongside `AGENTS.md`. Content change within the existing v1 block; no schema bump.
- `AGENTS.md` / `templates/AGENTS.md`: the `grund` project's own repository URL was `github.com/anthropics/grund` in the agents template; corrected to `github.com/vjovanov/grund` to match `Cargo.toml` `repository`/`homepage`. This repo's `AGENTS.md` is regenerated from the corrected template (it had also accumulated a duplicated copy of the v1 block above the block).
- CLI: restore the default `SIGPIPE` disposition at startup so a closed downstream pipe (`grund list | head`, `grund refs … | head`) ends the process quietly the way `ls | head` does, instead of panicking on the next `println!` with `failed printing to stdout: Broken pipe`. Unix only; a no-op elsewhere.
- [§FS-check.4.1](../functional-spec/FS-check.md#41-unused-declaration): state that the unused-declaration warning skips `E2E` declarations — an end-to-end case is exercised by being run, not by being cited, so a never-cited `§E2E-…` is not a warning. README "What it Checks" updated to match.
- README: add an **Install** section (`cargo install --git …` / `--path .`) — the prior "Try it" only built from source; the crates.io publish remains gated by [§RM-distribution-naming](../roadmap.md#rm-distribution-naming-verify-package-names-before-first-publish), with npm/PyPI still planned.
- README: correct the "Verifying what a file refers to" preamble — it claimed `grund`'s own source carries no inline `§` citations and that they "land when [§RM-core-cli-split](../roadmap.md#rm-core-cli-split-split-grund-core-from-grund-cli) breaks the engine into its own module"; in fact `src/lib.rs` already carries hundreds of `§…` doc-comment citations and this repo runs `[reference] require_grounding = true` ([§FS-check.3.6](../functional-spec/FS-check.md#36-ungrounded-source-file-opt-in)), so `grund cover src/` / `grund refs` / `grund show` work against the repo as-is. [§RM-core-cli-split](../roadmap.md#rm-core-cli-split-split-grund-core-from-grund-cli) now refers only to the still-pending per-component `# AR-…:` inline declarations. Also note in the project-layout list that `DISC` is a project-local `[[kinds]]` entry, not a canonical default.
- [§FS-list.1](../functional-spec/FS-list.md#1-inputs) / [§FS-list.3.1](../functional-spec/FS-list.md#31---format-text-default): `grund list --unused` now **excludes `E2E` cases by default** — the same suppression `check`'s unused-declaration warning already applies ([§FS-check.4.1](../functional-spec/FS-check.md#41-unused-declaration)), since an e2e case is exercised by being run, not cited, so it is uncited by construction and only buried the actionable signal (uncited specs, decisions, goals) under the whole case corpus. `grund list --unused --kind E2E` re-includes them for an inventory. CLI help (`grund list --help`) and a new e2e case (`list-unused-skips-e2e`) updated to match; `grund refs --help` spells out that text citation lines go to stdout, NDJSON for `--format json`.
- [§FS-config.3.5](../functional-spec/FS-config.md#35-scan--what-gets-walked) / [§GOAL-zero-config](../goals.md#goal-zero-config-works-on-any-conformant-tree): the zero-config "config root" is now the current working directory, never a subdirectory that merely got passed as `grund`'s path argument. Before this, `grund check src/` in a config-less repo set the root *to* `src/`, then re-applied the default `[scan] include` underneath it (`src/docs`, `src/e2e`, `src/src`), scanned nothing, and exited `0` — a silent false "all clear". Now `grund check <path>` scans exactly the file or directory it is handed (an explicit path overrides `include` rather than being filtered by it), while `grund` / `grund check .` still apply `include` against the repo. The `grund .` ≡ `grund` ≡ `grund check .` equivalence ([§FS-cli](../functional-spec/FS-cli.md#fs-cli-grunds-command-line-surface-conventions)) is preserved. `§FS-check.2`/`§FS-check.2.1` wording and `§FS-config.3.5` updated; e2e and unit coverage added.
- [§GOAL-fast-feedback](../goals.md#goal-fast-feedback-grund-must-be-as-fast-as-possible): relabel "Hard budgets — these must hold; CI fails on regression" as "Performance targets" with an honest status note — the targets are met by a wide margin in practice but are *not yet* a release-blocking measured contract; the criterion harness that records baselines and fails CI on regression is [§RM-benchmarks](../roadmap.md#rm-benchmarks-a-benchmark-harness-for-the-goal-fast-feedback-budgets). New [§AR-ci.4](../architecture/AR-ci.md#4-performance-smoke-guard) documents the interim guard (the built `grund .` self-check runs under a generous `timeout` in CI — `.github/workflows/ci.yml` updated). README "Distribution" and "Install" no longer present `cargo install grund` from crates.io as the live path: the crates.io publish is gated on [§RM-distribution-naming](../roadmap.md#rm-distribution-naming-verify-package-names-before-first-publish), and `cargo install --git …` / `--path .` are the install paths that work today.
- [§RM-distribution-naming](../roadmap.md#rm-distribution-naming-verify-package-names-before-first-publish): add `scripts/check-registry-names.sh` and the manual **Pre-release checks** workflow so package-name availability is rechecked against crates.io, npm, and PyPI immediately before publish.
- [§FS-distribution.4](../functional-spec/FS-distribution.md#4-release-process) / [§DA-pgo-release](../decisions/architectural/DA-pgo-release.md#da-pgo-release-distributed-binaries-are-pgo-built-trained-on-the-benchmark-workload): add the release workflow. A `vX.Y.Z` tag builds and self-checks PGO binaries on Linux, macOS Intel, macOS Apple Silicon, and Windows; uploads those binaries to the GitHub release; and, when crate publishing is enabled, publishes `grund-core` before `grund` after waiting for the crates.io index. The Linux GNU binary is built in a `manylinux2014_x86_64` container so it does not inherit a too-new `ubuntu-latest` glibc baseline. `scripts/pgo-build.sh` now resolves the Windows `.exe` binary name so the same PGO pipeline works on all four release runners.
- README / `grund check --help`: document the default scan scope (`docs/`, `e2e/`, `src/` with no config) and that an explicit `PATH` argument is scanned exactly, ignoring that default; drop the bare `(FS-config.1)` token from the `check` help page (help text is prose, not a checked citation).
- `examples/scheme-slug/README.md`: drop the `§` marker from the illustrative IDs in the Pros bullets so `grund check examples/scheme-slug` (and `grund check examples`) is clean — a marker-prefixed token is a checked citation everywhere it appears, including inside Markdown inline code, so an illustration of "what a slug ID looks like" must not wear the marker. The fenced "Example IDs" block already shows the un-prefixed form.
- `AGENTS.md` is the single canonical agent entrypoint; the repo's root `CLAUDE.md` is now a symlink to it. `grund check` treats a companion that is a symlink to `AGENTS.md` as already covered ([§FS-check.3.5](../functional-spec/FS-check.md#35-invalid-agent-entrypoint-init-block)), so this removes the drift surface without changing what `check` validates.
- [§FS-show.2.2](../functional-spec/FS-show.md#22-section) / [§FS-show.2.1](../functional-spec/FS-show.md#21-whole-declaration-default) / [§FS-show.3.1](../functional-spec/FS-show.md#31-format-variants): `grund show <ID>.<section>` now prints the selected section heading line (`### 3.1 …`) above its body in `text` and `json` output, so a pulled slice is self-labeled; `--head` on a section likewise keeps the section heading (and prints just that heading when the section opens directly with a sub-subsection). The whole-declaration `text` output still omits the opening heading line (only `md` includes it); the `json` `body` for a section now carries the heading. E2E cases `show-section-markdown`, `show-head-section-markdown`, and `show-json` updated.
- README: reframe around the agent loop — lead with the three-step workflow (cite as you write → re-read the cited spec before you edit → `grund check` for dangling pointers), then the structure that gets cited (with inline `AR-…` specs called out), then how the same `show` / `refs` / `cover` tools serve code review; install, the subcommand table, and the pre-commit hook move below the narrative, with the deeper walkthroughs in `docs/`. Adds the `[id] format` diagram and the scheme trade-off table.
- **[§FS-errors.1](../functional-spec/FS-errors.md#1-streams) — `grund` adopts the linter stdout/stderr convention.** A checker's findings *are* its output, so they go to **stdout** (the `eslint` / `ruff` / `shellcheck` / `golangci-lint` model), not stderr. Concretely:
  - [§FS-check.2.1](../functional-spec/FS-check.md#21-report-format): `grund check`'s findings (`<path>:<line>: <message>` text, and `--format=json` NDJSON) now print on **stdout** — `grund check | grep …`, `grund check > findings.txt`, and `grund check --format=json | jq` all work with no redirection. A clean text check prints `success`; clean JSON remains diagnostics-only and empty on stdout. The mid-walk `error: <path>: <reason>` (a file it couldn't read) and the empty-scan `warning:` ([§FS-check.2.2](../functional-spec/FS-check.md#22-empty-scan)) stay on stderr — they are about the run, not findings about the graph.
  - [§FS-refs.3.1](../functional-spec/FS-refs.md#31---format-text-default): `grund refs` text output moves to **stdout** too, matching its own `--format=json` (already on stdout) and `grund list` / `grund cover` / `grund show` — `grund refs <ID> | …` works without `2>&1`. The `note:` typo breadcrumb still goes to stderr.
  - [§FS-fmt.3](../functional-spec/FS-fmt.md#3-outputs): `grund fmt`'s report — the `--check` / dry-run `path:line: <kind>` lines *and* the `--write` `rewrote N reference(s):` summary — now prints on **stdout**, the same stream as `grund check`'s findings. `grund init`'s file-by-file transcript stays on stderr ([§FS-errors.6](../functional-spec/FS-errors.md#6-the-grund-init-transcript)) — that one carve-out is now documented as such, because `init`'s real output is the scaffold on disk.
  - The rule, project-wide: **stdout = the command's output** (a query result, or a checker/formatter report); **stderr = everything about the run** (`error:` launch/I-O failures, `warning:` cautions like an empty scan, `note:`/`hint:` recovery breadcrumbs, a failed `grund show` query's message, and `grund init`'s transcript). `grund check 2>/dev/null` now shows the findings and only the findings; `grund check >/dev/null` shows only run-level errors.
  - [§FS-errors.5](../functional-spec/FS-errors.md#5-json-format) rewritten to match, and two stale claims fixed along the way: `grund list` / `refs` / `cover --format=json` emit NDJSON on stdout (one record per row — the old "stdout is never NDJSON for results" line was contradicted by all three), and a *launch-time* CLI-level `error:` stays as text on stderr even under `--format=json` (only a *mid-walk* per-file failure, which is one of `check`'s report diagnostics, is JSON-ified). [§FS-cli.3](../functional-spec/FS-cli.md#3-cross-subcommand-flags) and [§FS-lsp.1.1](../functional-spec/FS-lsp.md#11-diagnostics) updated; the README ("Reviewing code"), `grund check --help`, `grund fmt --help`, and `grund refs --help` updated; ~35 e2e cases re-blessed (the located findings — and `fmt`'s report — moved stream).
- [§AR-checker](../../crates/grund-core/src/checker.rs): the architectural spec for the checker now lives **inline** in the doc-comment of `fn check` in `crates/grund-core/src/checker.rs`, with the one-line stub at `docs/architecture/AR-checker.md` — so `grund` exercises its own "an `AR-` spec can live in a class- or module-level doc-comment" path ([§AR-scanner.4](../architecture/AR-scanner.md#4-inline-declarations-in-language-doc-comments)) instead of only documenting it. `grund show AR-checker` / `grund show AR-checker.<n>` resolve through the stub; `grund list --kind AR` reports the source-file home; `grund check` validates the stub. The body picks up `### 2.6` (the `AGENTS.md`-block check, [§FS-check.3.5](../functional-spec/FS-check.md#35-invalid-agent-entrypoint-init-block)) and `### 2.7` (the opt-in grounding floor, [§FS-check.3.6](../functional-spec/FS-check.md#36-ungrounded-source-file-opt-in)) it was missing. Cross-references to `§AR-checker.<n>` from [§AR-scanner](../architecture/AR-scanner.md#ar-scanner-how-grund-discovers-declarations-and-citations), the architecture index, and this changelog now point at `crates/grund-core/src/checker.rs` (the source-homed link form, [§FS-fmt.6.2](../functional-spec/FS-fmt.md#62-form)); the README's "specs can live inline" section names `AR-checker` as the worked example.
- [§FS-init.2.2](../functional-spec/FS-init.md#22-stdout--stderr) / [§FS-init.2.3](../functional-spec/FS-init.md#23-generated-agent-entrypoints): re-running `grund init` on a repo whose managed `AGENTS.md` block already matches the current render now writes **nothing** and reports the file with `exists ` instead of `updated ` — `grund init` on an up-to-date repo is a true no-op on every file. `updated ` is now reserved for a block whose bytes actually changed (an older block upgraded, or a same-version block re-rendered against a changed template or config). New e2e case `init-agents-up-to-date` and a `tests/init.rs` re-run test cover it; §2.2 / §2.3 / §2.3.1 reworded.
- [§FS-refs.1](../functional-spec/FS-refs.md#1-inputs) / [§FS-refs.4](../functional-spec/FS-refs.md#4-exit-codes): `grund refs <ID>` now follows its `error: invalid ID \`<arg>\`` line (for an `<ID>` that does not match the repo's `[id] format`) with the same `hint: this repo's [id] format is \`<format>\` …` breadcrumb `grund show` already gives — the common stumble in a repo whose format differs from `{kind}-{slug}`. Exit code unchanged (`2` — `refs` has no exit-`1` query-failure class). New e2e case `refs-invalid-id-format`.
- [§FS-errors.5](../functional-spec/FS-errors.md#5-json-format) / [§FS-distribution.3.0](../functional-spec/FS-distribution.md#30-language-neutral-data-shapes): document the `sites` field on the diagnostic JSON shape (it was emitted but the spec listed only `{ severity, path, line, code, message }`) — `null` for a single-site finding, a `[{ path, line }]` list for a duplicate-declaration finding or an ambiguous-ID query failure — and extend the `code` enum with the values `grund show`'s failed queries emit (`not-found`, `invalid-id`, `ambiguous`, `query-failed`) plus `ungrounded` / `empty-scan`. FS-errors.5's cross-reference to the shape now points at [§FS-distribution.3.0](../functional-spec/FS-distribution.md#30-language-neutral-data-shapes) (it said §2, "CLI parity", which defines no shape).
- `.agents/grund.toml` (this repo): reorder the `[[kinds]]` tables to `DF` before `DA`, matching the canonical default order ([§FS-config.3.4](../functional-spec/FS-config.md#34-kinds--recognized-prefixes), `templates/grund.toml`, the README kind table) — the repo was the lone divergence. `AGENTS.md` regenerated via `grund init` so its kinds table and `KIND ∈ {…}` set follow.
- [§FS-init.2.3](../functional-spec/FS-init.md#23-generated-agent-entrypoints): the managed `AGENTS.md` block (`templates/AGENTS.md`, embedded in the binary) now names `grund refs <ID>` in the opening command list alongside `grund check` / `grund list` / `grund show`, and adds a "Rules for agents" line — *know what leans on a declaration before you change it: run `grund refs <ID>` before editing, renaming, or moving any declaration*. Content change within block `v1`; no schema bump. The regenerated `AGENTS.md` for this repo and the `init-agents-up-to-date` e2e fixture follow. (`[id] slug_pattern` stays `[a-z][a-z0-9-]*` here, not the default `[a-z0-9][a-z0-9-]*`, on purpose — a slug-only `[id] format` must forbid leading-digit slugs so `FS-001`-shaped tokens in prose are not mistaken for IDs; the `.agents/grund.toml` comment records why.)
- [§FS-show.3.2](../functional-spec/FS-show.md#32-cross-reference-links-are-flattened-in-text-and-json) / [§DF-show-cross-ref-flattening](../decisions/functional/DF-show-cross-ref-flattening.md#df-show-cross-ref-flattening-grund-show-flattens-cross-reference-link-wrappers): `grund show` now **flattens** `grund fmt --cross-refs` link wrappers back to the bare citation in `--format text` and the `--format json` `body` — `[§FS-check.1](FS-check.md#1-inputs)` prints as `§FS-check.1`. `--format md` keeps the wrapper verbatim (that is the renderable, self-contained fragment). The point of `grund show` is cheap grounding ([§GOAL-friendliness-first](../goals.md#goal-friendliness-first-as-user--and-agent-friendly-as-possible)); the wrapper is token-bloat and the relative path inside it is the wrong next step for the consumer (it should `grund show <ID>`, not open the file). Purely textual — no resolution, so a dangling wrapped citation is flattened the same and `grund check` still reports it; ordinary Markdown links and source-comment bodies are untouched. New `flatten_cross_ref_links` in `crates/grund-core/src/fmt_links.rs`; new e2e cases `show-cross-refs-flattened` (text) and `show-cross-refs-kept-in-md` (`md`). Builds on [§DF-md-link-emission](../decisions/functional/DF-md-link-emission.md#df-md-link-emission-grund-fmt-may-emit-clickable-markdown-links-alongside--prefixed-citations) (the wrapped form was always a presentation layer, never canonical).
- [§FS-init.2.3](../functional-spec/FS-init.md#23-generated-agent-entrypoints): the v1 managed `AGENTS.md` block ships in its final shape — an H2 marker (`## Grounding with grund (v1)`), a tighter body that teaches the cheap grounding ladder (`grund show <ID>` for bare IDs, `--toc` for navigation, `<ID>.<section>` for section citations, `--full` to escalate, `grund refs <ID> --summary` before back-reference dumps, `grund list --kind FS,AR` for scoped discovery), and a single citation form irrespective of `[fmt.cross_refs]`. No schema bump: v1 is the first published version.
- [§RM-positioning](../roadmap.md#rm-positioning-the-lychee-contrast-and-the-instruction-count-framing-in-readme-and-landing-copy) / [§GND-grund.1](../grund.md#1-what-grund-does-about-it): tighten README positioning around the "link checker vs. intent checker" contrast and add the instruction-count framing beside the benchmark badge.
- [§AR-benchmarks](../architecture/AR-benchmarks.md#ar-benchmarks-instruction-counting-benchmarks-for-the-hot-cli-commands): add `show --full` to the benchmark and PGO training workload now that bare `show` is the lead-default read, so both the cheap grounding path and the full recursive body path stay measured.
- [§FS-distribution.1](../functional-spec/FS-distribution.md#1-targets) / [§DA-pypi-uses-grund-as-the-package-name](../decisions/architectural/DA-pypi-uses-grund-as-the-package-name.md#da-pypi-uses-grund-as-the-package-name-pypi-uses-grund-as-the-package-name): final registry-name check found PyPI `grund` available, so the planned Python package is `grund` instead of the fallback `grund-cli`; npm remains `grund-cli` because bare npm `grund` is externally occupied.
- [§AR-bindings.1](../architecture/AR-bindings.md#1-target-workspace-layout) / [§FS-distribution.1](../functional-spec/FS-distribution.md#1-targets): split the shared implementation into a real `crates/grund-core` workspace crate and make the published `grund` CLI package depend on it. The root `src/main.rs` is now a thin binary entrypoint; the registry-name guard checks `crates.io/grund-core` before release alongside `grund` and `grund-lsp`.
- [§FS-distribution.4](../functional-spec/FS-distribution.md#4-release-process): the manual release workflow now creates the `vX.Y.Z` tag from the selected commit when needed and publishes `grund-core` / `grund` to crates.io by default, preserving the tag-push release path. The publish step is idempotent across partial reruns: it skips already-published crates but still waits up to 30 minutes for Cargo to resolve `grund-core` before publishing `grund`; the Windows PGO build passes native profile paths to `rustc` while keeping Bash paths for the shell.
- [§FS-distribution.4](../functional-spec/FS-distribution.md#4-release-process): harden the release workflow. The PGO matrix now covers six targets — `x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu` (via the new `manylinux2014_aarch64` leg on `ubuntu-24.04-arm`), `x86_64-apple-darwin`, `aarch64-apple-darwin`, `x86_64-pc-windows-msvc`, and `aarch64-pc-windows-msvc` (via `windows-11-arm`). The `manylinux2014` containers are pinned by SHA256 digest (tag `2026.05.13-1`) so a registry-side rebuild cannot shift the glibc baseline mid-release; both Linux jobs `chown` the bind-mounted `target/` back to the runner user after the container exits so subsequent steps run on non-root-owned files. The Rust toolchain is pinned via a single workflow env var (`RUST_TOOLCHAIN = "1.95.0"`) and threaded through every host-runner and in-container install so all six binaries are produced by the same compiler. `verify` runs a fail-fast preflight on the `CARGO_REGISTRY_TOKEN` secret when `publish_crates=true`, instead of discovering an unset secret behind the 20-minute matrix. `scripts/pgo-build.sh` now asserts that the training loop produced at least one `.profraw` file before merging — a silently-broken instrumented binary used to surface only as an `llvm-profdata` error.
- [§FS-distribution.4](../functional-spec/FS-distribution.md#4-release-process): split release version bumping from release publishing. `.github/workflows/release.yml` now remains the exact-version publisher; `.github/workflows/release-minor.yml` is the manual `vX.(Y+1).0` bump-and-dispatch path; `.github/workflows/auto-bump.yml` is the scheduled/manual `vX.Y.(Z+1)` patch bump-and-dispatch path, renamed from the weekly patch workflow.
- [§FS-distribution.1](../functional-spec/FS-distribution.md#1-targets): add a crates.io README for `grund-core` that points users to the installable `grund` CLI and names the future `grund-lsp` sibling package.

### 2.3 Added

- [§FS-init](../functional-spec/FS-init.md#fs-init-grund-bootstraps-a-new-grund-conformant-repo) / [§FS-config](../functional-spec/FS-config.md#fs-config-grund-reads-a-toml-config-file-under-agents) / [§FS-cli](../functional-spec/FS-cli.md#fs-cli-grunds-command-line-surface-conventions): add a distributable `skills/grund-init/SKILL.md` guide for interactive `grund` adoption and expose the same body through `grund agent-setup-instructions`. The skill analyzes the target repo first, identifies existing specs, artifact types, roadmaps, changelogs, decisions, plans, tests, and agent instruction files, recommends suitable config values with pros and cons for every option, includes language-specific scan examples, then writes `.agents/grund.toml`, runs `grund init`, and validates with `grund config validate` / `grund check`; the CLI command embeds that skill source so the two instruction surfaces cannot diverge.
- [§FS-init.5](../functional-spec/FS-init.md#5-agent-setup-instructions): clarify that existing docs-heavy repos should still run `grund init` after writing `.agents/grund.toml`, but should normally omit `--docs`; existing specs and project artifacts are represented through `[[kinds]]` and `[scan]` settings rather than replaced by generic scaffold folders.
- [§FS-check.3.5](../functional-spec/FS-check.md#35-invalid-agent-entrypoint-init-block): companion agent entrypoints are now validated only when canonical `AGENTS.md` exists. Config-only adoption can therefore leave existing companion files such as `CLAUDE.md` untouched while still using `.agents/grund.toml` for citation checking.
- [§FS-init.2.1](../functional-spec/FS-init.md#21-files-written-updated-or-left-in-place) / [§FS-check.3.5](../functional-spec/FS-check.md#35-invalid-agent-entrypoint-init-block): extend companion agent entrypoint discovery to existing `AGENTS.override.md` and `.claude/CLAUDE.md` files, so `grund init` appends or updates the managed block there and `grund check` validates them alongside the canonical `AGENTS.md` and `CLAUDE.md`.
- [§GOAL-polyglot-citation](../goals.md#goal-polyglot-citation-ids-cite-cleanly-from-anywhere-they-are-useful): new goal making explicit that one citation grammar resolves identically across `.md` and every supported source-comment form in [§AR-scanner.4](../architecture/AR-scanner.md#4-inline-declarations-in-language-doc-comments). Pairs with [§GOAL-no-dangling-refs](../goals.md#goal-no-dangling-refs-every-cited-id-resolves-to-a-declaration) (correctness × coverage).
- [§GOAL-polyglot-citation](../goals.md#goal-polyglot-citation-ids-cite-cleanly-from-anywhere-they-are-useful) / [§AR-scanner.4](../architecture/AR-scanner.md#4-inline-declarations-in-language-doc-comments): extend the doc-comment e2e suite with a broad positive fixture and an all-host negative fixture for the documented Java/Kotlin/Scala, C/C++, C#, Rust, Go, TypeScript/JavaScript, Swift, PHP, Ruby, and Python hosts.
- [§FS-completions.1](../functional-spec/FS-completions.md#1-user-facing-command) / [§FS-completions.2](../functional-spec/FS-completions.md#2-internal-dynamic-helper): add zsh and fish completion golden e2e cases beside the existing bash golden.
- [§FS-fmt.6](../functional-spec/FS-fmt.md#6-cross-reference-emission): new section spec'ing an opt-in `--cross-refs` mode that wraps marker-prefixed citations in clickable Markdown links inside `.md` files. Source files are never touched.
- [§DF-md-link-emission](../decisions/functional/DF-md-link-emission.md#df-md-link-emission-grund-fmt-may-emit-clickable-markdown-links-alongside--prefixed-citations): decision record for the wrap-the-citation form and the reconciliation with [§FS-non-goals.1](../functional-spec/FS-non-goals.md#1-markdown-link-validation) (link validation stays out of scope) and [§FS-non-goals.5](../functional-spec/FS-non-goals.md#5-documentation-generation) (no rendered docs). The §2.2 "Anchor format" section is superseded by [§DF-md-link-anchor-strategy](../decisions/functional/DF-md-link-anchor-strategy.md#df-md-link-anchor-strategy-heading-text-slugs-re-derived-on-every-fmt-pass) below.
- [§DF-md-link-anchor-strategy](../decisions/functional/DF-md-link-anchor-strategy.md#df-md-link-anchor-strategy-heading-text-slugs-re-derived-on-every-fmt-pass): decision record picking heading-text slugs (per a configurable renderer profile) re-derived on every `grund fmt --cross-refs` pass. Retracts the placeholder section-coordinate anchor format from [§DF-md-link-emission](../decisions/functional/DF-md-link-emission.md#df-md-link-emission-grund-fmt-may-emit-clickable-markdown-links-alongside--prefixed-citations)'s first draft, which proved factually wrong about renderer behavior on review. Updates [§FS-fmt.6.2](../functional-spec/FS-fmt.md#62-form) (anchor bullet), [§FS-fmt.6.3](../functional-spec/FS-fmt.md#63-idempotency-and-re-derive) (re-derive supersedes "leave URLs alone"), and [§FS-fmt.6.7](../functional-spec/FS-fmt.md#67-configurability) (named profiles `github`/`gitlab`/`mkdocs`/`pandoc`/`none`).
- [§RM-md-link-emission](../roadmap.md#rm-md-link-emission-grund-fmt---cross-refs): roadmap item that owns the implementation. Sequenced after [§RM-marker-fmt](../roadmap.md#rm-marker-fmt-the--marker-the--trigger-and-grund-fmt) (marker + fmt) and before [§RM-distribution](../roadmap.md#rm-distribution-cargo--npm--pypi-from-one-engine) (bindings) so the link form stabilizes once across all three registries.
- [§FS-lsp](../functional-spec/FS-lsp.md#fs-lsp-grund-will-ship-an-optional-lsp-server): new functional spec for the optional LSP server (`grund-lsp`). Covers the four v1 capabilities (diagnostics, hover, go-to-definition, live `$$` → `§` trigger via `textDocument/onTypeFormatting`), opt-in install, and the policy that no first-party per-editor wrappers ship.
- [§AR-lsp](../architecture/AR-lsp.md#ar-lsp-how-the-lsp-server-is-built): new architectural spec describing the server's relationship to `grund-core`, in-memory `Findings` cache, full-rescan strategy for v1 with incremental as a follow-on, and stdio-only transport.
- [§DA-lsp-optional](../decisions/architectural/DA-lsp-optional.md#da-lsp-optional-lsp-server-ships-as-a-separate-optional-binary): new architectural decision pinning the LSP as a separate published package on every registry — not a bundled subcommand, not a Cargo feature, not a second binary in the CLI crate. Records the dependency-cost, CI-binary-size, and industry-parallel reasoning.
- [§FS-non-goals.12.2](../functional-spec/FS-non-goals.md#122-first-party-per-editor-plugins): new non-goal entry — first-party per-editor plugins (VSCode, IntelliJ, Vim, Emacs wrappers) are out of scope. Reorganizes §12 into a parent header about "surfaces outside `grund-core` and the LSP transport"; the engine-plugins non-goal becomes §12.1, the editor-wrappers non-goal §12.2. The §13 bright line is unchanged.
- [§FS-refs](../functional-spec/FS-refs.md#fs-refs-grund-lists-every-citation-of-an-id): new functional spec for `grund refs <ID>` — the reverse of `grund show`: list every citation site of an ID (located-finding shape; NDJSON for tooling), sharing the scanner with `grund check` so the two never disagree on what counts as a citation. Closes the agent-grounding loop (read the body / know the blast radius) without telling agents to `grep`.
- [§FS-cli](../functional-spec/FS-cli.md#fs-cli-grunds-command-line-surface-conventions): new cross-cutting spec pinning the command-line surface — `grund` / `grund <path>` default to `grund check`; `grund --version` (alias `-V`) and `grund --help` (alias `-h`) are handled before any scan; `--format` is the cross-subcommand flag; unknown subcommand / bad flag → `error:` + exit 2; the exit-code mapping is frozen; `grund graph` / `grund new` are explicitly absent.
- [§FS-check.6](../functional-spec/FS-check.md#6-watch-mode---watch): new section spec'ing `grund check --watch` — stay resident, re-check on every change under the scanned tree, clearing prior output each run; filesystem-notification-driven and debounced (no polling, no interval knob); each run byte-identical to a plain `grund check`; non-interactive, no network. The editor-less counterpart to [§FS-lsp](../functional-spec/FS-lsp.md#fs-lsp-grund-will-ship-an-optional-lsp-server).
- [§AR-scanner.6](../architecture/AR-scanner.md#6-e2e-case-declarations): new section spec'ing `E2E` declaration discovery — the one kind discovered by directory structure (a case directory under `e2e/cases/` containing `expected.exit`), not a heading line; the directory name is the ID with the `{kind}` portion stripped; the recorded "body" is the case manifest (invocation, expected exit, fixture list) that [§FS-show.2.4](../functional-spec/FS-show.md#24-e2e-cases) prints. `§E2E-<name>` citations resolve like any other.
- [§FS-show.2.4](../functional-spec/FS-show.md#24-e2e-cases): `grund show E2E-<name>` returns the case manifest; `--head` prints the invocation line; section paths are not defined for E2E cases.
- [§RM-refs](../roadmap.md#rm-refs-grund-refs-): roadmap item owning `grund refs` implementation (cheap — citations are already in `Findings`).
- [§RM-watch](../roadmap.md#rm-watch-implement-grund-check---watch): roadmap item owning `grund check --watch`; sequenced after [§RM-core-cli-split](../roadmap.md#rm-core-cli-split-split-grund-core-from-grund-cli) so the watcher calls `grund-core` rather than re-implementing the walk.
- [§DF-require-grounding](../decisions/functional/DF-require-grounding.md#df-require-grounding-an-opt-in-check-that-every-source-file-cites-a-spec): new functional decision record on the grounding floor — Tier 1 (the opt-in source-file citation requirement, [§FS-check.3.6](../functional-spec/FS-check.md#36-ungrounded-source-file-opt-in)) ships now; Tier 2 (the diff-aware co-change gate) stays a recipe under [§RM-cochange-gate](../roadmap.md#rm-cochange-gate-a-pre-commit--ci-recipe--no-impl-change-without-spec-and-test), not engine behaviour.
- [§FS-check.3.6](../functional-spec/FS-check.md#36-ungrounded-source-file-opt-in): `[reference] require_grounding` (off by default) and `grund check --require-grounding` — when on, `check` reports an `ungrounded source file` error for every scanned non-Markdown file that carries no resolving citation and declares no ID inline. Shipped under [§RM-require-grounding](../roadmap.md#rm-require-grounding-the-opt-in-grounding-floor); the grounding floor of [§DF-require-grounding](../decisions/functional/DF-require-grounding.md#df-require-grounding-an-opt-in-check-that-every-source-file-cites-a-spec); the diff-aware co-change recipe remains under [§RM-cochange-gate](../roadmap.md#rm-cochange-gate-a-pre-commit--ci-recipe--no-impl-change-without-spec-and-test). `grund config show` now prints `require_grounding`; `templates/grund.toml` carries `require_grounding = false`. Content change within `grund_config_version = 1` — a v1 config without the key is unaffected.
- [§FS-config.3.1](../functional-spec/FS-config.md#31-reference--citation-form): document the `require_grounding` key.
- [§FS-cover](../functional-spec/FS-cover.md#fs-cover-grund-groups-citations-by-scanned-file): new functional spec — `grund cover [path] [--format text|json]`, the file-grouped citation graph for co-change recipes. It uses the same scanner data as `check` / `refs`, emits one JSON record per scanned file, includes files with zero citations, and keeps git diff policy outside the engine. Shipped under [§RM-cover](../roadmap.md#rm-cover-grund-cover); [§FS-cli](../functional-spec/FS-cli.md#fs-cli-grunds-command-line-surface-conventions), [§FS-completions](../functional-spec/FS-completions.md#fs-completions-grund-completes-declared-ids-in-shells), README, and the e2e corpus now include the command.
- [§RM-require-grounding](../roadmap.md#rm-require-grounding-the-opt-in-grounding-floor) / [§RM-cover](../roadmap.md#rm-cover-grund-cover): roadmap items, created already-shipped; their detail lives in the §2.7 Implemented block below, with one-line declarations under `docs/roadmap.md` §"Shipped milestones" so `§RM-…` citations resolve.
- [§FS-check.2.2](../functional-spec/FS-check.md#22-empty-scan): `grund check` now emits one CLI-level `warning:` line on stderr when a walk reads **no scannable files** and turns up nothing to report — naming `[scan] include` and `grund init` when the scope is the repo root, or the explicit path and the recognized extensions otherwise — instead of printing nothing and exiting `0`. A misconfigured scope now fails loud. Exit code unchanged (`0`); `--format=json` still emits its empty report on stdout; a repo with a stale `AGENTS.md` block ([§FS-check.3.5](../functional-spec/FS-check.md#35-invalid-agent-entrypoint-init-block)) or any other finding gets that finding and no empty-scan notice. New [§AR-ci.4](../architecture/AR-ci.md#4-performance-smoke-guard) and the [§GOAL-zero-config](../goals.md#goal-zero-config-works-on-any-conformant-tree) measurable reference it.
- [§FS-refs.2](../functional-spec/FS-refs.md#2-behaviour): `grund refs <ID>` for an ID that is *neither declared nor cited* now prints a `note:` breadcrumb on stderr (`note: <ID> is neither declared nor cited — run \`grund list\` …`), mirroring `grund show`'s `ID not found` hint, while still exiting `0` — the empty stdout (NDJSON) / stderr (text) result is unchanged, so machine consumers are unaffected.
- [§AR-ci.4](../architecture/AR-ci.md#4-performance-smoke-guard): new architecture section — the interim performance smoke guard (the built `grund .` self-check under a generous CI `timeout`) that backstops [§GOAL-fast-feedback](../goals.md#goal-fast-feedback-grund-must-be-as-fast-as-possible) until the [§RM-benchmarks](../roadmap.md#rm-benchmarks-a-benchmark-harness-for-the-goal-fast-feedback-budgets) criterion harness lands.
- [§DF-id-number-width](../decisions/functional/DF-id-number-width.md#df-id-number-width-grund-id-zero-pads-minted-numbers-to-a-default-width-of-3): new functional decision record — why `grund id`'s `--width` defaults to `3` (it is the documented `-NNN-` canonical shape, the smallest width that keeps IDs numerically sortable for the first 999 of a kind, and a floor not a cap), what happens at the rollover past 999 (permanently mixed-width, cosmetic only, no re-pad because IDs are immutable — [§FS-non-goals.4](../functional-spec/FS-non-goals.md#4-cross-workspace-id-renaming)), and why the width is a per-invocation flag rather than an `[id]` config key today (with `[id] number_width` left as the additive, no-version-bump door for a repo that ever needs it). Cited from [§FS-id.1](../functional-spec/FS-id.md#1-inputs).
- [§RM-declaration-near-miss](../roadmap.md#rm-declaration-near-miss-warn-on-a-heading-that-looks-like-a-declaration-but-does-not-match-id-format): new roadmap item — a `grund check` *warning* (exit code unchanged) for a heading shaped like `# <KIND>-…: <title>` whose ID does not match the configured `[id] format` (the `# FS-login:` -under-numbered-format stumble), so a near-miss heading that is silently *not* a declaration today gets surfaced. Non-heuristic: it points out the mismatch, never guesses the corrected ID. Cited from [§FS-check.5](../functional-spec/FS-check.md#5-what-grund-does-not-check) and the roadmap intro.
- [§AR-benchmarks](../architecture/AR-benchmarks.md#ar-benchmarks-instruction-counting-benchmarks-for-the-hot-cli-commands): new architectural spec plus the harness it pins — `benches/instructions.rs` (a `cargo bench` target whose real body is gated behind a `bench` Cargo feature, with `iai-callgrind` as a `[dev-dependency]`) runs the built `grund` binary under Callgrind for the commands agents and CI run most — `check`, `list`, `show`, `refs`, `cover`, `fmt --check` — over this repo, reporting a deterministic instruction count per invocation. A new `bench` CI job ([§AR-ci.5](../architecture/AR-ci.md#5-benchmark-job)) installs Valgrind and the version-matched `iai-callgrind-runner` and runs `cargo bench --features bench` on every push; the regular build/test matrix is unaffected (without the feature it compiles only a no-op bench target, so it never needs Valgrind). The [§GOAL-fast-feedback.1](../goals.md#1-performance-targets) budgets and [§GOAL-fast-feedback.3](../goals.md#3-measurable) "track the number / fail on regression" measurable, and the [§AR-ci.4](../architecture/AR-ci.md#4-performance-smoke-guard) smoke-guard section, are updated to reference the harness; the 10k-file synthetic input, the committed 0.1.0 baseline, and the build-failing regression threshold remain under [§RM-benchmarks](../roadmap.md#rm-benchmarks-a-benchmark-harness-for-the-goal-fast-feedback-budgets).
- [§DA-benchmark-instruction-counting](../decisions/architectural/DA-benchmark-instruction-counting.md#da-benchmark-instruction-counting-the-performance-harness-counts-instructions-not-wall-clock-seconds): new architectural decision record — the [§GOAL-fast-feedback](../goals.md#goal-fast-feedback-grund-must-be-as-fast-as-possible) performance harness counts CPU instructions (Callgrind via `iai-callgrind`), not wall-clock seconds, and benchmarks the `grund` binary rather than the library. Records why determinism is what makes "fail CI on regression" implementable on shared runners, why the millisecond budget stays covered twice (the [§AR-ci.4](../architecture/AR-ci.md#4-performance-smoke-guard) timeout as the catastrophic-blowup floor, the recorded baseline as the headline figure), why the binary is the right unit (start-up / arg-parse / formatting are part of the cost, and 0.1.0 has no stable library surface yet — [§RM-core-cli-split](../roadmap.md#rm-core-cli-split-split-grund-core-from-grund-cli)), and why not "both" frameworks for now. Supersedes the "criterion" wording in [§RM-benchmarks](../roadmap.md#rm-benchmarks-a-benchmark-harness-for-the-goal-fast-feedback-budgets)'s original "What"; cited from [§AR-benchmarks](../architecture/AR-benchmarks.md#ar-benchmarks-instruction-counting-benchmarks-for-the-hot-cli-commands).
- [§DA-pgo-release](../decisions/architectural/DA-pgo-release.md#da-pgo-release-distributed-binaries-are-pgo-built-trained-on-the-benchmark-workload) / [§FS-distribution.4](../functional-spec/FS-distribution.md#4-release-process): new architectural decision record plus the mechanism — `scripts/pgo-build.sh` builds the release `grund` binary with profile-guided optimization, training the profile on the [§AR-benchmarks](../architecture/AR-benchmarks.md#ar-benchmarks-instruction-counting-benchmarks-for-the-hot-cli-commands) workload (`check` / `list` / `show` / `refs` / `cover` / `fmt --check`) against `grund`'s own conformant tree, so the thing we benchmark is the thing the optimizer profiles against. The release pipeline ([§RM-distribution](../roadmap.md#rm-distribution-cargo--npm--pypi-from-one-engine)) runs it for the distributed binaries — wired for the crates.io `grund` binary today, extended to the prebuilt npm/PyPI binaries as those land; the manual **Pre-release checks** workflow also runs it and self-checks the PGO binary before publish. Explicit benchmark work may also run it when comparing the optimized release artifact. `cargo install grund` from source stays LTO-only and byte-for-byte behaviour-identical, and normal development CI does not run PGO ([§AR-ci.6](../architecture/AR-ci.md#6-pgo-stays-out-of-development-ci)). `[profile.release]` in `Cargo.toml` carries a pointer comment; the decision record records why a script not a `Cargo.toml` profile, why hand-rolled not `cargo-pgo`, and why the benchmark workload is the right training corpus.
- [§FS-show](../functional-spec/FS-show.md#fs-show-grund-reads-a-single-declaration-body-by-id) / [§FS-refs](../functional-spec/FS-refs.md#fs-refs-grund-lists-every-citation-of-an-id) / [§FS-list](../functional-spec/FS-list.md#fs-list-grund-lists-every-declared-id): add token-cheap read surfaces for agents — the lead-default `grund show <ID>` read, `grund show --brief`, `grund show --toc`, `grund refs --summary`, multi-kind `grund list --kind FS,AR`, and `grund list --summary`. Outputs are structural slices, not generated summaries, per [§GOAL-token-economy](../goals.md#goal-token-economy-give-an-agent-the-right-amount-of-spec-not-the-whole-file) and [§DF-show-default-token-cheap](../decisions/functional/DF-show-default-token-cheap.md#df-show-default-token-cheap-grund-show-defaults-to-the-cheap-read-the-full-body-is-opt-in). E2E coverage added for text and JSON shapes where applicable.

### 2.4 Removed

- `FS-ide-plugins`: the multi-editor first-party-plugins spec is deleted; replaced by [§FS-lsp](../functional-spec/FS-lsp.md#fs-lsp-grund-will-ship-an-optional-lsp-server) and [§FS-non-goals.12.2](../functional-spec/FS-non-goals.md#122-first-party-per-editor-plugins). References in [§FS-errors](../functional-spec/FS-errors.md#fs-errors-grund-emits-messages-in-fixed-shapes), [§FS-id](../functional-spec/FS-id.md#fs-id-grund-proposes-ids-for-new-declarations), [§FS-fmt](../functional-spec/FS-fmt.md#fs-fmt-grund-normalizes-references-in-bulk), [§FS-show](../functional-spec/FS-show.md#fs-show-grund-reads-a-single-declaration-body-by-id), [§AR-checker](../../crates/grund-core/src/checker.rs), [§DF-reference-marker](../decisions/functional/DF-reference-marker.md#df-reference-marker-use--as-the-reference-marker-with--as-the-typing-trigger), and the roadmap have been swept to point at [§FS-lsp](../functional-spec/FS-lsp.md#fs-lsp-grund-will-ship-an-optional-lsp-server) instead.

### 2.5 Renamed

- **Canonical kind set: new top-of-chain `GND` kind; `G` (Goal) → `GOAL`** ([§FS-config.3.4](../functional-spec/FS-config.md#34-kinds--recognized-prefixes), [§AR-scanner.4](../architecture/AR-scanner.md#4-inline-declarations-in-language-doc-comments)). The pre-release "raison d'être" doc is now the project's **grund** — the single declaration every citation ultimately climbs toward — declared as `# GND-<slug>: …` (here `§GND-grund`) at the H1 of `docs/grund.md` (renamed from `docs/raison-detre.md`); `GND` (`file = "docs/grund.md"`, title `Reason for existence`) is the first of the canonical kinds. Because grund rejects any config where one prefix is a string-prefix of another, the `GND` prefix collides with the one-letter `G` Goal kind, so `G` was renamed to `GOAL` in the same pass: the `[[kinds]]` prefix, all ten `## GOAL-…` headings in `docs/goals.md`, every `§GOAL-…` citation and `goals.md#goal-…` anchor across the docs, code comments, the spec, the `agent-setup-instructions` skill, the README kind table, `templates/grund.toml`, this repo's `.agents/grund.toml`, and the regenerated `AGENTS.md` managed block. Every goal's *slug* is unchanged (`§G-fast-feedback` → `§GOAL-fast-feedback`, etc.) — only the kind prefix moved. This is an *additive* grammar change for the `GND` part (a default-config repo with a `§GND-…` token in prose now gets it checked) and a pre-publish rename for the `G` → `GOAL` part (no released config ever used `G`); `grund_config_version` and the `AGENTS.md` block version stay at 1 / v1. `templates/grund.md` (the `--docs` scaffold's reason-for-being stub) and `templates/grund.toml` (the embedded `grund init` config) gain the `GND` kind; [§FS-init.2.1](../functional-spec/FS-init.md#21-files-written-updated-or-left-in-place)'s scaffold list now names `docs/grund.md`.
- **The tool itself: `gnd` → `grund`** ([§DA-rename-to-grund](../decisions/architectural/DA-rename-to-grund.md#da-rename-to-grund-rename-gnd-to-grund-before-first-publish)). `gnd` was the working title carried through pre-release development; the published name is `grund` (German for *reason / ground / basis* — the concept the reference scheme implements). The cargo crate (`grund`, library `grund-core`, `grund-lsp`), the binary, the GitHub repository, the npm/PyPI packages (`grund-cli`, `grund-lsp`), the config file (`.agents/gnd.toml` → `.agents/grund.toml`), the config-version key (`gnd_config_version` → `grund_config_version`), the `agent-setup-instructions` skill (`skills/gnd-init/` → `skills/grund-init/`), the `AGENTS.md` managed block, the e2e corpus, and every doc and code reference were renamed in one pass. The `§` reference marker, the `$$` typing trigger, the `<KIND>-<slug>` ID grammar, and every declared ID are unchanged. The `AGENTS.md` block version and `grund_config_version` value stay at v1 / 1 — the config-key *name* changed (a fresh-clone consumer must use `.agents/grund.toml` with `grund_config_version`), but no released `gnd` ever shipped, so there is no migration to perform. [§DA-reference-checker-name](../decisions/architectural/DA-reference-checker-name.md#da-reference-checker-name-name-for-the-spec-reference-checker-tool) and [§DA-pypi-package-name](../decisions/architectural/DA-pypi-package-name.md#da-pypi-package-name-pypi-uses-gnd-cli-as-the-package-name) are kept as historical records and marked superseded.
- `grund name` → `grund id`; the spec `FS-name` → `§FS-id`. The subcommand that emits the next conflict-free ID for a new declaration is now invoked as `grund id <KIND> "<title>"` — the verb names what it returns (an ID), matching the `ID=$(grund id …)` idiom [§FS-init.2.2](../functional-spec/FS-init.md#22-stdout--stderr) and the `AGENTS.md` scaffold already teach. No deprecation alias: `grund name` is now an unknown subcommand — the `known commands:` list ([§FS-cli.4](../functional-spec/FS-cli.md#4-errors-with-no-source-location)), the `grund help` pages ([§FS-cli.3](../functional-spec/FS-cli.md#3-cross-subcommand-flags)), the `--format`-accepting set ([§FS-errors.5](../functional-spec/FS-errors.md#5-json-format)), and the bash/zsh/fish completion scripts all list `id`. The flag surface (`--width`, `--explain`, `--format text|json`), the slug/number derivation, and the `0`/`1`/`2` exit mapping are unchanged.
- `RM-ide-plugins` → `§RM-lsp`. Scope shrinks: ship the `grund-lsp` crate and per-registry packages; document one-time editor setup snippets in the README; do not maintain per-editor wrappers.
- `grund fmt --md-links` → `grund fmt --cross-refs`; the config block `[fmt.md_links]` → `[fmt.cross_refs]` (keys `enabled` / `anchor_format` unchanged). A "cross-reference" is whatever construct the surrounding markup uses to point elsewhere — a Markdown inline link, an AsciiDoc `xref:`, a reStructuredText `:ref:` — so the flag is named for the concept, not for `md` + `links`. **Behaviour is unchanged: `--cross-refs` still emits exactly the Markdown inline-link form, and only in `.md` files** ([§FS-fmt.6](../functional-spec/FS-fmt.md#6-cross-reference-emission)); language-specific cross-references stay out of scope for now (getting each renderer's anchor algorithm right is real work — [§DF-github-anchor-fidelity](../decisions/functional/DF-github-anchor-fidelity.md#df-github-anchor-fidelity-the-github-anchor-profile-reproduces-github-slugger-exactly)). The general name and the `[fmt.cross_refs]` block ([§FS-config.3.7](../functional-spec/FS-config.md#37-fmtcross_refs--cross-reference-emission)) leave room to add a second markup family later under the same flag and config — additive, no `grund_config_version` bump. The decision and roadmap IDs that pre-date the rename keep their slugs (`§DF-md-link-emission`, `§DF-md-link-anchor-strategy`, `§DF-declaration-anchor`, `§RM-md-link-emission`) — IDs are stable handles ([§FS-non-goals](../functional-spec/FS-non-goals.md#fs-non-goals-what-grund-will-deliberately-not-do)); only their flag-name prose changed. `grund config show`, `grund fmt --help`, `templates/grund.toml`, this repo's `.agents/grund.toml`, `skills/grund-init/SKILL.md`, the README, and the e2e cases (`fmt-md-links-*` → `fmt-cross-refs-*`) updated.
- Roadmap milestone IDs reslugged to fit this repo's slug-only `[id] format` now that `RM` is a checked kind: `RM-001-e2e-corpus` → `§RM-e2e-corpus`, `RM-002-show` → `§RM-show`, `RM-003-config` → `§RM-config`, `RM-004-marker-fmt` → `§RM-marker-fmt`, `RM-005-distribution` → `§RM-distribution`, `RM-006-lsp` → `§RM-lsp`, `RM-007-self-host` → `§RM-self-host`, `RM-008-core-cli-split` → `§RM-core-cli-split`, `RM-009-distribution-naming` → `§RM-distribution-naming`, `RM-010-md-link-emission` → `§RM-md-link-emission`, `RM-011-refs` → `§RM-refs`, `RM-012-watch` → `§RM-watch`. The shipped milestones (`§RM-e2e-corpus`, `§RM-show`, `§RM-config`, `§RM-marker-fmt`, `§RM-md-link-emission`, `§RM-refs`) keep a one-line declaration under `docs/roadmap.md` §"Shipped milestones" so their citations resolve; the detail stays in §2.7 below.
- **Architectural-spec kind: `AS` → `AR`, home `docs/architectural-spec/` → `docs/architecture/`** ([§FS-config.3.4](../functional-spec/FS-config.md#34-kinds--recognized-prefixes), [§FS-init.2.1](../functional-spec/FS-init.md#21-files-written-updated-or-left-in-place)). The `AR` (formerly `AS`) kind's default `folder` is now `docs/architecture` — kind and home now read as the same word, the way `FS` ↔ `docs/functional-spec/` does. The kind *title* (`Architectural spec`) is unchanged; every declaration keeps its slug (`§AS-scanner` → `§AR-scanner`, `§AS-checker` → `§AR-checker`, etc. — only the kind prefix moved). Renamed across the board: the `[[kinds]]` `prefix`, `DEFAULT_KINDS` / `default_kind_folder` / `default_kind_title` and the `tests/e2e.rs` canonical-kinds list, all six `docs/architecture/AR-*.md` files and their H1 declarations, the inline `# AR-checker:` declaration in `src/lib.rs`'s `fn check` doc-comment and its stub, every `§AR-…` citation and `architecture/AR-…#…` anchor across the docs, the README kind table and `G│FS│AR│DA│…` diagram, `grund list --kind AR` / `grund id` help text, `templates/grund.toml`, `templates/architecture-README.md`, this repo's `.agents/grund.toml`, the regenerated `AGENTS.md` managed block, `skills/grund-init/SKILL.md`, `tests/init.rs`, `Cargo.toml`, the benchmark harness and `scripts/`, `.github/workflows/ci.yml`, and the e2e corpus (every `*/repo/docs/architecture/AR-*.md` fixture, the polyglot source files' `§AR-…` citations, the `command.args` / `spec.refs` files, and the `init`/`list`/`stub`/`cli-help-*` expected outputs). Pre-publish change; `grund_config_version` and the `AGENTS.md` block version stay at 1 / v1, and no released config ever used `AS` or the old path.

### 2.6 Distribution and bindings

- [§FS-distribution.1](../functional-spec/FS-distribution.md#1-targets): adds `grund-lsp` as a separate package on cargo, npm, and PyPI. The CLI install on each registry does not transitively pull in `grund-lsp`. [§FS-distribution.4](../functional-spec/FS-distribution.md#4-release-process) release process publishes the LSP alongside the CLI in lockstep.
- [§AR-bindings.1](../architecture/AR-bindings.md#1-target-workspace-layout): workspace gains a `crates/grund-lsp/` member; sections renumbered (§3 CLI, §4 LSP, §5 Node, §6 Python, §7 Why this shape) with no external citations affected.

### 2.7 Implemented

The CLI now covers the full subcommand surface the specs describe. The remaining work — the `grund-core`/`grund-cli` workspace split, the npm/PyPI bindings, the optional `grund-lsp` server, `grund check --watch`, and the diff-aware co-change gate ([§RM-cochange-gate](../roadmap.md#rm-cochange-gate-a-pre-commit--ci-recipe--no-impl-change-without-spec-and-test)) — is tracked in `docs/roadmap.md`.

- [§RM-self-host](../roadmap.md#rm-self-host-guard-the-self-host-loop-in-ci): the self-host loop passes — `cargo run -- .` on this repo exits `0` with the clean text `success` marker — and CI now enforces it on every push and pull request. The scanner skips fenced Markdown blocks ([§FS-fmt](../functional-spec/FS-fmt.md#fs-fmt-grund-normalizes-references-in-bulk)'s existing exemption applied to the scanner), so illustrative IDs in fenced examples are inert. Remaining under [§RM-self-host](../roadmap.md#rm-self-host-guard-the-self-host-loop-in-ci): a default-config nested-fixtures e2e fixture.
- [§RM-e2e-corpus](../roadmap.md#rm-e2e-corpus-the-e2ecases-corpus-and-ci-harness): the `e2e/cases/*` corpus plus `tests/e2e.rs` and `tests/init.rs` run on every push (`.github/workflows/ci.yml`), with positive and negative fixtures per [§FS-check](../functional-spec/FS-check.md#fs-check-grund-validates-every-reference-in-a-repo) error class and a byte-for-byte determinism sweep ([§FS-non-goals.13](../functional-spec/FS-non-goals.md#13-anything-that-would-let-two-grund-installs-disagree)).
- [§RM-show](../roadmap.md#rm-show-id-query-reads): `grund show <ID>` per [§FS-show](../functional-spec/FS-show.md#fs-show-grund-reads-a-single-declaration-body-by-id) — whole declaration, `--head`, `--section` and dotted-inline section paths, `--full`, `--format text|md|json`, inline-source extraction with comment-marker stripping ([§FS-show.2.3](../functional-spec/FS-show.md#23-inline-declarations-in-code-and-doc-comments)), E2E case manifests ([§FS-show.2.4](../functional-spec/FS-show.md#24-e2e-cases)), and the ambiguous-ID / broken-stub query-result forms.
- [§RM-config](../roadmap.md#rm-config-agentsgrundtoml-discovery-parsing-and-inspection): `.agents/grund.toml` discovery and parsing per [§FS-config](../functional-spec/FS-config.md#fs-config-grund-reads-a-toml-config-file-under-agents) — the line-oriented TOML subset, unknown-key errors with `path:line:` pointers, `grund_config_version` gating, `[reference]` / `[id]` / `[[kinds]]` / `[scan]` / `[output]` / `[fmt.cross_refs]`, plus `grund config validate` and `grund config show`.
- [§RM-marker-fmt](../roadmap.md#rm-marker-fmt-the--marker-the--trigger-and-grund-fmt): the `§` marker and `$$` trigger per [§DF-reference-marker](../decisions/functional/DF-reference-marker.md#df-reference-marker-use--as-the-reference-marker-with--as-the-typing-trigger); `grund fmt` with `--check` / `--write` / `--marker`, the deterministic string-literal carve-out ([§FS-fmt.2.3.1](../functional-spec/FS-fmt.md#231-string-literal-exclusion-rule)), declaration-heading and fenced-block exemptions, and `[reference] strict = true` (this repo runs in strict mode).
- [§RM-md-link-emission](../roadmap.md#rm-md-link-emission-grund-fmt---cross-refs): `grund fmt --cross-refs` per [§FS-fmt.6](../functional-spec/FS-fmt.md#6-cross-reference-emission) and [§DF-md-link-anchor-strategy](../decisions/functional/DF-md-link-anchor-strategy.md#df-md-link-anchor-strategy-heading-text-slugs-re-derived-on-every-fmt-pass) — wraps marker-prefixed citations in `.md` files only, heading-text anchor slugs per the `github` / `gitlab` / `mkdocs` / `pandoc` / `none` profiles, re-derived each pass, fenced/inline-code-span and dangling-citation skips, idempotent, and the `[fmt.cross_refs] enabled` opt-in.
- [§RM-refs](../roadmap.md#rm-refs-grund-refs-): `grund refs <ID> [path] [--section <s>] [--format text|json]` per [§FS-refs](../functional-spec/FS-refs.md#fs-refs-grund-lists-every-citation-of-an-id) — over the same scan as `check`, sorted by `(path, line, column)`, honouring strict mode and the string-literal carve-out; NDJSON on stdout for `--format=json`, exit `0` always on a successful scan.
- [§RM-cover](../roadmap.md#rm-cover-grund-cover): `grund cover [path] [--format text|json]` per [§FS-cover](../functional-spec/FS-cover.md#fs-cover-grund-groups-citations-by-scanned-file) — the scanner's citation graph grouped by scanned file, files with zero citations included, text on stdout or one JSON record per file (the nested citation objects byte-match `grund refs --format=json`); no git history, no policy — the diff-aware co-change recipe ([§RM-cochange-gate](../roadmap.md#rm-cochange-gate-a-pre-commit--ci-recipe--no-impl-change-without-spec-and-test)) sits on top.
- [§RM-require-grounding](../roadmap.md#rm-require-grounding-the-opt-in-grounding-floor): `grund check --require-grounding` and `[reference] require_grounding` per [§FS-check.3.6](../functional-spec/FS-check.md#36-ungrounded-source-file-opt-in) — the `ungrounded source file` error class for scanned non-Markdown files with no resolving citation and no inline declaration; `--require-grounding` only ever adds the check, never disables a config that already sets it; `grund config show` prints `require_grounding`; this repo sets it `true`, `templates/grund.toml` ships it `false`. The diff-aware co-change gate stays a recipe under [§RM-cochange-gate](../roadmap.md#rm-cochange-gate-a-pre-commit--ci-recipe--no-impl-change-without-spec-and-test).
- `grund init` per [§FS-init](../functional-spec/FS-init.md#fs-init-grund-bootstraps-a-new-grund-conformant-repo) — `AGENTS.md` + `.agents/grund.toml`, the `--docs` scaffold, the versioned managed block with append / in-place update / position preservation / CRLF handling, `--name` / `--force` / `--append`, and idempotent `exists ` reporting.
- `grund id <KIND> "<title>" [path]` per [§FS-id](../functional-spec/FS-id.md#fs-id-grund-proposes-ids-for-new-declarations) — deterministic NFKD slugging, next-number derivation, `--width`, the collision check, number-less / slug-less `[id] format` handling, `--format text|json`.
- `grund` CLI surface per [§FS-cli](../functional-spec/FS-cli.md#fs-cli-grunds-command-line-surface-conventions) and [§FS-errors](../functional-spec/FS-errors.md#fs-errors-grund-emits-messages-in-fixed-shapes) — default-subcommand routing, `--version` / `-V`, `--help` / `-h`, the cross-subcommand `--format`, the three message shapes, `error:`-prefixed CLI errors, and the frozen `0`/`1`/`2` exit mapping.

### 2.8 Fixed

- [§FS-show](../functional-spec/FS-show.md#fs-show-grund-reads-a-single-declaration-body-by-id): `grund show <ID>.<section> --head` (and `--head --section S`) reported `section not found` for a section that exists — the `--head` short-circuit fired before the section was matched. It now prints the lead prose of the requested section; a missing section is still a `section not found` error. New e2e case `show-head-section-markdown` covers it.
- [§FS-fmt.2.3](../functional-spec/FS-fmt.md#23-what-is-never-rewritten): `grund fmt --marker` no longer rewrites ID-shaped file names inside Markdown link destinations, so a link like `[§FS-check](FS-check.md)` keeps its URL intact while the visible citation remains canonical.
- [§FS-cli.3](../functional-spec/FS-cli.md#3-cross-subcommand-flags): `grund check`, `show`, `refs`, `cover`, `fmt`, `list`, and `id` now reject a second path positional with `error: <subcommand> takes at most one path argument` (exit `2`) instead of silently using the last one and dropping the rest — for `check` the silent drop could turn `grund check docs src` into a flood of misleading `unknown reference …` findings. `config` and `agent-setup-instructions` already enforced this; the rule is now uniform. New e2e cases `check-extra-path-arg` and `show-extra-path-arg`.
- [§FS-init.2.4](../functional-spec/FS-init.md#24-generated-agentsgrundtoml) / [§FS-init.3](../functional-spec/FS-init.md#3-non-intrusive-guarantees): `grund init --force` no longer overwrites an existing `.agents/grund.toml` with the canonical template — a footgun that silently reset a repo's customized kinds, marker, scan scope, and everything else in the config. `--force` now targets only what `init` owns end to end — the managed `AGENTS.md` block and the `--docs` scaffold stubs — and an existing `.agents/grund.toml` is always reported as `exists` and left byte-for-byte unchanged ([§GOAL-configurable](../goals.md#goal-configurable-every-default-is-overridable): the config is the repo's customization surface, not a scaffold artifact); the file is only ever written when it is absent. New e2e case `init-force-keeps-existing-config`; `grund init --help` and [§FS-init.2.4](../functional-spec/FS-init.md#24-generated-agentsgrundtoml) / [§FS-init.3](../functional-spec/FS-init.md#3-non-intrusive-guarantees) updated to match.
- [§FS-init](../functional-spec/FS-init.md#fs-init-grund-bootstraps-a-new-grund-conformant-repo): `grund init` without `--docs` no longer tells the user to "run `grund check` — a freshly scaffolded tree is clean" as step 1 — without `docs/`/`e2e/` that run prints the empty-scan warning. The `next:` block now leads with scaffolding (`grund init --docs` or creating the folders), then the clean `grund check`. The empty-scan warning itself ([§FS-check.2.2](../functional-spec/FS-check.md#22-empty-scan)) points at `grund init --docs` rather than `grund init`, which was the command the user had just run.
- [§FS-config](../functional-spec/FS-config.md#fs-config-grund-reads-a-toml-config-file-under-agents): `grund config show` no longer prints `number_pattern` under a number-less `[id] format` (nor `slug_pattern` under a slug-less one) — the pattern only governs its placeholder, so emitting it implied a grammar that isn't in effect.
- [§FS-config.6](../functional-spec/FS-config.md#6-what-is-not-configured-here) / [§FS-errors.3](../functional-spec/FS-errors.md#3-message-text): `[output] color` is now validated against the documented `auto | always | never` set on load (a typo like `color = "rainbow"` fails with `error: <path>:<line>: unknown [output] color …`, exit `2` — or `1` from `grund config validate`), matching how `[output] format` and `[fmt.cross_refs] anchor_format` already validate their enums. The value stays inert until the colored-output feature lands; this only stops a misspelling from being silently accepted and then ignored.
- [§FS-errors.2.2](../functional-spec/FS-errors.md#22-cli-level-message) / [§FS-errors.2.3](../functional-spec/FS-errors.md#23-bare-query-failure): fix an internal contradiction — §2.2 claimed an `error:` prefix accompanies the exit-`1` "failed slug/collision query" and listed [§FS-id.3](../functional-spec/FS-id.md#3-slug-derivation) / [§FS-id.5](../functional-spec/FS-id.md#5-collision-check) as users, while [§FS-id](../functional-spec/FS-id.md#fs-id-grund-proposes-ids-for-new-declarations) and the shipped binary print those exit-`1` cases as the **bare** query-failure shape of §2.3 (no `error:`). §2.2 now says `error:` accompanies exit `2` only; §2.3's "used by" lists `grund show`'s missing/ambiguous-ID failures *and* `grund id`'s empty-slug / collision failures. No behavior change — the docs now match the binary.
- README: the ID-format diagram annotated the slug as `[a-z][a-z0-9-]*`, which is this repo's narrowed `slug_pattern`, not the default; it now shows the default `[a-z0-9][a-z0-9-]*` ([§FS-config.3.2](../functional-spec/FS-config.md#32-id--id-grammar)) and notes the narrowing in parentheses.
- [§FS-fmt.2.1](../functional-spec/FS-fmt.md#21-trigger-to-marker) / [§FS-lsp.1.4](../functional-spec/FS-lsp.md#14-live-trigger-transform): correct the `$$`-trigger description — it fires when the trigger is followed by a token matching the repo's `[id] format` ([§FS-config.3.2](../functional-spec/FS-config.md#32-id--id-grammar)), not only by `<KIND>-<digit>`. The implementation already handled slug-only IDs (the `{kind}-{slug}` form `grund` itself uses, where the trigger rewrites `$$FS-check` to the marker form); the spec text said otherwise and would have implied the trigger never fires in this repo.
- README: the `grund cover --format json` example piped through `jq '.files[] | …'`, but the command emits NDJSON — one `{"path":…,"citations":[…]}` record per scanned file ([§FS-cover.3.2](../functional-spec/FS-cover.md#32---format-json)) — so the example is now `jq -c 'select(…)'` with a note on the shape. Also drops the maintainer-only "run the Pre-release checks workflow before publish" line from the user-facing install section; that is owned by [§RM-distribution-naming](../roadmap.md#rm-distribution-naming-verify-package-names-before-first-publish).
- [§FS-cli](../functional-spec/FS-cli.md#fs-cli-grunds-command-line-surface-conventions): `grund config <unknown>` now reports `error: unknown config command \`<word>\`` plus an `expected: config validate, config show` line — matching the "name what was given" shape the rest of the surface uses (`unknown kind …`, `unsupported shell …`) — and a leading-dash argument is reported as `unknown flag`. New e2e case `config-unknown-command`.
- [§FS-cli.3](../functional-spec/FS-cli.md#3-cross-subcommand-flags) / [§FS-errors.3](../functional-spec/FS-errors.md#3-message-text): `--format=<value>` (the `=`-joined form the README's examples use) now behaves exactly like the space-separated `--format <value>` for `check`, `show`, `list`, `refs`, `cover`, and `id` — an unsupported value reports `error: unsupported <subcommand> format \`<value>\`` (exit `2`) instead of the misleading `error: unknown flag \`--format=<value>\``. The parser had matched only the literal `--format=json` / `--format=text` (plus `--format=md` for `show`) and let any other `--format=…` fall through to the generic unknown-flag arm. New e2e case `cli-invalid-format-eq`.
- `grund --help`: the `config` line is capitalized like every other command line (`Validate or show …`, was `validate or show …`), and the `cover` line reads `Group the citation graph by scanned file.` instead of the self-redundant `Group every scanned file's citations by file.`. `cli-help` golden output updated.
- [§FS-fmt.6.2](../functional-spec/FS-fmt.md#62-form) / [§DF-md-link-anchor-strategy.2.3](../decisions/functional/DF-md-link-anchor-strategy.md#23-renderer-profiles): the `github` (and aliased `gitlab`) `anchor_format` profile now reproduces `github-slugger` byte-for-byte — disallowed characters are deleted in place and *each* remaining space becomes one `-`, with no run-collapsing and no trailing-`-` trim. Before this, `grund fmt --cross-refs` emitted `#a-b` for `## A — B` and `#6-watch-mode-watch` for `` ## 6. Watch mode (`--watch`) ``, anchors that resolve nowhere on GitHub — the bug rode an erroneous "collapse consecutive `-`" clause in [§DF-md-link-anchor-strategy.2.3](../decisions/functional/DF-md-link-anchor-strategy.md#23-renderer-profiles), now corrected by the new decision record [§DF-github-anchor-fidelity](../decisions/functional/DF-github-anchor-fidelity.md#df-github-anchor-fidelity-the-github-anchor-profile-reproduces-github-slugger-exactly). `mkdocs` and `pandoc` are unchanged (Python-Markdown's TOC slugger genuinely collapses `[-\s]+`). New e2e case `fmt-cross-refs-profile-github` over a curated heading set; this repo's pre-commit hook gains `grund fmt --cross-refs --write` and switches `lychee` to `--include-fragments` so anchor drift in `docs/` fails the commit.
- [§FS-fmt.6.2](../functional-spec/FS-fmt.md#62-form): anchors derive from a heading's *rendered* text — an inline link `[text](url)` in a heading is reduced to `text` before slugifying, mirroring the renderer. This makes `grund fmt --cross-refs` idempotent over a citation that lives in a section heading (`### 2.2 … supersede §FS-fmt.6.3` → `### 2.2 … supersede [§FS-fmt.6.3](…)`): before the fix, the second `fmt` pass slugified the raw source and mangled the anchor with the destination URL it had just inserted. The `fmt-cross-refs-profile-github` e2e case covers it.
- [§DF-github-anchor-fidelity](../decisions/functional/DF-github-anchor-fidelity.md#df-github-anchor-fidelity-the-github-anchor-profile-reproduces-github-slugger-exactly): new decision record — the `github` anchor profile must match `github-slugger` exactly and operate on rendered heading text; records why the previous "collapse runs" description was wrong, why heading text must be reduced before slugging, and why `grund check` never caught either (it validates the citation, not the `#fragment`).
- [§FS-fmt.6.2](../functional-spec/FS-fmt.md#62-form) / [§DF-declaration-anchor](../decisions/functional/DF-declaration-anchor.md#df-declaration-anchor-a-bare-id-markdown-link-points-at-the-declarations-heading-anchor): `grund fmt --cross-refs` now gives a *bare-ID* citation an anchor too — `§GOAL-<x>` wraps to `[§GOAL-<x>](../goals.md#goal-x-the-title)` (the declaration's own heading), not a bare link to `goals.md` that drops the reader at the file top. Applies whenever the declaration's home is Markdown; a stub whose home is a source file (`# AR-x: [the scanner](src/x.rs)`) still gets a bare file link (a renderer won't jump inside a doc-comment), and `anchor_format = "none"` is unchanged. New decision record [§DF-declaration-anchor](../decisions/functional/DF-declaration-anchor.md#df-declaration-anchor-a-bare-id-markdown-link-points-at-the-declarations-heading-anchor) amends [§FS-fmt.6.2](../functional-spec/FS-fmt.md#62-form)'s old "no section → no anchor" rule; the `fmt-cross-refs-profile-github` e2e case gains a bare-ID anchor case, and running `grund fmt --cross-refs --write` over this repo rewrites every bare-ID `.md` citation to carry the declaration anchor.
- [§FS-fmt.6.7](../functional-spec/FS-fmt.md#67-configurability): the `mkdocs` `anchor_format` profile now keeps `-` in its slugs and collapses runs of `-`/whitespace to one separator, matching Python-Markdown's TOC slugger (`re.sub(r'[-\s]+', sep, …)`) — `# FS-1-x: Y` slugs to `#fs-1-x-y`, not `#fs1x-y`. Previously `-` was dropped, which only surfaced once [§DF-declaration-anchor](../decisions/functional/DF-declaration-anchor.md#df-declaration-anchor-a-bare-id-markdown-link-points-at-the-declarations-heading-anchor) started emitting anchors derived from headings (declaration headings carry `-` in the ID).
- [§FS-fmt.6.2](../functional-spec/FS-fmt.md#62-form): heading-text reduction (the "rendered text" rule) now also drops HTML-tag-shaped spans `<…>`, matching what a renderer does — `## RM-show: grund show <ID>` slugs as `rm-show-grund-show`, not `rm-show-grund-show-id`. Surfaced by [§DF-declaration-anchor](../decisions/functional/DF-declaration-anchor.md#df-declaration-anchor-a-bare-id-markdown-link-points-at-the-declarations-heading-anchor): the `<ID>` placeholders in the `RM-…` roadmap headings were producing anchors GitHub does not render. `lychee --include-fragments` over `docs/` (the pre-commit hook) is clean.
- [§GOAL-multi-language.1](../goals.md#1-identical-behavior) / [§FS-distribution.2](../functional-spec/FS-distribution.md#2-cli-parity) / [§FS-errors.4](../functional-spec/FS-errors.md#4-determinism): make cross-platform CLI parity explicit, add Windows to the Rust CI matrix, and normalize repo-relative report paths to `/` so Linux, macOS, and Windows produce byte-identical e2e output.
