diff --git a/.github/scripts/docs-only.sh b/.github/scripts/docs-only.sh
index d2264dab..6e27ab9e 100755
--- a/.github/scripts/docs-only.sh
+++ b/.github/scripts/docs-only.sh
@@ -25,5 +25,8 @@ if [ -n "$base" ] \
esac
done <<<"$files"
fi
+if [ "$result" = true ]; then
+ scripts/check-guide-names.sh
+fi
echo "docs_only=$result" >>"$out"
echo "docs-only: $result" >&2
diff --git a/README.md b/README.md
index b0fed75c..0962a49e 100644
--- a/README.md
+++ b/README.md
@@ -171,6 +171,29 @@ graft is early. The design is [docs/design.md](docs/design.md); work lands
on short-lived branches through the merge queue. See
[CONTRIBUTING.md](CONTRIBUTING.md).
+## Documentation
+
+User guide:
+
+- [Concepts](docs/guide/concepts.md): glossary and the git-to-graft table.
+- [MCP](docs/guide/mcp.md): every MCP tool, with its arguments and results.
+- [Server](docs/guide/server.md): config, tokens, repositories, lines, metrics and upgrades.
+- [Git migration](docs/guide/git-migration.md): import, follow GitHub, batch pull requests and export.
+- [FAQ](docs/guide/faq.md): git compatibility, offline use, lines, CI and the licence.
+- [Benchmarks](docs/guide/benchmarks.md): how the published timings were measured.
+- [Agents](docs/agents.md): how an agent session uses graft.
+
+[The same index](docs/guide/README.md) lives with the guide.
+
+## How it is built
+
+- [Design](docs/design.md)
+- [Plan](docs/plan.md)
+- [Decisions](docs/decisions.md)
+- [Status](docs/status.md)
+- [Trials](docs/trials)
+- [Notes](docs/notes)
+
## Licence
[AGPL-3.0-only](LICENSE).
diff --git a/changes/WI-2549.md b/changes/WI-2549.md
new file mode 100644
index 00000000..957112fa
--- /dev/null
+++ b/changes/WI-2549.md
@@ -0,0 +1 @@
+- User guide pages are indexed from the README and `docs/guide/README.md`, with the engineering record under "How it is built". Repository CI fails the docs-only path when the guide names an estate host, tracker, trial or internal id.
diff --git a/docs/guide/README.md b/docs/guide/README.md
new file mode 100644
index 00000000..e79f2e9b
--- /dev/null
+++ b/docs/guide/README.md
@@ -0,0 +1,23 @@
+# User guide
+
+- [Concepts](concepts.md): glossary and the git-to-graft table.
+- [MCP](mcp.md): every MCP tool, with its arguments and results.
+- [Server](server.md): config, tokens, repositories, lines, metrics and upgrades.
+- [Git migration](git-migration.md): import, follow GitHub, batch pull requests and export.
+- [FAQ](faq.md): git compatibility, offline use, lines, CI and the licence.
+- [Benchmarks](benchmarks.md): how the published timings were measured.
+- [Bench chart](bench.svg): the rendered timings the methodology describes.
+- [Agents](../agents.md): how an agent session uses graft.
+
+The same list is in the repository [README](../../README.md#documentation).
+
+## How it is built
+
+Engineering record, not part of the user guide:
+
+- [Design](../design.md)
+- [Plan](../plan.md)
+- [Decisions](../decisions.md)
+- [Status](../status.md)
+- [Trials](../trials)
+- [Notes](../notes)
diff --git a/docs/guide/bench.svg b/docs/guide/bench.svg
index 12eedf3b..982ce708 100644
--- a/docs/guide/bench.svg
+++ b/docs/guide/bench.svg
@@ -80,7 +80,7 @@
rdpapp-split: 9 PRs, clean 1=1, conflicts 8=8, rt 17/19
rdpapp-stack: 11 PRs, clean 0=0, conflicts 1=1, rt 2/2
fluent2-registration: 34 PRs, clean 18=18, conflicts 16=16, rt 50/64
-rusttorrent-qbit: 4 PRs, clean 0=0, conflicts 4=4, rt 8/8
+qbit-importer: 4 PRs, clean 0=0, conflicts 4=4, rt 8/8
graft-merge-back: 57 PRs, clean 39=39, conflicts 18=18, rt 75/153
Branching strategies
@@ -88,6 +88,6 @@
trunk: round trips 85 / 60, conflict blocks 4 / 5
gitflow: round trips 293 / 154, conflict blocks 29 / 18
release: round trips 173 / 106, conflict blocks 11 / 11
-Not on this chart: the WI-2341 twelve-run read-path medians (D31 floor). They are in docs/status.md and docs/decisions.md only, not in a results file.
+Not on this chart: the twelve-run read-path medians (the read-latency floor). They are in the engineering record only, not in a results file.
diff --git a/docs/guide/benchmarks.md b/docs/guide/benchmarks.md
index eb55d5cf..0072dbb8 100644
--- a/docs/guide/benchmarks.md
+++ b/docs/guide/benchmarks.md
@@ -25,9 +25,9 @@ as a speed change.
## What is not on the chart
-The twelve-run read-path medians recorded by WI-2341 on 2026-10-24 are
-not in any file under `bench/results/`. They are written in
-[docs/status.md](../status.md) and [docs/decisions.md](../decisions.md) (D31):
+The twelve-run read-path medians recorded on 2026-10-24 are
+not in any file under `bench/results/`. They are written in the engineering
+record listed under "How it is built":
| | 1k graft / git | 10k graft / git |
| --- | ---: | ---: |
@@ -48,17 +48,17 @@ graft is faster on full clone, sparse clone, `log --path` and blame at
both recorded scales, and faster on concurrent clean lands at 10k
(238 ms vs 365 ms). It is slower on `log -50`, status and, at 10k, sync.
-Read latency is a no-regression floor, not a phase-1 target (D31,
-2026-10-25). No read path may be slower than the WI-2341 medians above,
-and every operation stays O(change). Being 1.3× to 2.4× slower than git
-on log, status and sync in the single-run files is recorded, not a
-defect to close in phase 1. 2× faster than git is a later-phase target.
-WI-2400 is wont_do.
+Read latency is a no-regression floor, not a phase-1 target
+(recorded 2026-10-25). No read path may be slower than the twelve-run
+medians above, and every operation stays O(change). Being 1.3× to 2.4×
+slower than git on log, status and sync in the single-run files is
+recorded, not a defect to close in phase 1. 2× faster than git is a
+later-phase target. Closing that gap is out of scope for phase 1.
## Real-PR replay
`bench/results/2026-10-24-real-prs/results.json`, 2026-10-24, 115 PRs in
-five cases (rdpapp split and stack, fluent2 registration, rustTorrent
+five cases (rdpapp split and stack, fluent2 registration, the qBittorrent importer
qBittorrent, graft merge-back). Clean merges and conflicts match git on
every case: 58 clean, 47 conflicts, graft-only and git-only clean both
zero. One case (`rdpapp-stack`) has no `agreement` object; its graft and
@@ -73,8 +73,8 @@ The chart uses the agreement block.
`bench/results/2026-10-24-branching/results.json`, seed 2362. The chart
shows round trips and conflict blocks for trunk, git flow and release
-branches, graft-tracked against git. This file predates merge points
-(WI-2372). [docs/status.md](../status.md) records the later hand-off: with merge points,
+branches, graft-tracked against git. This file predates merge points.
+The engineering record lists the later hand-off: with merge points,
graft reports the same conflicts as git (18 of 18 blocks) and merges
`release/1.1` into `main` clean. That later run is not in this file, so
it is not on the chart. The file will be regenerated; until then the
diff --git a/scripts/check-guide-names.sh b/scripts/check-guide-names.sh
new file mode 100755
index 00000000..3f0fbc4c
--- /dev/null
+++ b/scripts/check-guide-names.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+# Fail if the user guide names estate hosts, trackers, or internal ids.
+# Repository CI only. Not part of graft's land path.
+set -euo pipefail
+root=$(cd "$(dirname "$0")/.." && pwd)
+cd "$root"
+shopt -s nullglob
+files=(docs/guide/* docs/agents.md)
+pattern='graft-dev|vogt|trial-[0-9]|rusttorrent|\bWI-[0-9]+|\bD[0-9]{2}\b|\bG[0-9]\.[0-9]+'
+if grep -nEiH "$pattern" "${files[@]}"; then
+ exit 1
+fi