Files
summercms/.planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-07-PLAN.md
Jakub Zych 209e44cfd7 docs(11.1): add a resume point after Task 3 to plan 11.1-07
Tasks 1 to 3 record their commits, Deviation: body lines and a
state.record-session line naming PLAN_BASE, so Tasks 4 and 5 can resume
in a fresh context. Their read_first lists now name only the symbols and
helpers they use. The plan stays single and autonomous.
2026-10-01 01:51:47 +02:00

67 KiB

phase, plan, type, wave, depends_on, gap_closure, files_modified, autonomous, requirements, assumption_delta_decision, user_setup, estimate, must_haves
phase plan type wave depends_on gap_closure files_modified autonomous requirements assumption_delta_decision user_setup estimate must_haves
11.1-summercms-documentation-for-humans-and-ai-agents 07 execute 7
11.1-06
true
internal/docsite/fences.go
internal/docsite/render.go
internal/docsite/snippet.go
internal/docsite/docsite.go
internal/docsite/load.go
internal/docsite/check_policy.go
internal/docsite/check_commands.go
internal/docsite/check_identifiers.go
internal/docsite/highlight.go
internal/docsite/fences_test.go
internal/docsite/render_test.go
internal/docsite/highlight_test.go
internal/docsite/checks_test.go
internal/docsite/snippet_test.go
internal/docsite/testdata/violations/
cmd/summer/phase11_1_acceptance_test.go
scripts/check-phase11.1.sh
docs/console/utilities.md
README.md
.planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-VALIDATION.md
.planning/todos/pending/readme-go-fences-src.md
true
DOCS-04
DOCS-05
no-change
tokens raw_tokens tasks confidence
180000 180000 5 low
truths prohibitions artifacts key_links
Per D-07 and DOCS-04, the snippet, go-fence policy and shell-command checks find fences in the goldmark AST that the renderer uses (every *ast.FencedCodeBlock, at any depth), not by scanning lines; a fence goldmark renders is never invisible to the checkers.
A `src=` fence that is not a top-level block of a docs/ page (inside a callout, a plain blockquote or a list item) fails Check, `summer docs:build --check`, TestDocsTree and `summer docs:sync` with `snippet: {ref}: src= code block must be a top-level block of the page, not inside a callout, blockquote or list item`, whatever its body and whether or not its path exists; docs:sync writes nothing while one exists.
Per D-18, a `src=` fence in a module README fails with `snippet: {ref}: module README code blocks are rendered as written; src= is verified only in docs/ pages`, and no built page (guide or API reference) carries a source `<figcaption>` over a fence the snippet checker did not compare: fenceAnnotator captions only top-level `src=` fences of docs/ pages.
Any fence in a docs/ page whose language resolves, through the chroma lexer lookup the highlighter uses, to the Go lexer (go, Go, GO, golang, Golang, main.go) fails with `snippet: {lang} code block has no src= reference` when it has no src=, including inside callouts, blockquotes and list items; go-html-template, go-text-template, text and an empty language do not.
A sh/shell/bash/console fence inside a callout, blockquote or list item is command-checked like a top-level one, and top-level text indented by four or more spaces is an indented code block, not a fence (IN-02).
Per D-07 proof of execution, the reachability roots of a src= helper, type or region in a _test.go file are the Test functions go test runs (cmd/go naming rule) and the Examples that go/doc reports with Output or EmptyOutput, taken only from the test files in the default build; a helper or region reached only from an Example without `// Output:`, or only from a Test in a build-ignored file, fails with the not-run message.
A .go src= target must be compiled by go test ./...: it fails when a path segment is `testdata` or starts with `_`, or when build.ImportDir under build.Default does not list it in GoFiles/CgoFiles (non-test) or TestGoFiles/XTestGoFiles (test), which covers `//go:build ignore` and foreign GOOS/GOARCH file-name suffixes.
Per D-12 and DOCS-05, the identifier checker's go doc fallback runs `go doc -c`, so a wrong-case span such as `demo.Openfromapp` (declared `OpenFromApp`) fails with `identifier: demo.Openfromapp does not exist in modules/demo`.
Per DOCS-05, the command checker finds the command word after leading VAR=value assignments and after flags (cobra stripFlags semantics for a root whose only flag is the bool --help/-h) for the programs `summer`, `go run ./cmd/summer`, `./bin/{app}` and `bin/{app}`; an unknown name in each form fails with `command: "{name}" is not a summer or application command`, and `summer --help` alone is not a command.
Every hole above has a planted fixture in internal/docsite/testdata/violations that produces exactly its own problem; the existing corpus and the clean fixture still pass; the real tree still passes TestDocsTree, `summer docs:build --check` and TestPhase11_1Acceptance, whose independent fence scanner now strips `>` prefixes, resolves Go aliases through chroma and cross-checks built captions; `scripts/check-phase11.1.sh --all` prints `phase11.1 all passed`.
requirement_id category status verification resolution reason statement
DOCS-04 transparency resolved judgment fenceAnnotator captions only the fences checkSnippets compares (top-level src= fences of docs/ pages); nested and README src= fences are refused; the acceptance test cross-checks built captions against an independent scan. A caption that names and links a source file tells readers and agents the code was verified; over an unchecked body it is a false claim. The site must not show a source caption over a code block the snippet checker did not compare with its source.
path provides contains
internal/docsite/fences.go AST fence discovery shared by the snippet, policy and command checks and by docs:sync func collectFences
path provides contains
internal/docsite/snippet.go top-level-only src= verification, README refusal, go/build membership, go/doc Example roots build.ImportDir
path provides contains
internal/docsite/highlight.go goLang: Go-lexer detection through the highlighter's lookup func goLang
path provides contains
internal/docsite/check_commands.go commandWord: env-prefix, flag-first, go run and bin/ command forms func commandWord
path provides contains
internal/docsite/check_identifiers.go case-sensitive go doc fallback "-c"
path provides contains
internal/docsite/testdata/violations/snippet-callout-drift/want.txt CR-01 planted fixture top-level block
path provides contains
cmd/summer/phase11_1_acceptance_test.go independent scanner that sees blockquoted fences and Go aliases; caption cross-check TestAcceptanceFenceScanner
from to via pattern
internal/docsite/render.go internal/docsite/snippet.go fenceAnnotator captions exactly the fence set checkSnippets verifies (top-level src= fences of docs/ pages) Module == ""
from to via pattern
internal/docsite/check_policy.go internal/docsite/highlight.go goLang reuses lexerFor, the renderer's chroma lookup, so policy and highlighting agree on what is Go goLang(
from to via pattern
internal/docsite/snippet.go go/build and go/doc buildPackage (build.ImportDir) file lists and doc.Examples output roots doc.Examples
from to via pattern
internal/docsite/check_identifiers.go go doc -c goDoc argv "doc", "-c"
from to via pattern
scripts/check-phase11.1.sh internal/docsite new tests and fixtures --named DOCSITE_TESTS list and --self-test plants TestNestedFenceChecks
Close the three verification gaps of Phase 11.1 (11.1-VERIFICATION.md `gaps:`), all of which are docs checkers that fail open, and ship the planted-violation fixtures and unit tests for every hole in this same plan (locked at the plan-count checkpoint: one gap-closure plan, tests as its final tasks).
  1. Fence discovery (BLOCKER, CR-01 + WR-04, IN-02): the snippet, go-fence policy and command checks read fences from the goldmark AST; src= fences must be top-level blocks of a docs/ page; module README src= fences are refused and never captioned; any Go-lexer alias needs src=; the acceptance scanner strips > and resolves aliases.
  2. Proof of execution (WR-01 + WR-02): reachability roots are Test functions plus Examples with output; a .go src= target must be in the default build.
  3. Identifier and command forms (WR-03 + WR-05): go doc -c; env-prefix, flag-first, go run ./cmd/summer and bin/{app} command forms.

Purpose: restore the guarantees DOCS-04 ("a test fails on a missing or drifted snippet", D-07) and DOCS-05 ("checkers fail on stale identifiers ... unknown command names", D-12) promise, so go test ./... and summer docs:build --check refuse every form goldmark renders or a reader would copy.

Output: internal/docsite/fences.go, changes to the docsite checkers, 18 new planted fixtures, new unit tests, an updated acceptance test and gate, a sentence in docs/console/utilities.md and the root README, and 11.1-VALIDATION.md rows.

Out of scope (not cited by the verification gaps): WR-06, WR-07, IN-01, IN-03 to IN-08. No phase SPEC.md exists; as a gap-closure plan its edge coverage comes from the VERIFICATION gaps and the REVIEW findings they cite, so the spec-less fallback is not applied. Do not tick DOCS-04 or DOCS-05 in REQUIREMENTS.md; re-verification does that.

<execution_context> @/.claude/gsd-core/workflows/execute-plan.md @/.claude/gsd-core/templates/summary.md </execution_context>

@.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-VERIFICATION.md @.planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-REVIEW.md @.planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-06-SUMMARY.md @internal/docsite/render.go @internal/docsite/snippet.go @internal/docsite/check_policy.go @internal/docsite/check_commands.go @internal/docsite/check_identifiers.go @internal/docsite/violations_test.go

Repository conventions: stdlib testing only in these packages (no testify), t.TempDir() fixtures, t.Fatalf("x = %v, want %v"). goldmark v1.8.6 and chroma/v2 v2.27.0 are already in go.mod; go/ast, go/build, go/doc and go/parser are stdlib: this plan adds no dependency (D-02, D-15) and go.mod/go.sum must not change. go vet ./... and go test ./... stay green at every commit. Commits: one logical change each, code and planning docs in separate commits, no co-author tags. Stage only each task's files (the worktree has unrelated untracked files).

Planted-fixture format (from violations_test.go): each testdata/violations/{case}/ holds only the files that differ from testdata/clean/ (copied over it at the same relative path), an optional remove.txt, and a want.txt with rule: ..., optional file: ..., message: ... (substring). A case passes only when Check reports exactly one problem matching it, and Build refuses the tree. The clean fixture's docs/extras/faq.md ends with a text fence; new plants append after it.

Resume point: this plan has a recorded seam after Task 3 (see <resume_point> after </tasks>). Tasks 1 to 3 each write any deviation into their commit body as a Deviation: ... line. Before starting Task 1, check the seam: when Tasks 1 to 3 are already committed, start at Task 4 and load only what <resume_point> lists instead of the source files above.

Task 1: Tracer: a src= fence inside a callout is found in the goldmark AST and refused end to end, and only verified fences get a source caption internal/docsite/fences.go, internal/docsite/snippet.go, internal/docsite/render.go, internal/docsite/docsite.go, internal/docsite/load.go - internal/docsite/render.go (fenceAnnotator, fence, scanFences, openFence, closesFence, parseRaw, pageKey, pageContext) - internal/docsite/snippet.go (Extract, checkFences, drift, fenceBody, checkSnippets, Sync) - internal/docsite/check_identifiers.go (parsedDoc, parseDocs, lineOf) - internal/docsite/docsite.go (checkContent) and internal/docsite/load.go (assemble, splitFrontmatter, Page.Body, Page.BodyLine, Page.Module) - .planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-REVIEW.md CR-01 and 11.1-VERIFICATION.md gap 1 - internal/docsite/testdata/clean/docs/extras/faq.md and internal/docsite/violations_test.go (fixture format) Wire one path through every layer: AST discovery, the snippet checker, docs:sync, the renderer's caption, and `summer docs:build --check` (per D-07 a snippet the checker cannot compare is refused, never passed).
  1. New file internal/docsite/fences.go with type fenceLine (fields line int, 0-based body line, and text string) and type mdFence with fields info string (the Info segment value, "" when Info is nil), lang string (first field of info), line int (0-based body line of the opening fence: from Info.Segment.Start counted with bytes.Count of newlines; when Info is nil, the line before the first body line; -1 when the fence has neither), topLevel bool (the node's parent is the document, Parent().Kind() == gast.KindDocument), code []fenceLine (one entry per Lines() segment: the line from the segment start and the text from Segment.Value(body) with the trailing newline trimmed; goldmark already strips container markers such as the blockquote > ), and top fence (set only for top-level fences that have an info string). Add collectFences(doc gast.Node, body []byte) ([]mdFence, error) that walks every *gast.FencedCodeBlock of a document parsed with newMarkdown() (the renderer's pipeline, so a fence in a callout has the callout node as parent). For a top-level fence with info, build top from the raw body line at line with openFence (indent, char, count, info), open = line, and close = the line after the last body line (or line+1 for an empty body) when that line exists and closesFence accepts it, otherwise -1 (unterminated). If openFence rejects the opening line of a top-level fence, return an error naming the line: the AST and the line view disagree and that must never be silent (fail closed).
  2. snippet.go: change checkFences to take the collected fences instead of scanning: checkFences(root, file string, lines []string, lineBase int, fences []mdFence) ([]drift, []Problem, error), where lines are the body lines the fences index into. For each fence whose info has src= (ParseSrc): when !topLevel, add a snippet problem at lineBase + line with message {ref}: followed by a new constant nestedSrcMessage = "src= code block must be a top-level block of the page, not inside a callout, blockquote or list item", and do not call Extract (docs:sync cannot rewrite > -prefixed lines). For top-level fences keep the existing logic unchanged on top (unclosed fence, Extract, not found, snippetError, fenceBody comparison, drift). Change checkSnippets to (s *site) checkSnippets(docs []parsedDoc) ([]Problem, error): for every doc with page != nil and page.Module == "" (module READMEs are Task 2), collectFences(d.doc, d.body) then checkFences(s.opts.Root, d.file, lines of d.body, d.line, fences). Remove the checkSnippets call from assemble in load.go and call it from checkContent in docsite.go right after parseDocs, so snippets use the same parse as every other checker.
  3. Sync: per file, splitFrontmatter(raw) gives the body and bodyLine (use the whole file with bodyLine 1 when there is no frontmatter block); parse the body with s.parseRaw(newMarkdown(), body), collectFences, checkFences with lineBase bodyLine; rewrite drifted top-level fences in the body lines (drift offsets are body-relative) and write the unchanged prefix raw[:len(raw)-len(body)] plus the rewritten body. Nested src= fences are problems, so the existing all-or-nothing rule makes Sync write nothing while one exists. Keep the message filter for "body differs from " as today.
  4. render.go fenceAnnotator: set data-src/data-href only when pctx != nil, pctx.page != nil, pctx.page.Module == "" and the fence's parent is the document: the exact set checkSnippets compares, so a source caption can never sit over an unchecked body (CR-01; per D-18 module README fences render as written, without a caption). Commit as fix(11.1-07): find src= fences in the goldmark AST and refuse nested ones. go vet ./... && go test ./internal/docsite ./cmd/summer -count=1 <fails_when>non-zero exit or a "FAIL" line (for example TestCleanFixture, TestPlantedViolations, TestSyncPreservesAndReports or TestDocsTree)</fails_when> d=$(mktemp -d) && cp -r internal/docsite/testdata/clean/. "$d" && go run ./cmd/summer docs:build --root "$d" --check && printf '\n> [!TIP]\n> \140\140\140go src=modules/demo/example_test.go#ExampleHello\n> BOGUS\n> \140\140\140\n' >> "$d/docs/extras/faq.md" && ! go run ./cmd/summer docs:build --root "$d" --check > "$d/check.log" 2>&1 && grep -F 'modules/demo/example_test.go#ExampleHello: src= code block must be a top-level block' "$d/check.log" <fails_when>non-zero exit: the unplanted clean copy is refused, the planted copy passes docs:build --check (the CR-01 reproduction), or check.log has no "src= code block must be a top-level block" line</fails_when> go run ./cmd/summer docs:build --check <fails_when>non-zero exit or no "no problems found" line (the real tree must stay clean)</fails_when> <acceptance_criteria>
    • grep -n 'func collectFences' internal/docsite/fences.go finds one match, and grep -n 'collectFences(' internal/docsite/snippet.go finds at least two (checkSnippets and Sync).
    • grep -n 'Module == ""' internal/docsite/render.go finds the fenceAnnotator condition.
    • The second automated command prints the line docs/extras/faq.md:{n}: snippet: modules/demo/example_test.go#ExampleHello: src= code block must be a top-level block of the page, not inside a callout, blockquote or list item.
    • go test ./internal/docsite -run '^(TestCleanFixture|TestPlantedViolations|TestSyncPreservesAndReports|TestSyncRewritesDrift|TestEditAndSourceURLs)$' -count=1 passes (existing corpus, Sync and caption behaviour unchanged for top-level fences).
    • go run ./cmd/summer docs:build --check prints docs:build: no problems found. </acceptance_criteria> A src= fence goldmark renders inside a callout is refused by Check, docs:build --check and docs:sync, top-level fences are verified and synced exactly as before, and only verified fences get a source caption. Refusing non-top-level src= fences (instead of checking them) is one branch in checkFences; lifting it later only needs Sync to rewrite prefixed lines.
Task 2: Go-fence policy, module README src= refusal and shell-command checks read the same AST fences, and every Go-lexer alias needs src= internal/docsite/highlight.go, internal/docsite/check_policy.go, internal/docsite/snippet.go, internal/docsite/check_commands.go, internal/docsite/render.go, docs/console/utilities.md, README.md - internal/docsite/highlight.go (lexerFor, highlight, shellLangs use) - internal/docsite/check_policy.go (checkPolicy, calloutMarker, headingProblems) - internal/docsite/render.go (calloutTransformer, calloutLine, calloutTypes use, openFence) - internal/docsite/check_commands.go (checkCommands fence loop) - internal/docsite/fences.go and internal/docsite/snippet.go as changed by Task 1 - internal/docsite/checks_test.go TestFencePolicy and TestCommandChecker (expected problem lines that must not change) - docs/console/utilities.md "Documentation commands" section and README.md Documentation paragraph (line 149) - .planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-REVIEW.md WR-04 and IN-02 1. `highlight.go`: add `goLang(lang string) bool`, true when `lexerFor(lang)` (the highlighter's own chroma lookup, per D-15) returns a lexer whose `Config().Name` is "Go". chroma's registry resolves names, aliases, lowercase forms and file names, so go, Go, GO, golang, Golang and main.go are Go, while go-html-template, go-text-template, text and "" are not. 2. `check_policy.go`: build the go-fence rule from `collectFences(d.doc, d.body)` for guide pages (`page.Module == ""`; module READMEs stay exempt per D-18): a fence with `goLang(f.lang)` and no `src=` in its info adds a `snippet` problem at `d.line + f.line` with message `{lang} code block has no src= reference` (the language as written, so the existing go message is unchanged). It applies at every depth: callouts, blockquotes, list items. Replace the raw-line callout check and its fence mask with an AST check: after calloutTransformer, every `*gast.Blockquote` whose first child is a paragraph whose first line matches `calloutLine` with a type not in `calloutTypes` is a `callout` problem at that line with the existing message; remove the `calloutMarker` regex. The heading rule is unchanged. TestFencePolicy's expected lines must still hold. 3. `snippet.go` checkSnippets: include module README pages. On a page with `Module != ""` every `src=` fence, top-level or nested, adds a `snippet` problem `{ref}: ` plus new constant `readmeSrcMessage` = "module README code blocks are rendered as written; src= is verified only in docs/ pages" (D-18 keeps README fences unverified, so a src= there would promise a check that never runs; `.planning/todos/pending/readme-go-fences-src.md` replaces this refusal with a real check later). 4. `check_commands.go`: take shell fences from `collectFences`: for each fence whose `lang` is in `shellLangs`, run `check` on every `code` line at `d.line + line`, so a sh fence inside a callout or list item is checked like a top-level one. Code-span checking is unchanged. 5. `render.go` openFence: accept at most three leading spaces (CommonMark); four or more is an indented code block, as goldmark parses it (IN-02). This keeps rewriteMarkdown and serve's watch list in line with the renderer. 6. Docs (same change as the behaviour, per the CLAUDE.md documentation rule): in docs/console/utilities.md, extend the paragraph that lists what makes `docs:build` fail so it also names a Go code block (including `golang`) without a `src=` reference, a `src=` code block inside a callout, blockquote or list item (src= blocks must be top-level), and a `src=` code block in a module README. In README.md's Documentation paragraph about `summer docs:build`, add that `src=` works on top-level code blocks of `docs/` pages only. Run the docs checker on both files through the real-tree check below. Commit as `fix(11.1-07): check go fences, README src= and shell fences from the AST`. go vet ./... && go test ./internal/docsite ./cmd/summer -count=1 non-zero exit or a "FAIL" line (TestFencePolicy, TestCommandChecker, TestCommandTokenForms, TestPlantedViolations or TestDocsTree regressing) d=$(mktemp -d) && cp -r internal/docsite/testdata/clean/. "$d" && printf '\n\140\140\140golang\nx := 1\n\140\140\140\n' >> "$d/docs/extras/faq.md" && ! go run ./cmd/summer docs:build --root "$d" --check > "$d/check.log" 2>&1 && grep -F 'snippet: golang code block has no src= reference' "$d/check.log" non-zero exit: the golang fence without src= passes docs:build --check (WR-04), or check.log lacks "snippet: golang code block has no src= reference" d=$(mktemp -d) && cp -r internal/docsite/testdata/clean/. "$d" && printf '\n\140\140\140go src=modules/demo/example_test.go#ExampleHello\nfmt.Println(demo.Hello("blog"))\n// Output: Hello, blog\n\140\140\140\n' >> "$d/modules/demo/README.md" && ! go run ./cmd/summer docs:build --root "$d" --check > "$d/check.log" 2>&1 && grep -F 'module README code blocks are rendered as written' "$d/check.log" non-zero exit: a src= fence in a module README passes docs:build --check, or check.log lacks "module README code blocks are rendered as written" go run ./cmd/summer docs:build --check non-zero exit or no "no problems found" line - `grep -n 'func goLang' internal/docsite/highlight.go` finds one match and `grep -n 'goLang(' internal/docsite/check_policy.go` finds the go-fence rule. - `grep -n 'collectFences(' internal/docsite/check_policy.go internal/docsite/check_commands.go` finds a match in each file. - `grep -n 'readmeSrcMessage' internal/docsite/snippet.go` finds the constant and its use. - `grep -n 'top-level' docs/console/utilities.md` and `grep -n 'top-level' README.md` each find a match. - The second and third automated commands print their expected problem lines; `go run ./cmd/summer docs:build --check` prints `docs:build: no problems found`. Go-lexer fences without src= are refused at any depth, README src= fences are refused, shell fences in callouts and lists are command-checked, indented backticks are not fences, and the docs page and README describe the rules. The README refusal is one branch in checkSnippets that the pending README todo replaces with a check. Task 3: Proof of execution and name forms: roots are what go test runs, .go src= targets are in the default build, go doc -c, and every summer and app command form is parsed internal/docsite/snippet.go, internal/docsite/check_identifiers.go, internal/docsite/check_commands.go, docs/console/utilities.md - internal/docsite/snippet.go (Extract, checkRootModule, extractIdent, exampleHasOutput, testGraph, isRoot, loadTestGraph, notRunMessage, checkTestIdent, checkTestRegion) - internal/docsite/check_identifiers.go (goDoc, goDocIdent) - internal/docsite/check_commands.go (commandToken, commandSeparators, checkCommands check closure) - internal/docsite/snippet_test.go TestSnippetGenericsAndGroups and TestSnippetTestGraph, internal/docsite/docsite_test.go TestSnippetConfinement (messages that must keep matching, including "has no // Output: comment") - internal/docsite/checks_test.go TestCommandTokenForms and TestCommandChecker (forms that must keep passing: `summer --version`, `summer --help`, `go install ./cmd/summer`, `echo summer x`, comment lines) - the vendored cobra `stripFlags` in the module cache (`go list -m -f '{{.Dir}}' github.com/spf13/cobra`, command.go) for the flag rule to mirror - .planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-REVIEW.md WR-01, WR-02, WR-03, WR-05 Proof of execution (D-07, gap 2): 1. `snippet.go`: add `buildPackage(dir string) (*build.Package, error)` wrapping `build.ImportDir(dir, 0)` on `build.Default` (host GOOS/GOARCH, CGO_ENABLED and release tags: the build `go test ./...` runs). In `Extract`, for a `.go` target after `checkRootModule`: refuse a repo-relative path with a segment named `testdata` or starting with `_` with snippetError "file is in a directory go test ./... skips (testdata or a _-prefixed directory)" (non-Go targets such as YAML under testdata stay allowed); then call buildPackage: a `*build.NoGoError`, or the file's base name missing from GoFiles plus CgoFiles (non-test file) or from TestGoFiles plus XTestGoFiles (`_test.go` file), gives snippetError "file is excluded from the default build (build constraint or GOOS/GOARCH file-name suffix)"; any other error gives snippetError "cannot load package: " plus `firstLine(err.Error())`. For a non-test file, "package has no _test.go files, so go test does not cover it" is now decided by empty TestGoFiles and XTestGoFiles instead of a glob. 2. `loadTestGraph`: take the file list from buildPackage's TestGoFiles plus XTestGoFiles (so a build-ignored test file contributes nothing), parse each with `parser.ParseComments`, and keep the parse error message format ("cannot parse {base}: ..."). Replace `isRoot` with roots = functions without a receiver whose name passes a new `isTestName(name string) bool` (the cmd/go rule: "Test" alone, or "Test" followed by a rune that is not lowercase) plus the Examples `doc.Examples(files...)` reports with `Output != ""` or `EmptyOutput` (key "Example"+ex.Name). Delete `isRoot`. 3. `extractIdent`: for an Example function (no receiver, name starting "Example") in a test file, run the existing `// Output:` check first and skip `checkTestIdent` (an Example with output is a root), so the "has no // Output: comment" message stays; every other func, type, var and const keeps `checkTestIdent`. Reword `notRunMessage` to "fragment is not inside a Test or Example function (an Example counts only with an // Output: comment), or a function one of them calls, so go test does not run it" (the substring the committed want.txt files match is unchanged).

Identifier and command forms (D-12, DOCS-05, gap 3): 4. check_identifiers.go goDoc: run go doc -c ./{dir} {query} (argv only, no shell; goDocIdent still gates the query), so matching is case-sensitive (WR-03). 5. check_commands.go: replace the commandToken regex with commandWord(cmd string) (name string, tool bool, ok bool) over strings.Fields of the trimmed command: drop a leading "$" prompt token; skip leading assignment tokens (a name of letters, digits and underscores not starting with a digit, then "="); the program is summer (tool), the three tokens go run ./cmd/summer (tool), or ./bin/{app} or bin/{app} with app in [A-Za-z0-9._-]+ (application); anything else is not a command (ok false). After the program, find the command word the way cobra's stripFlags does for a root whose only flag is the bool --help/-h: "--" ends the search with no word; a token starting with "-" that contains "=" is skipped alone; "--help" and "-h" are skipped alone; any other "--name" or two-character "-x" also consumes the next token, and when only that value remains there is no word; other "-" tokens are skipped alone; the first remaining token is the word. checkCommands uses it (tool words against the tool set, application words against the app set) with the existing separators and message. 6. docs/console/utilities.md: add one sentence to the same paragraph: a src= target must be code go test ./... compiles and runs, that is a file in the default build, inside a Test function or an Example with an // Output: comment, or a function one of them calls. Commit as fix(11.1-07): require built, run src= code; case-sensitive go doc; parse command forms.

  1. Resume point (after the commit): record progress as <resume_point> "Recording" describes (the three task subjects and their Deviation: body lines are in git; state.record-session names PLAN_BASE and Task 4), then go straight on to Task 4 in the same run. This is not a checkpoint: the executor does not stop and the plan stays autonomous. go vet ./... && go test ./internal/docsite ./cmd/summer -count=1 <fails_when>non-zero exit or a "FAIL" line (TestSnippetGenericsAndGroups, TestSnippetTestGraph, TestSnippetConfinement, TestIdentifierGoDocFallback, TestCommandTokenForms, TestPlantedViolations or TestDocsTree regressing)</fails_when> d=$(mktemp -d) && cp -r internal/docsite/testdata/clean/. "$d" && printf '\n\140\140\140sh\nFOO=1 summer no:such\n\140\140\140\n' >> "$d/docs/extras/faq.md" && ! go run ./cmd/summer docs:build --root "$d" --check > "$d/check.log" 2>&1 && grep -F '"no:such" is not a summer or application command' "$d/check.log" <fails_when>non-zero exit: the env-prefixed unknown command passes (WR-05), or check.log lacks the command problem line</fails_when> d=$(mktemp -d) && cp -r internal/docsite/testdata/clean/. "$d" && printf 'package demo\n\n// OpenFromApp is a fixture.\nfunc OpenFromApp() {}\n' > "$d/modules/demo/open.go" && printf '\nCall \140demo.Openfromapp\140.\n' >> "$d/docs/extras/faq.md" && ! go run ./cmd/summer docs:build --root "$d" --check > "$d/check.log" 2>&1 && grep -F 'identifier: demo.Openfromapp does not exist in modules/demo' "$d/check.log" <fails_when>non-zero exit: the wrong-case identifier passes through the go doc fallback (WR-03), or check.log lacks the identifier problem line</fails_when> d=$(mktemp -d) && cp -r internal/docsite/testdata/clean/. "$d" && printf '//go:build ignore\n\npackage demo\n\n// Broken is never built.\nfunc Broken() { undefinedCall() }\n' > "$d/modules/demo/broken.go" && printf '\n\140\140\140go src=modules/demo/broken.go#Broken\n// Broken is never built.\nfunc Broken() { undefinedCall() }\n\140\140\140\n' >> "$d/docs/extras/faq.md" && ! go run ./cmd/summer docs:build --root "$d" --check > "$d/check.log" 2>&1 && grep -F 'file is excluded from the default build' "$d/check.log" <fails_when>non-zero exit: a //go:build ignore source passes as compiled (WR-02), or check.log lacks "file is excluded from the default build"</fails_when> go run ./cmd/summer docs:build --check <fails_when>non-zero exit or no "no problems found" line (a real src= target or command span now refused must be fixed at its source in this task)</fails_when> <acceptance_criteria>
    • grep -n 'build.ImportDir' internal/docsite/snippet.go and grep -n 'doc.Examples(' internal/docsite/snippet.go each find a match; grep -n 'func isTestName' internal/docsite/snippet.go finds one.
    • grep -n '"doc", "-c"' internal/docsite/check_identifiers.go finds the goDoc argv.
    • grep -n 'func commandWord' internal/docsite/check_commands.go finds one match.
    • go test ./internal/docsite -run '^(TestSnippetGenericsAndGroups|TestSnippetTestGraph|TestSnippetConfinement|TestSnippetForms|TestIdentifierGoDocFallback|TestCommandTokenForms|TestCommandChecker)$' -count=1 passes.
    • Each of the three planted commands prints its expected problem line, and go run ./cmd/summer docs:build --check prints docs:build: no problems found.
    • Resume point recorded: git log --format=%s -E --grep='^fix\(11\.1-07\): ' lists the Task 1, Task 2 and Task 3 subjects, grep -n 'resume at Task 4' .planning/STATE.md finds the stopped-at line, and 11.1-07-SUMMARY.md does not exist yet. </acceptance_criteria> Only code go test compiles and runs counts as verified, identifier spans must match case exactly, and env-prefix, flag-first, go run and bin/ command forms are checked; the resume point after Task 3 is recorded.
Task 4: Unit tests (1/2): a planted fixture for every hole plus branch tests for fences, captions, Go aliases, roots, build membership, go doc -c and command words internal/docsite/testdata/violations/, internal/docsite/fences_test.go, internal/docsite/render_test.go, internal/docsite/highlight_test.go, internal/docsite/checks_test.go, internal/docsite/snippet_test.go - internal/docsite/violations_test.go (plantCase, parseWant, assertOneProblem, TestCleanFixture) - internal/docsite/testdata/clean/ (docs/extras/faq.md, modules/demo/demo.go, demo_test.go, example_test.go, README.md) - internal/docsite/testdata/violations/snippet-unrun-func/ and go-fence-no-src/ (overlay examples) - the named test helpers only, each located with `grep -n 'func {name}('` and read by line range, not the whole file: internal/docsite/docsite_test.go (page, fixtureCommands, snippetTree, writeTree), checks_test.go (checkFixture, assertProblems, writeFile), render_test.go (renderFixture, renderHTML), snippet_test.go (genericTree) - internal/docsite/fences.go (whole file, new in Task 1) - from the other files Tasks 1 to 3 changed, only the symbols these tests call or match, located with `grep -n` and read by range: snippet.go (nestedSrcMessage, readmeSrcMessage, notRunMessage, buildPackage, isTestName, loadTestGraph, Sync), highlight.go (goLang), check_commands.go (commandWord), check_identifiers.go (the identIndex methods goDoc and checkSpan), render.go (openFence). "Artifacts this phase produces" lists their signatures; check_policy.go, REVIEW.md and VERIFICATION.md are not needed (the fixtures below state every expected message) - Each new violation case yields exactly one problem with its own rule and message, and Build writes nothing. - collectFences reports top-level, callout, blockquote and list-item fences with correct lines, topLevel flags, `>`-free code text and top open/close; four-space-indented backticks are not collected. - A guide page renders a caption for a top-level src= fence and none for a nested one; a module README page renders none. - goLang is true for go, Go, GO, golang, Golang, main.go and false for go-html-template, go-text-template, text, yaml, sh and "". - Roots: Test, Test_x and TestMain are roots, Testable is not; an Example with Output or EmptyOutput is a root, one without is not; a Test in a //go:build ignore test file is not. - commandWord table cases return the expected name, tool flag and ok. 1. Planted fixtures, each a new directory under `internal/docsite/testdata/violations/` in the existing overlay format with a `want.txt` (`rule`, `file`, `message` substring). Plants in `docs/extras/faq.md` are the clean faq.md plus the lines described; a "backtick fence" is opened and closed with three backticks. Gap 1 (fence discovery): - `snippet-callout-drift`: a `> [!TIP]` callout holding a backtick fence with info `go src=modules/demo/example_test.go#ExampleHello` and body BOGUS, every line prefixed `> `. want: rule snippet, file docs/extras/faq.md, message `modules/demo/example_test.go#ExampleHello: src= code block must be a top-level block`. - `snippet-callout-missing`: a `> [!NOTE]` callout holding a fence `go src=modules/demo/missing_test.go#ExampleNope` with body `fmt.Println()`. message `modules/demo/missing_test.go#ExampleNope: src= code block must be a top-level block`. - `snippet-blockquote`: a plain blockquote (no callout marker) holding a fence `yaml src=config/app.yaml` with the file's exact content. message `config/app.yaml: src= code block must be a top-level block`. - `snippet-list-item`: a list item "- Greet:" followed by a two-space-indented fence `go src=modules/demo/example_test.go#ExampleHello` with the correct body. message `modules/demo/example_test.go#ExampleHello: src= code block must be a top-level block`. - `go-fence-callout-no-src`: a `> [!NOTE]` callout holding a fence with info `go` and body `x := 1`. message `go code block has no src= reference`. - `go-fence-golang`: a top-level fence with info `golang` and body `x := 1`. message `golang code block has no src= reference`. - `snippet-readme-src`: `modules/demo/README.md` = the clean README plus a top-level fence `go src=modules/demo/example_test.go#ExampleHello` with the correct body. want: rule snippet, file modules/demo/README.md, message `modules/demo/example_test.go#ExampleHello: module README code blocks are rendered as written`. - `command-in-callout`: a `> [!NOTE]` callout holding a `sh` fence with `summer no:such`. want: rule command, message `"no:such" is not a summer or application command`. Gap 2 (proof of execution): - `snippet-example-no-output-helper`: `modules/demo/never_test.go` (package demo_test) with `func Example_neverRun() { neverRunHelper() }` without an output comment and `func neverRunHelper() {}`; faq.md fence `go src=modules/demo/never_test.go#neverRunHelper` with body `func neverRunHelper() {}`. message `fragment is not inside a Test or Example function`. - `snippet-example-no-output-region`: the same never_test.go with a `// docs:start never` / `// docs:end never` region inside neverRunHelper; fence `go src=modules/demo/never_test.go#never` with the region's body. Same message. - `snippet-build-ignored`: `modules/demo/broken.go` with a `//go:build ignore` line, `package demo`, and `// Broken is never built.` over `func Broken() { undefinedCall() }`; fence `go src=modules/demo/broken.go#Broken` with that exact declaration. message `modules/demo/broken.go#Broken: file is excluded from the default build`. - `snippet-build-ignored-test-root`: `modules/demo/demo_test.go` = the clean file plus `func ignoredOnly() string { return "x" }`, and `modules/demo/ignored_test.go` with `//go:build ignore`, package demo, and `func TestIgnored(t *testing.T) { _ = ignoredOnly() }`; fence `go src=modules/demo/demo_test.go#ignoredOnly` with that declaration. message `fragment is not inside a Test or Example function`. - `snippet-testdata-go`: `modules/demo/testdata/fixture_test.go` (package demo) with a Test holding a `td` region; fence `go src=modules/demo/testdata/fixture_test.go#td`. message `file is in a directory go test ./... skips`. Gap 3 (names): - `identifier-wrong-case`: `modules/demo/open.go` (package demo, `// OpenFromApp is a fixture.` over `func OpenFromApp() {}`) and faq.md text "Call `demo.Openfromapp`." want: rule identifier, message `demo.Openfromapp does not exist in modules/demo`. - `command-env-prefix` (`FOO=1 summer no:such`), `command-flag-first` (`summer --root . no:such`), `command-go-run` (`go run ./cmd/summer no:such`), `command-bin-no-dot` (`bin/demo no:such`): each a top-level `sh` fence in faq.md; want: rule command, message `"no:such" is not a summer or application command`. All Go fixture files sit under internal/docsite/testdata, which the go tool ignores; write each so it would compile if built (except broken.go, whose point is that it is never built). No fixture names a consuming application. 2. Unit tests (stdlib testing, table-driven where natural): - `internal/docsite/fences_test.go` `TestCollectFences`: one body with a top-level closed fence, a top-level unterminated fence at EOF (top.close -1), a fence in a callout, in a plain blockquote and in a list item (topLevel false, code text without `> ` or list indentation, correct lines), a fence without info (info "", line from the body), and four-space-indented backticks (not collected); plus `openFence` accepting three leading spaces and rejecting four. - `render_test.go` `TestFenceCaptionsOnlyVerified`: via renderFixture, a guide page's top-level src= fence has a `
`, a src= fence inside a callout on the same page has none, and a module README page's top-level src= fence has none. - `highlight_test.go` `TestGoLang`: the true and false cases in the behavior list. - `checks_test.go` `TestNestedFenceChecks`: one checkFixture tree whose docs page has a callout go fence without src=, a list-item go fence without src=, a `GO` fence and a `main.go` fence, four-space-indented backticks holding `go` (no problem), a callout sh fence with `summer bogus:nested`, and a blockquote src= fence, and whose module README has a go fence without src= (no problem, D-18) and a src= fence; assert the exact problem lines (file:line: rule: message). - `checks_test.go` `TestIdentifierGoDocCaseSensitive`: a writeTree module declaring `OpenFromApp` and `Outer` with a method `HelloWorld`; `goDoc` accepts `OpenFromApp` and `Outer.HelloWorld`, refuses `Openfromapp` and `Outer.Helloworld`; `checkSpan` reports `forms.Openfromapp does not exist in modules/forms` (use the module name the tree declares). - `checks_test.go` `TestCommandWord`: table over `commandWord` with at least: `summer docs:build`; `$ summer docs:build --out site`; `FOO=1 summer no:such`; `FOO=1 BAR=x ./bin/acme serve`; `summer --root . no:such`; `summer --root=. docs:build`; `summer -r . docs:build`; `summer --help` (no word); `summer --help docs:build`; `summer -h docs:build`; `summer --root` (no word); `summer --root .` (no word); `summer -- docs:build` (no word); `go run ./cmd/summer no:such`; `go run ./cmd/summer --root . docs:build`; `bin/acme migrate`; `./bin/acme migrate`; `echo summer x`; `# summer x`; `go test ./modules/demo`; `go install ./cmd/summer`; `summer`; `FOO=1`; `summer --version` (no word). - `snippet_test.go` `TestSnippetRootsAndBuild`: in writeTree packages, Test, Test_x and TestMain are roots and a helper called only from `Testable` is refused; an Example with `// Output:` and one with an empty `// Output:` make their helpers reachable, one without output does not; a `//go:build ignore` non-test target and test target are refused as excluded; a file named `only_{goos}.go` for a GOOS other than runtime.GOOS is refused as excluded; `.go` targets under `testdata/` and `_old/` are refused as skipped directories while a YAML target under `testdata/` is still extracted; a helper called only from a Test in a build-ignored test file is refused as not run. - `snippet_test.go` `TestSyncParsedFences`: Sync on a page with frontmatter rewrites a drifted top-level fence and keeps the frontmatter and a callout's text byte for byte; Sync on a page with a src= fence inside a callout returns the nested problem and leaves every file unchanged. 3. If an existing unit-test tree relied on an Example without output as a root, that tree encoded the WR-01 hole: add the output comment to the fixture rather than weakening the rule, and list the change in the SUMMARY. Fix any production defect these tests expose in the same commit as its test, and list each fix in the SUMMARY. 4. Mutation evidence (by hand once, recorded in the SUMMARY): in a scratch copy, removing the nested branch of checkFences turns `TestPlantedViolations/snippet-callout-drift` red; dropping `-c` from goDoc turns `TestPlantedViolations/identifier-wrong-case` red; treating every Example as a root again turns `TestPlantedViolations/snippet-example-no-output-helper` red. Commit as `test(11.1-07): planted fixtures and unit tests for fence, execution and name-form holes`. go vet ./... && go test ./internal/docsite -run '^(TestPlantedViolations|TestCleanFixture|TestCollectFences|TestFenceCaptionsOnlyVerified|TestGoLang|TestNestedFenceChecks|TestIdentifierGoDocCaseSensitive|TestCommandWord|TestSnippetRootsAndBuild|TestSyncParsedFences)$' -count=1 -v non-zero exit, a "--- FAIL" line, or "no tests to run" go test ./internal/docsite -count=1 -coverprofile="${TMPDIR:-/tmp}/docsite.cover" && go tool cover -func="${TMPDIR:-/tmp}/docsite.cover" | awk '/^total:/ { sub("%", "", $3); if ($3 + 0 < 85.0) { print "coverage " $3 "% below 85%"; exit 1 } }' non-zero exit, a "FAIL" line, or a "coverage ... below 85%" line - `ls -d internal/docsite/testdata/violations/*/ | wc -l` prints at least 81 (63 existing plus 18 new). - `go test ./internal/docsite -run '^TestPlantedViolations$' -count=1 -v | grep -cE -- '--- PASS: TestPlantedViolations/(snippet-callout-drift|snippet-callout-missing|snippet-blockquote|snippet-list-item|go-fence-callout-no-src|go-fence-golang|snippet-readme-src|command-in-callout|snippet-example-no-output-helper|snippet-example-no-output-region|snippet-build-ignored|snippet-build-ignored-test-root|snippet-testdata-go|identifier-wrong-case|command-env-prefix|command-flag-first|command-go-run|command-bin-no-dot) '` prints 18. - `go test ./internal/docsite -run '^(TestCollectFences|TestFenceCaptionsOnlyVerified|TestGoLang|TestNestedFenceChecks|TestIdentifierGoDocCaseSensitive|TestCommandWord|TestSnippetRootsAndBuild|TestSyncParsedFences)$' -count=1 -v | grep -c -- '^--- PASS'` prints 8. - `grep -rniE 'fonoteka|p(l|ł)ytarium' internal/docsite/testdata internal/docsite/fences_test.go` finds nothing (exit 1). - `go test -cover ./internal/docsite -count=1` reports at least 85.0%. - The SUMMARY records the three mutation checks from action step 4 and their red test names. Every hole in the three gaps is pinned by a fixture that fails for its own rule and by branch-level unit tests, the old corpus still passes, and coverage stays at or above 85%. Task 5: Unit tests (2/2): the acceptance scanner sees blockquoted fences and Go aliases, the gate plants every hole, and VALIDATION records plan 07 cmd/summer/phase11_1_acceptance_test.go, scripts/check-phase11.1.sh, .planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-VALIDATION.md, .planning/todos/pending/readme-go-fences-src.md - cmd/summer/phase11_1_acceptance_test.go (TestPhase11_1Acceptance SC4 and SC5, docFence, pageFences, docsGoFences, buildRealTree output `out`) - scripts/check-phase11.1.sh (run_self_test, expect_refusal, restore, DOCSITE_TESTS, SUMMER_TESTS, run_named, --all) - .planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-VALIDATION.md (frontmatter and the Per-Task Verification Map section only) - .planning/todos/pending/readme-go-fences-src.md - no docsite source file and not 11.1-VERIFICATION.md: action step 1 states every scanner requirement from gap 1, the acceptance scanner must not share docsite's code, and each VALIDATION row's command is the one in that task's `` above The Docker daemon is reachable (`docker info` exits 0): the gate's --named and --go stages run the walkthrough's Postgres tests without -short. 1. `cmd/summer/phase11_1_acceptance_test.go` (the independent scanner must not share docsite's code or its old blind spot): - Split `pageFences` into a content scanner `scanDocFences(rel, content string) []docFence` that pageFences calls. Before fence detection, strip from each line any run of leading spaces and `>` container markers (each `>` optionally followed by one space); add `nested bool` to `docFence`, true when a `>` was stripped or the opening line was indented. Body lines are stripped the same way, and the closing fence is detected on stripped lines. - Add `goFenceLang(lang string) bool` using chroma directly (`lexers.Get(lang)` and `Config().Name == "Go"`, the same registry the renderer uses but not docsite's helper); `docsGoFences` keeps every fence for which it is true. - SC4 additionally asserts: no fence with src= under docs/ is nested; no module README (every `modules/**/README.md`) has a src= fence; and in the built site `out`, each guide page's HTML has exactly as many `
` elements as the scanner found top-level src= fences in its source, and every `api/*.html` has none. - New test `TestAcceptanceFenceScanner` feeding `scanDocFences` a body with a `> [!TIP]` src= fence, a plain blockquote go fence, a `golang` fence and a top-level src= fence, asserting the nested flags, the src values and that goFenceLang accepts go and golang and rejects go-html-template and text. 2. `scripts/check-phase11.1.sh`: - `run_self_test`: after the existing plants, add expect_refusal plants on the real-tree scratch copy, each restored afterwards: a `> [!TIP]` callout holding a fence `go src=modules/bonfire/example_test.go#ExampleCall` with body BOGUS (want `snippet: modules/bonfire/example_test.go#ExampleCall: src= code block must be a top-level block`); a top-level `golang` fence without src= (want `snippet: golang code block has no src= reference`); an sh fence `FOO=1 summer no:such` and one `summer --root . no:such` (want `"no:such" is not a summer or application command`); and the text "See `lagoon.Openfromapp`." (want `identifier: lagoon.Openfromapp does not exist in modules/lagoon`). Build every backtick plant with printf as the existing go-fence plant does. - Append to DOCSITE_TESTS: TestCollectFences TestFenceCaptionsOnlyVerified TestGoLang TestNestedFenceChecks TestIdentifierGoDocCaseSensitive TestCommandWord TestSnippetRootsAndBuild TestSyncParsedFences; append TestAcceptanceFenceScanner to SUMMER_TESTS. - Once by hand, rename TestCommandWord in a scratch copy and confirm `--named` refuses; record it in the SUMMARY. 3. Planning docs (a separate `docs(11.1-07): ...` commit, never mixed with code): - 11.1-VALIDATION.md: add rows 11.1-07-T1 to 11.1-07-T5 to the Per-Task Verification Map with requirement (DOCS-04 or DOCS-05), behaviour, test type, the exact automated command from each task's verify, File Exists and a green status after running it; mention plan 07 in the map's intro sentence; keep `status: validated` and `nyquist_compliant: true`. - `.planning/todos/pending/readme-go-fences-src.md`: add a paragraph saying that since 11.1-07 checkSnippets refuses src= in module READMEs (readmeSrcMessage) and fenceAnnotator captions only docs/ pages, so converting README fences means replacing that refusal with checkFences over README pages, captioning their top-level src= fences, and extending Sync to README files. 4. Run `scripts/check-phase11.1.sh --all` and fix any failure at its source. Commit code as `test(11.1-07): acceptance scanner sees blockquoted and aliased fences; gate plants the holes`. go vet ./... && go test ./cmd/summer -run '^(TestPhase11_1Acceptance|TestAcceptanceFenceScanner)$' -count=1 -v non-zero exit, a "--- FAIL" line, fewer than five "--- PASS: TestPhase11_1Acceptance/SC" lines, no "--- PASS: TestAcceptanceFenceScanner" line, or "no tests to run" bash -n scripts/check-phase11.1.sh && scripts/check-phase11.1.sh --self-test && scripts/check-phase11.1.sh --all non-zero exit, a line starting "refuse:", or no "phase11.1 all passed" line - `go test ./cmd/summer -run '^TestPhase11_1Acceptance$' -count=1 -v | grep -c -- '--- PASS: TestPhase11_1Acceptance/SC'` prints 5. - `grep -n 'func scanDocFences' cmd/summer/phase11_1_acceptance_test.go` and `grep -n 'func TestAcceptanceFenceScanner' cmd/summer/phase11_1_acceptance_test.go` each find one match. - `grep -c 'top-level block' scripts/check-phase11.1.sh` prints at least 1, and `grep -n 'TestNestedFenceChecks' scripts/check-phase11.1.sh` finds the DOCSITE_TESTS entry. - `scripts/check-phase11.1.sh --all` prints `phase11.1 all passed`. - `grep -c '11.1-07-T' .planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-VALIDATION.md` prints at least 5, and `grep -n '^status: validated$' .planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-VALIDATION.md` finds a match. - `grep -n '11.1-07' .planning/todos/pending/readme-go-fences-src.md` finds the added paragraph. - `scripts/check-phase11.1.sh --deps` passes, and `git diff --exit-code "$PLAN_BASE" HEAD -- go.mod go.sum` exits 0, where PLAN_BASE is the sha `git rev-parse HEAD` printed before Task 1's first commit (recorded at the resume point, derivable per ``, and named in the SUMMARY): this plan adds no dependency. The acceptance test's own scanner would catch every CR-01/WR-04 form and cross-checks captions, the gate plants each hole and requires every new test by name, and VALIDATION and the README todo reflect plan 07.

<resume_point after="Task 3"> Tasks 1 to 3 change production code and docs; Tasks 4 and 5 add only fixtures, tests, the gate and planning docs. That seam lets Tasks 4 and 5 finish in a fresh context when the 180k-token run does not fit one, without splitting the plan (locked: one gap-closure plan) and without a checkpoint (the executor does not stop at the seam; the plan stays autonomous).

Recording (Task 3 step 7, after the Task 3 commit):

  1. The git log is the progress record. Tasks 1 to 3 commit with the exact subjects their actions name, so git log --format='%h %s' -E --grep='^fix\(11\.1-07\): ' lists them. Each of those commits carries in its body one Deviation: ... line per deviation (a production defect fixed, a real-tree page or source changed to pass a new check, an existing test fixture corrected), so the SUMMARY can be written without the context that made them.
  2. Run gsd_run query state.record-session --stopped-at "11.1-07 Tasks 1-3 committed (PLAN_BASE {sha}); resume at Task 4" --resume-file ".planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-07-PLAN.md", where {sha} is the PLAN_BASE printed before Task 1. Do not stage STATE.md here; the plan's close-out metadata commit carries it. Do not create 11.1-07-SUMMARY.md at the seam: a SUMMARY on disk marks the plan complete to execute-phase.
  3. Continue with Task 4 in the same run.

Resuming (a fresh executor on this plan, spawned with <completed_tasks> or re-dispatched):

  1. Before Task 1, run the git log command from Recording step 1. When it lists the Task 1, 2 and 3 subjects, do not redo or revert those tasks: start at Task 4. When it lists fewer, the seam was not reached: start at the first task whose subject is missing, after checking git status for that task's uncommitted edits.
  2. Confirm the committed half once before Task 4: go vet ./... && go test ./internal/docsite ./cmd/summer -count=1 passes and go run ./cmd/summer docs:build --check prints docs:build: no problems found. Fix a failure at its source in Task 4's commit and record it as a deviation.
  3. PLAN_BASE comes from STATE.md's stopped-at line; if that line has been overwritten, it is the parent of Task 1's commit: git rev-parse "$(git log --format=%H -F --grep='fix(11.1-07): find src= fences in the goldmark AST' -1)^".
  4. Load only STATE.md, 11.1-CONTEXT.md (D-07, D-12, D-15, D-18), this plan and the <read_first> entries of Tasks 4 and 5. Skip the source files, REVIEW.md, VERIFICATION.md and 11.1-06-SUMMARY.md listed in <context>; "Artifacts this phase produces" lists every signature Tasks 4 and 5 call.
  5. Build the SUMMARY's Tasks 1 to 3 entries (commits, deviations) from git log --format='%h %s%n%b' -E --grep='^fix\(11\.1-07\): '. </resume_point>

<assumption_delta_decision> The assumption-delta detector fired on two pluralization cues ("another", "also") in the ROADMAP success-criteria prose, not on a model change. This plan tightens checker rules; no identity noun changes (a page, a fence, a src= reference and a module stay what they were). Decision: no-change. </assumption_delta_decision>

<threat_model>

Trust Boundaries

Boundary Description
docs/ pages and module READMEs → built site and llms outputs Markdown authored by developers and agents becomes published "verified" documentation
docs checkers → go test / docs:build sign-off A green check is the evidence that shown code compiles, runs and names real APIs
code-span text → go doc argv A page's span text becomes an argument of a subprocess

STRIDE Threat Register

Threat ID Category Component Severity Disposition Mitigation Plan
T-11.1-20 Repudiation (false "verified source" caption) internal/docsite/render.go fenceAnnotator, snippet.go checkSnippets high mitigate Captions only on top-level src= fences of docs/ pages, the set checkSnippets compares; nested and module README src= fences refused by Check, docs:build and docs:sync; fixtures snippet-callout-drift, snippet-callout-missing, snippet-blockquote, snippet-list-item, snippet-readme-src; SC4 caption cross-check in the built site
T-11.1-21 Repudiation (unverified Go published as an example) internal/docsite/check_policy.go go-fence rule medium mitigate AST discovery at every depth; goLang through the highlighter's chroma lookup; fixtures go-fence-golang and go-fence-callout-no-src; acceptance scanner strips > and resolves aliases independently
T-11.1-22 Repudiation (code never compiled or run shown as verified) internal/docsite/snippet.go Extract, loadTestGraph medium mitigate build.ImportDir membership under build.Default; testdata and _-prefixed Go targets refused; roots are Test functions and Examples with output from built test files; fixtures snippet-example-no-output-helper, -region, snippet-build-ignored, snippet-build-ignored-test-root, snippet-testdata-go
T-11.1-23 Repudiation (stale identifier or command names pass) internal/docsite/check_identifiers.go goDoc, check_commands.go commandWord medium mitigate go doc -c; commandWord mirrors cobra stripFlags and covers env prefixes, go run ./cmd/summer and bin/{app}; fixtures identifier-wrong-case and command-env-prefix, -flag-first, -go-run, -bin-no-dot, command-in-callout; gate self-test plants
T-11.1-24 Tampering (argument injection into go doc) internal/docsite/check_identifiers.go goDoc low mitigate -c is a constant argv element; the query is still gated by goDocIdent (an identifier or Ident.Member), so a span cannot add flags; exec without a shell, 30 s timeout unchanged
T-11.1-25 Denial of service (a src= path makes the checker load arbitrary directories) internal/docsite/snippet.go buildPackage low accept build.ImportDir runs only on the directory of a src= path already confined to the repository root and root module (checkRefPath, EvalSymlinks, checkRootModule); it reads file headers and starts no process
T-11.1-SC Tampering go module installs high accept This plan adds no module or package; go.mod and go.sum are unchanged (gate --deps)
</threat_model>
- `go vet ./... && go test ./internal/docsite ./cmd/summer -count=1` green after every task. - `go run ./cmd/summer docs:build --check` prints `docs:build: no problems found` on the real tree. - The CR-01, WR-04, WR-02, WR-03 and WR-05 reproductions from 11.1-VERIFICATION.md are refused (Tasks 1 to 3 planted commands; Task 4 fixtures; Task 5 gate plants). - `scripts/check-phase11.1.sh --all` prints `phase11.1 all passed` (Docker available). - `go test -cover ./internal/docsite` at least 85%.

<success_criteria>

  • Gap 1 closed: fences for the snippet, policy and command checks come from the goldmark AST; nested and README src= fences are refused and never captioned; Go-lexer aliases need src=; the acceptance scanner strips > and resolves aliases; fixtures for the callout drift, callout missing file, callout go fence without src= and golang fence exist and pass.
  • Gap 2 closed: roots are Test functions plus Examples with Output or EmptyOutput; .go src= targets are in the default build per go/build ImportDir; fixtures for an Example without Output and a //go:build ignore file exist and pass.
  • Gap 3 closed: go doc runs with -c; env-prefix, go run ./cmd/summer, flag-first and bin/{app} command forms are parsed; fixtures for a wrong-case identifier and each command form exist and pass.
  • DOCS-04 and DOCS-05 enforcement restored with the real tree still clean. </success_criteria>

Source audit

Source ID Item Task Status
GOAL — "Every code example compiles and is tested" (enforcement, not just content) 1-5 COVERED
REQ DOCS-04 Every Go fence src=; a test fails on missing or drifted snippet; Examples carry Output and run 1, 2, 3, 4, 5 COVERED
REQ DOCS-05 Checkers fail on stale identifiers and unknown command names 3, 4, 5 COVERED
REQ DOCS-01..03, 06..08 Not affected by the gaps; covered by plans 01 to 06 — COVERED (unchanged)
VERIFICATION gap 1 missing 1 AST fence collection for snippet, policy and command checks 1, 2 COVERED
VERIFICATION gap 1 missing 2 Refuse README src= fences and stop captioning them 1, 2 COVERED
VERIFICATION gap 1 missing 3 Normalise fence language through chroma 2 COVERED
VERIFICATION gap 1 missing 4 Fixtures (callout drift, callout missing file, callout go fence, golang fence) and acceptance scanner > stripping 4, 5 COVERED
VERIFICATION gap 2 missing 1-3 Roots from Test plus Examples with output; go/build ImportDir membership; fixtures 3, 4 COVERED
VERIFICATION gap 3 missing 1-3 go doc -c; env, go run, flag-first, bin/ forms; fixtures 3, 4 COVERED
RESEARCH Q4 rules 1, 3, 5 Every go fence src=; Examples need Output; missing refs fail 2, 3 COVERED
RESEARCH Q6 go doc fallback only on index miss; CLI checker forms 3 COVERED
CONTEXT D-07 Verified examples, compiled and run 1, 3 COVERED
CONTEXT D-12 Identifier checker must be exact 3 COVERED
CONTEXT D-15 chroma/v2 only; reused for alias detection, no new dependency 2 COVERED
CONTEXT D-18 README fences rendered as written: src= refused there, never captioned 1, 2 COVERED

Task count note: the user locked exactly one gap-closure plan carrying its own tests at the plan-count checkpoint, so this plan has five tasks (tracer, two expansion tasks, two closing test tasks) instead of the default two or three. To keep that within a context budget, <resume_point> records a seam after Task 3 (task commits with Deviation: bodies, a state.record-session line naming PLAN_BASE) so Tasks 4 and 5 can resume in a fresh context, and their <read_first> lists name only the symbols and helpers they use.

Artifacts this phase produces

  • New file internal/docsite/fences.go: type mdFence (fields info, lang, line, topLevel, code, top), type fenceLine (fields line, text), func collectFences(doc gast.Node, body []byte) ([]mdFence, error).
  • internal/docsite/highlight.go: func goLang(lang string) bool.
  • internal/docsite/snippet.go: constants nestedSrcMessage, readmeSrcMessage; reworded notRunMessage; funcs buildPackage(dir string) (*build.Package, error), isTestName(name string) bool; changed signatures checkFences(root, file string, lines []string, lineBase int, fences []mdFence) ([]drift, []Problem, error) and (s *site) checkSnippets(docs []parsedDoc) ([]Problem, error); isRoot removed; Sync parses bodies after splitFrontmatter.
  • internal/docsite/check_commands.go: func commandWord(cmd string) (name string, tool bool, ok bool); the commandToken regex removed.
  • internal/docsite/check_policy.go: AST go-fence rule via collectFences and goLang; AST callout-type check; the calloutMarker regex removed.
  • internal/docsite/check_identifiers.go: goDoc argv gains -c.
  • internal/docsite/render.go: fenceAnnotator restricted to top-level fences of docs/ pages; openFence accepts at most three leading spaces.
  • internal/docsite/docsite.go / load.go: checkSnippets runs inside checkContent on the parsed docs.
  • Tests: TestCollectFences (fences_test.go), TestFenceCaptionsOnlyVerified (render_test.go), TestGoLang (highlight_test.go), TestNestedFenceChecks, TestIdentifierGoDocCaseSensitive, TestCommandWord (checks_test.go), TestSnippetRootsAndBuild, TestSyncParsedFences (snippet_test.go), TestAcceptanceFenceScanner and helpers scanDocFences, goFenceLang, field docFence.nested (cmd/summer/phase11_1_acceptance_test.go).
  • Fixtures under internal/docsite/testdata/violations/: snippet-callout-drift, snippet-callout-missing, snippet-blockquote, snippet-list-item, go-fence-callout-no-src, go-fence-golang, snippet-readme-src, command-in-callout, snippet-example-no-output-helper, snippet-example-no-output-region, snippet-build-ignored, snippet-build-ignored-test-root, snippet-testdata-go, identifier-wrong-case, command-env-prefix, command-flag-first, command-go-run, command-bin-no-dot (each with want.txt).
  • Gate: five new --self-test plants; eight names added to DOCSITE_TESTS and one to SUMMER_TESTS.
  • Docs: docs/console/utilities.md and README.md sentences on the src= rules.
  • Planning: 11.1-VALIDATION.md rows 11.1-07-T1 to T5; a note in .planning/todos/pending/readme-go-fences-src.md.
Create `.planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-07-SUMMARY.md` when done. Record the mutation checks, any unit-test fixture corrected for the WR-01 hole, any production defect fixed with its test, and (as a follow-up candidate, not fixed here) that the identifier index still reads build-ignored files, which no gap cites.