Six plans (tracer generator, site UX and checkers, content A, content B,
acme/blog walkthrough, unit tests). SC4/DOCS-04 narrowed to docs/ pages per
D-18; README Go fence conversion logged as a todo.
Per D-12, an inline code span `pkg.Ident`, `pkg.Type.Member`, `*pkg.Ident`, `pkg.Ident[...]` or `pkg.Ident(...)` whose first segment is a discovered module (or sub-package) name and whose Ident does not exist in that package fails go test, docs:build and the gate with `<file>:<line>: identifier: <pkg>.<Ident> does not exist in modules/<pkg>`; this covers docs/ pages, every module README and the root README.
Per D-12, a relative link or `#anchor` in a docs page or ingested README that does not resolve to a page or to a heading ID (computed by the renderer's slug IDs) fails with `link: <target> does not resolve` or `link: #<anchor> not found in <page>`.
A `summer <cmd>` token in a docs `sh` fence or code span must be a name from cmd/summer toolCommands(), and a `./bin/<app> <cmd>` token must be an application command collected by calling the module command constructors the generated main uses plus centrifugo.Commands and flare.Commands, or a bonfire.Command literal declared under docs/examples or examples; the checker receives these sets and holds no hard-coded command list.
Per D-11, a consuming-application name in any page source, snippet copy or built output (HTML, .md, llms.txt, llms-full.txt, search-index.json) fails with `forbidden: consuming-application name in output`, without echoing the matched word.
Every go fence in a docs/ page carries src=; one without it fails with `snippet: go code block has no src= reference`; an unknown `> [!TYPE]` callout fails with `callout: unknown type <TYPE> (use NOTE, TIP or WARNING)`; a docs/ heading that is not plain ASCII text or contains a link or code span fails with a `heading:` problem.
Per D-04, every rendered page has the Winter-style shell: header with search trigger and theme toggle, sidebar grouped by section, on-page TOC when a page has at least 2 H2/H3 headings, prev/next pager in reading order crossing sections, Edit this page (edit_url) and View as Markdown actions, heading permalinks, callouts, copy buttons and a footer linking llms.txt and llms-full.txt; the version selector and Docs/API/Markup/UI tabs are left out.
Per D-15, code fences are highlighted at build time by github.com/alecthomas/chroma/v2 called from a custom goldmark NodeRenderer that maps chroma token types onto the UI-SPEC classes tok-kw, tok-key, tok-str, tok-com, tok-num and tok-prompt; goldmark-highlighting is not used, and go.mod gains only chroma/v2 and its transitive regexp2 module.
Per D-03, `summer docs:serve` builds the site and serves it on 127.0.0.1:8088 by default, refuses a non-loopback --addr with the UI-SPEC copy unless --allow-remote is passed, serves 404.html with status 404, and rebuilds on change while keeping the last good build.
Per D-13 and DOCS-08, CLAUDE.md's Documentation section states that API, config-key or CLI changes update the module README and the affected docs pages in the same change, and names the automated checkers; per D-17 it notes that config-key checking is deferred.
Per D-17, `.planning/todos/pending/wristband-neutral-resource-default.md` records that the wristband default resource URL names the consuming application; the wristband API is unchanged in this phase.
scripts/check-phase11.1.sh --self-test plants an unknown identifier, a missing README, a drifted snippet, a frontmatter typo, a broken anchor, an unknown command, a forbidden name, a go fence without src= and an unknown callout, and each is refused for its own rule.
Empty search query shows the heading 'Search the documentation' and its body copy; no results list renders.
Search with 0 matches shows 'No results for "{query}"' and its body copy; 1 to 20 matches render rows; more than 20 are truncated to the top 20 by rank; the live region says 'No results', '1 result' or '{n} results'.
While search-index.json is being fetched on first open, 'Loading the search index…' shows in the results area.
A rejected index fetch shows 'Search needs a web server. Run `summer docs:serve` and open the address it prints.'; a non-200 or invalid JSON response shows 'The search index could not be loaded. Reload the page to try again.'; the dialog stays usable and closable.
The search results list has max-height 60vh with its own scroll; excerpts clamp to 2 lines; titles and headings wrap.
The sidebar scrolls on its own (height calc(100vh - 64px), overflow-y auto) and site.js scrolls the active item into view on load; sidebar and TOC items wrap with min-height 32px and no ellipsis.
With fewer than 2 H2/H3 headings neither the TOC column nor toc-inline renders.
The first page renders only Next (kept in the right column), the last page only Previous; a cross-section target adds the section line; a one-page site renders no pager nav; pager titles wrap and both cards stretch to equal height.
The build writes 404.html with 'Page not found' and docs:serve returns it with status 404.
pre and tables scroll horizontally inside their own box (overflow-x auto); inline code and bare URLs use overflow-wrap anywhere.
If localStorage throws, the theme falls back to system for the session, the toggle still cycles and no error is shown.
The copy button is not rendered without navigator.clipboard; a rejected write announces 'Copy failed. Select the code and copy it manually.'
statement
verification
Search result rows show section › title, heading and a 2-line excerpt with mark highlights, and arrow keys and Enter work (manual UAT via summer docs:serve).
backstop
statement
verification
theme-init.js applies dark or light before first paint in all three modes, so there is no flash of the wrong theme (manual UAT: reload in each mode).
backstop
statement
verification
The UI-SPEC contrast pairs hold on a guide page and an API reference page in light and dark at 1280px, 1024px and 375px (manual UAT).
backstop
statement
verification
Without JS the sidebar renders above the content below 1024px and the search and theme buttons are hidden (manual UAT with JS disabled).
backstop
Per D-18 (user decision): the strict src= policy applies to Go fences in docs/ pages; Go fences inside ingested module READMEs are rendered as written and are covered by the identifier checker, not by src= verification.
requirement_id
category
status
verification
resolution
reason
statement
DOCS-02
privacy
resolved
judgment
Fonts and icon paths are vendored under theme/assets with their licence files; templates and JS reference only base_url-prefixed /assets/ paths.
A docs site that phones home to a font CDN or analytics service leaks every visitor's reading history.
The theme must not load fonts, icons or scripts from any third-party origin and must not include analytics or tracking.
path
provides
contains
internal/docsite/check_identifiers.go
module identifier index and span checker with go doc fallback
go/parser
path
provides
contains
internal/docsite/check_links.go
internal link and anchor checker sharing the renderer IDs
not found in
path
provides
contains
internal/docsite/check_commands.go
summer and application command-name checker
is not a summer or application command
path
provides
contains
internal/docsite/check_forbidden.go
consuming-application name check over sources and outputs
consuming-application name in output
path
provides
contains
internal/docsite/highlight.go
goldmark NodeRenderer for fenced code using chroma/v2
chroma/v2
path
provides
contains
internal/docsite/serve.go
loopback preview server with 404 handling and rebuild
--allow-remote
path
provides
contains
scripts/check-phase11.1.sh
phase gate with --self-test and --all
--self-test
path
provides
contains
CLAUDE.md
D-13 documentation rule and named checkers
affected pages under `docs/`
from
to
via
pattern
cmd/summer/docs.go
internal/docsite/check_commands.go
docsCommands() passes toolCommands() names and module runtime command names into docsite.Options.Commands
toolCommands()
from
to
via
pattern
internal/docsite/check_links.go
internal/docsite/render.go
anchors computed with the same slug IDs the renderer passes to parser.WithIDs
slug|IDs
from
to
via
pattern
internal/docsite/highlight.go
github.com/alecthomas/chroma/v2
lexers.Get + Tokenise mapped onto tok-* classes
chroma
from
to
via
pattern
scripts/check-phase11.1.sh
cmd/summer docs:build --check
self-test plants violations in a scratch root and expects refusal by rule
docs:build --check
Make the docs trustworthy and usable. Every accuracy rule becomes a `go test ./...` failure and a `docs:build` refusal (identifiers, links and anchors, command names, consuming-application names, strict go-fence policy, callouts, headings), the phase gate `scripts/check-phase11.1.sh` orchestrates them, and the site gets the full WinterCMS-style theme from 11.1-UI-SPEC.md with chroma highlighting, client-side search, dark mode and `summer docs:serve`. CLAUDE.md records the D-13 rule.
Purpose: D-04, D-11, D-12, D-13, D-15 and D-17 before any content is written, so plans 11.1-03 to 11.1-05 are checked as they write.
Per D-18 (user decision): the src= policy covers Go fences in docs/ pages. Go fences in the ingested module READMEs are shown as written and covered by the identifier checker only. Converting README fences to src= copies would need edits to every module README, including files gap plan 11-08 is changing, so it is not in this plan.
Output: checkers, theme, docs:serve, gate script, CLAUDE.md edit, wristband todo, chroma/v2 in go.mod.
@.planning/PROJECT.md
@.planning/STATE.md
@.planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-CONTEXT.md
@.planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-RESEARCH.md
@.planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-PATTERNS.md
@.planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-UI-SPEC.md
@.planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-01-SUMMARY.md
@CLAUDE.md
@cmd/summer/main.go
@cmd/summer/docs.go
@internal/docsite/docsite.go
@internal/build/build.go
@scripts/check-phase11.sh
@scripts/check-phase10.2.sh
Additions to `internal/docsite` in this plan:
type Commands struct { Tool []string; App []string } and field Options.Commands *Commands. A nil Commands is a problem (command: no command set supplied), never a silent skip.
func Serve(ctx context.Context, opts Options, addr string, allowRemote bool, out io.Writer) error and func Handler(dir string) http.Handler (static files from dir, 404.html with status 404).
Problem rules added: identifier, link, command, forbidden, callout, heading (plus snippet for go fences without src=).
In cmd/summer/docs.go: func docsCommands() *docsite.Commands collects names; docs:serve command with flags root, src, base-url, addr (default 127.0.0.1:8088), bare allow-remote.
Runtime command constructors the generated app main calls (internal/build/build.go lines 114-118): lagoon.RuntimeCommands(app, plugins), conga.RuntimeCommands(app, plugins), surf.ServeCommand(app, plugins), surf.RouteListCommand(app, plugins), cabana.RuntimeCommands(app); plus lagoon.KeyGenerateCommand(), centrifugo.Commands(app) (modules/lighthouse/centrifugo) and flare.Commands(app), which applications append. backpack.New(cfg *compass.Config) *App builds the app handle; the constructors only capture it.
Task 1: Tracer: a stale identifier in any docs page or module README fails go test, docs:build and the phase gate
.planning/phases/11-jobs-realtime-and-search-infrastructure/11-08-SUMMARY.md exists (gap plan 11-08 has committed its lagoon and cabana changes, so module READMEs are stable)
internal/docsite/check_identifiers.go, internal/docsite/docsite.go, internal/docsite/checks_test.go, cmd/summer/docs_test.go, scripts/check-phase11.1.sh
- internal/docsite/docsite.go and internal/docsite/load.go (Check pipeline from plan 11.1-01)
- .planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-RESEARCH.md section Q6 "Identifier checker" and Q7
- .planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-PATTERNS.md section "scripts/check-phase11.1.sh"
- scripts/check-phase11.sh lines 1-60 and the final case block (mode layout, refuse, usage)
- scripts/check-phase10.2.sh lines 100-135 (expect_refusal, run_self_test)
- modules/lighthouse/README.md, modules/conga/README.md (Phase 11 README span style)
Per D-12, wire one checker through every layer: index, Check, docs:build refusal, real-tree test and gate.
check_identifiers.go: build an index with stdlib go/parser (non-test files only) for every directory under modules/ that holds Go files, including sub-packages (lagoon/attach, lighthouse/centrifugo, beachcomber/typesense), keyed by the last path element. Record exported top-level funcs, types, consts and vars; methods keyed by receiver base type (strip *, IndexExpr, IndexListExpr so Go 1.27 generic methods such as func (b *Bus) Fire[T any] index under Bus); struct fields including embedded type names; interface methods. Two directories with the same key is a problem naming both paths.
Spans: walk goldmark ast.CodeSpan nodes (never fenced blocks) in every docs page, every ingested module README and the root README.md. Match ^\*?(pkg)\.(Ident)(\.Member)?(\[[^\]]*\])?(\(.*\))?$ only when pkg is an index key and Ident starts uppercase; ignore a lowercase Member. So http.Handler, fields.yaml, acme.blog, summer.yaml and config keys are ignored (RESEARCH Pitfall 6).
On an index miss, fall back to exec.Command("go", "doc", "./modules/<path>", "<Ident>[.<Member>]") run in Root (argument list, no shell; Ident and Member already match the identifier regex) and accept exit 0, which covers promoted members through embedding. Otherwise report {file}:{line}: identifier: {pkg}.{Ident} does not exist in modules/{pkg} with the span's source line (README line, or page line counted from the top of the file including frontmatter).
Call the checker from Check, so docs:build, docs:build --check and TestDocsTree all enforce it. Fix any miss the real tree reports by correcting the README or page text, never by changing a module API (phase boundary); a README fix for a module the plan does not list is allowed and goes in this task's commit.
internal/docsite/checks_test.go: TestIdentifierChecker over a t.TempDir() fixture module: a known func, method, generic method, field and interface method pass; fixture.Missing fails with the exact problem text; http.Handler and fields.yaml are ignored.
scripts/check-phase11.1.sh, following check-phase11.sh and check-phase10.2.sh: header comment, set -euo pipefail, ROOT="${PHASE11_1_ROOT:-...}", refuse(), usage() exiting 2, modes --preconditions (11-08-SUMMARY.md exists; conga, lighthouse, flare and beachcomber have README.md and a root README table row), --deps (compare the module paths in go.mod with git show "${PHASE11_1_DEPS_BASE:-9033d81}":go.mod: nothing removed; added paths must be a subset of github.com/alecthomas/chroma/v2 and github.com/dlclark/regexp2/v2), --docs (build the summer binary once into a temp dir, run docs:build --out "$tmp/site", require index.html, index.md, llms.txt, llms-full.txt, search-index.json, assets/site.css and .summer-docs, and no .go file under the output), --forbidden (case-insensitive grep of docs/ and a fresh build output for the consuming-application spellings listed in RESEARCH Q6 plus the accented variant the Phase 11 hygiene regex covers; any hit refuses without printing the matched line content beyond the file name), --go (go vet ./... then go test ./... without -short), --self-test and --all. --self-test runs bash -n on itself, copies docs/, modules/, go.mod and go.sum into a scratch root, and for each plant runs <summer> docs:build --check --root <scratch copy> expecting a non-zero exit whose output contains the rule text: unknown identifier (identifier: bonfire.NoSuchThing does not exist in modules/bonfire), module dir with a Go file and no README (readme: package has Go files but no README.md), drifted installation snippet (snippet: body differs), unknown frontmatter field (frontmatter: unknown field). Each mode prints phase11.1 <mode> passed.
Stage only this task's files plus any README it had to correct.
go vet ./internal/docsite/... ./cmd/summer && go test ./internal/docsite ./cmd/summer -run '^(TestIdentifierChecker|TestDocsTree)$' -count=1 -v
<fails_when>non-zero exit, a "--- FAIL" line, or "no tests to run"</fails_when>
bash -n scripts/check-phase11.1.sh && scripts/check-phase11.1.sh --self-test && scripts/check-phase11.1.sh --preconditions && scripts/check-phase11.1.sh --deps && scripts/check-phase11.1.sh --docs
<fails_when>non-zero exit, a line starting "refuse:", or a missing "phase11.1 self-test passed" line</fails_when>
<acceptance_criteria>
- go test ./internal/docsite -run '^TestIdentifierChecker$' -count=1 -v prints --- PASS: TestIdentifierChecker.
- scripts/check-phase11.1.sh --self-test prints phase11.1 self-test passed.
- A scratch copy of docs/ with `lighthouse.NoSuchThing` added to docs/index.md makes go run ./cmd/summer docs:build --check --src <scratch-docs> exit non-zero and print identifier: lighthouse.NoSuchThing does not exist in modules/lighthouse.
- grep -n 'go/parser' internal/docsite/check_identifiers.go finds a match.
- go test ./cmd/summer -run '^TestDocsTree$' -count=1 passes on the real tree (READMEs of all 22 modules and the root README included).
</acceptance_criteria>
The identifier rule runs over docs pages, module READMEs and the root README in go test, docs:build and the gate, and the gate's self-test proves it refuses a planted stale name.
Task 2: Links, anchors, command names, consuming-application names and the go-fence policy fail the build; CLAUDE.md records the rule
internal/docsite/check_links.go, internal/docsite/check_commands.go, internal/docsite/check_forbidden.go, internal/docsite/check_policy.go, internal/docsite/docsite.go, internal/docsite/checks_test.go, internal/docsite/docsite_test.go, cmd/summer/docs.go, cmd/summer/docs_test.go, cmd/summer/main_test.go, scripts/check-phase11.1.sh, CLAUDE.md, .planning/todos/pending/wristband-neutral-resource-default.md
- internal/docsite/render.go (slug IDs helper and link transformer from plan 11.1-01)
- internal/build/build.go lines 105-130 (runtime command constructors in the generated main)
- modules/lagoon/commands.go, modules/conga/commands.go, modules/cabana/commands.go, modules/surf/routelist_command.go, modules/lighthouse/centrifugo/commands.go, modules/flare/commands.go (constructor signatures)
- cmd/summer/main_test.go TestToolDoesNotImportExamplePlugins
- CLAUDE.md "## Documentation" section (4 bullets)
- .planning/todos/pending/redacting-slog-handler.md (todo frontmatter format)
- modules/wristband/server.go lines 56-105 (the default resource value and comments; read only, do not edit)
- .planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-RESEARCH.md Q6 "Link and anchor checker", "CLI command-name checker", "Forbidden-name check"
- .planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-UI-SPEC.md CLI output table
1. `check_links.go` (D-12): walk `ast.Link` and `ast.Image` in every page (guides and ingested READMEs). `http(s)://` and `mailto:` are allowed and never fetched. `#frag` must be a heading ID of the same page; a relative `*.md` link (optional `#frag`) must resolve to a page in the tree or to an ingested `modules//README.md`, and its fragment must be a heading ID of the target. Any other relative repo path in a guide page (`.planning/`, `examples/`, source files) fails. IDs come from the same slug helper the renderer passes to `parser.WithIDs`. Problems: `{file}:{line}: link: {target} does not resolve` and `{file}:{line}: link: #{anchor} not found in {page}`.
2. `check_commands.go`: in `sh`/`shell`/`bash` fences and in code spans of docs pages and ingested READMEs, find `summer ` (optionally after `$ `) and `./bin/ ` tokens. A `summer` name must be in `Commands.Tool`; a `./bin/` name must be in `Commands.App` or in the set of `bonfire.Command` composite literals with a string-literal `Name` found by `go/parser` in non-test Go files under `docs/examples/` and `examples/` (the tool must not import example plugins). Problem: `{file}:{line}: command: "{name}" is not a summer or application command`. Flags and arguments after the name are ignored.
3. `cmd/summer/docs.go`: `docsCommands()` returns `Tool` = names from `toolCommands()` and `App` = names from `lagoon.RuntimeCommands`, `lagoon.KeyGenerateCommand`, `conga.RuntimeCommands`, `surf.ServeCommand`, `surf.RouteListCommand`, `cabana.RuntimeCommands`, `centrifugo.Commands` and `flare.Commands`, called on `backpack.New` with an empty config and nil plugins (confirm the constructors only capture the app; if one dereferences config at construction, pass an empty `compass` config loaded from a temp directory instead). Pass it to every `docsite` call, including `TestDocsTree`, `TestDocsBuildRealTree` and the other real-tree tests, and give the plan 11.1-01 fixture tests in `internal/docsite/docsite_test.go` an explicit fixture `Commands` value so they keep asserting exact problem lists. In `cmd/summer/docs_test.go`: `TestDocsCommandNames` asserts the sets include `docs:build`, `make:plugin`, `migrate:status` (Tool) and `key:generate`, `route:list`, `admin:create`, `queue:clear`, `websockets:health` (App); `TestDocsCommandsMirrorGeneratedMain` parses internal/build/build.go and asserts that every `.(app` constructor it writes into the generated main is also called in cmd/summer/docs.go. Extend `TestToolDoesNotImportExamplePlugins` so an import path containing `docs/examples` also fails.
4. `check_forbidden.go` (D-11): one unexported case-insensitive regexp holding the consuming-application spellings from RESEARCH Q6 (plus the accented variant from the Phase 11 hygiene regex). Run it over each page's source text (reporting source file and line) and over every in-memory output file (reporting the output-relative path and line). Problem: `{file}:{line}: forbidden: consuming-application name in output`; never echo the match. The test fixture for this rule builds the forbidden word at run time (string concatenation) so no test source contains it verbatim.
5. `check_policy.go`: every fence whose language is `go` in a page under `Src` must carry `src=` (`{file}:{line}: snippet: go code block has no src= reference`); ingested READMEs are exempt (D-18). `> [!TYPE]` blockquotes with TYPE outside NOTE, TIP, WARNING fail with `{file}:{line}: callout: unknown type {TYPE} (use NOTE, TIP or WARNING)`. Headings in docs/ pages must be ASCII text with no link or code span (`{file}:{line}: heading: headings must be plain ASCII text without links or code`).
6. Smoke tests in `internal/docsite/checks_test.go`: `TestLinkChecker`, `TestCommandChecker`, `TestForbiddenChecker`, `TestFencePolicy`, each with one passing and one failing fixture asserting the exact problem text.
7. Gate: add `--claude` (the CLAUDE.md Documentation section contains the new bullets below) and self-test plants for a broken anchor (`link: #`), an unknown command (`summer no:such` → `is not a summer or application command`), a forbidden name (built at run time in the script from two halves), a go fence without src= and a `> [!DANGER]` callout. `--all` runs `--preconditions --deps --self-test --docs --forbidden --claude --go`.
8. Docs-rules commit, separate from the code commit (CLAUDE.md commit rule), per D-13, DOCS-08 and D-17. In CLAUDE.md "## Documentation" add the bullet: "A change to a module's exported API, config keys or CLI commands also updates the affected pages under `docs/` in the same change. `go test ./cmd/summer -run TestDocsTree` and `summer docs:build --check` check identifiers, internal links and anchors, `src=` snippets, command names and consuming-application names across `docs/` and every module README." Amend the go doc bullet to: "Every identifier named in a README or a docs page must exist in the package. The docs checker verifies this automatically; `go doc ./modules/ ` remains the manual check." Add: "Config keys named in README or docs pages are not checked automatically yet (deferred in Phase 11.1); review them by hand." Write `.planning/todos/pending/wristband-neutral-resource-default.md` (frontmatter title, date 2026-09-30, priority medium, area `summercms.go wristband`) stating that `wristband.DefaultOptions()` ships a resource URL and comments that name a consuming application (server.go default and nearby comments, stores.go and client_issue.go comments), that a framework default should be empty or neutral, that docs pages must not quote it, and that the API is unchanged in Phase 11.1.
Stage only this task's files; commit code first, then CLAUDE.md plus the todo as docs(11.1): ....
go vet ./... && go test ./internal/docsite ./cmd/summer -count=1
<fails_when>non-zero exit or a "FAIL" line</fails_when>
scripts/check-phase11.1.sh --self-test && scripts/check-phase11.1.sh --claude && scripts/check-phase11.1.sh --forbidden
<fails_when>non-zero exit or a line starting "refuse:"</fails_when>
<acceptance_criteria>
- go test ./internal/docsite -run '^(TestLinkChecker|TestCommandChecker|TestForbiddenChecker|TestFencePolicy)$' -count=1 -v prints four --- PASS lines.
- go test ./cmd/summer -run '^(TestDocsCommandNames|TestDocsCommandsMirrorGeneratedMain|TestToolDoesNotImportExamplePlugins|TestDocsTree)$' -count=1 -v prints four --- PASS lines.
- grep -F 'also updates the affected pages under docs/' CLAUDE.md finds a match.
- grep -F 'Config keys named in README or docs pages are not checked automatically yet' CLAUDE.md finds a match.
- test -f .planning/todos/pending/wristband-neutral-resource-default.md.
- ! grep -rniE 'fonoteka|p(l|ł)ytarium' docs internal/docsite/checks_test.go (no match).
- git diff --name-only 9033d81 -- modules/wristband prints nothing (wristband untouched).
- grep -c 'toolCommands()' cmd/summer/docs.go prints at least 1.
</acceptance_criteria>
Every D-11/D-12 rule and the go-fence policy fail go test and docs:build with UI-SPEC problem lines; command sets are collected from real command constructors; the gate self-test refuses every planted violation; CLAUDE.md carries the D-13 rule and the D-17 note; the wristband gap is logged.
Task 3: The site gets the WinterCMS-style theme, chroma highlighting, search, dark mode and `summer docs:serve`
internal/docsite/highlight.go, internal/docsite/serve.go, internal/docsite/render.go, internal/docsite/emit.go, internal/docsite/theme_test.go, internal/docsite/theme/templates/page.html, internal/docsite/theme/templates/header.html, internal/docsite/theme/templates/sidebar.html, internal/docsite/theme/templates/toc.html, internal/docsite/theme/templates/pager.html, internal/docsite/theme/templates/footer.html, internal/docsite/theme/templates/search.html, internal/docsite/theme/templates/icons.html, internal/docsite/theme/templates/404.html, internal/docsite/theme/assets/site.css, internal/docsite/theme/assets/site.js, internal/docsite/theme/assets/search.js, internal/docsite/theme/assets/theme-init.js, internal/docsite/theme/assets/LICENSE-lucide.txt, internal/docsite/theme/assets/fonts/*, cmd/summer/docs.go, cmd/summer/main_test.go, cmd/summer/docs_test.go, go.mod, go.sum, scripts/check-phase11.1.sh, README.md
- .planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-UI-SPEC.md (whole file: Theme Parts, Layout, Spacing, Typography, Color, Page Anatomy, Interactions, Copywriting, UI Considerations)
- admin/src/styles/main.css lines 21-26 and 78-125 (token values to copy)
- admin/node_modules/@lucide/vue/dist/esm/icons/{sun,moon,monitor,search,menu,x,chevron-left,chevron-right,pencil,file-text,copy,check,info,lightbulb,triangle-alert}.mjs (SVG path data)
- admin/node_modules/@fontsource/dm-sans/files and admin/node_modules/@fontsource/dm-mono/files (the 8 woff2 files), their LICENSE files and 400.css/600.css/400-italic.css (unicode-range values)
- internal/docsite/render.go and internal/docsite/emit.go (plan 11.1-01 renderer and page template data)
- internal/dev/watch.go (fsnotify usage pattern)
- `go doc github.com/alecthomas/chroma/v2` and `go doc github.com/alecthomas/chroma/v2/lexers Get` after adding the module
1. Dependency (D-15): run `go list -m -versions github.com/alecthomas/chroma/v2` and add the newest v2 tag (v2.27.0 at planning time) with `go get`, then `go mod tidy`. If tidy adds any module path other than `github.com/alecthomas/chroma/v2` and `github.com/dlclark/regexp2/v2`, stop and report: D-15 approves only those two. Extend the gate's `--deps` to require chroma/v2 as a direct requirement.
2. `highlight.go`: a goldmark `renderer.NodeRenderer` registered with a priority above the default for `ast.KindFencedCodeBlock`. It emits the UI-SPEC code block: `
`, the `` for `src=` fences (from plan 11.1-01), a copy button element that stays hidden until site.js activates it, and `
`. Tokenise with `lexers.Get(lang)` (fallback: plain escaped text) and map chroma token categories onto spans: keywords to `tok-kw`, YAML/JSON keys (name tags and attributes) to `tok-key`, string literals to `tok-str`, comments to `tok-com`, number literals and YAML booleans to `tok-num`, generic prompts (the `$ ` of `sh` fences) to `tok-prompt`; everything else is escaped plain text. Escape every token value with `html.EscapeString`. Do not use chroma's HTML formatter package or its styles: no inline style attributes, class names only from the UI-SPEC list. The goldmark highlighting extension stays rejected (untagged, stale chroma pin).
3. `render.go`: heading renderer adds `#` to H2/H3; callout transformer turns `> [!NOTE]`, `> [!TIP]`, `> [!WARNING]` into `