Files
summercms/.planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-PATTERNS.md

9.7 KiB

Phase 11.1: SummerCMS documentation for humans and AI agents - Pattern Map

Mapped: 2026-09-30 Files analyzed: 14 file groups Analogs found: 11 / 14

All analog paths below are git-tracked in summercms.go. There is no docs/ tree and no example_test.go anywhere in modules/ yet (verified). Phase 11 modules exist: conga, lighthouse, flare, beachcomber, tide.

File Classification

New/Modified File Role Data Flow Closest Analog Match Quality
internal/docsite/*.go (load, frontmatter, site.yaml, walker, render, emit) service (generator) file-I/O, transform internal/build/build.go, internal/build/manifest.go, internal/build/scaffold.go role-match
internal/docsite site.yaml / frontmatter strict decode utility transform modules/tide/manifest.go:85-104 exact
internal/docsite/theme/* (embedded HTML/CSS/JS assets) config/assets file-I/O internal/build/scaffold.go:20-23 (//go:embed stubs/*.tmpl) role-match
internal/docsite checkers (identifiers, links, CLI names, forbidden names, src= sync) test utility batch cmd/summer/main_test.go (go/parser use, TestToolCommandNames) partial
internal/docsite/*_test.go test file-I/O internal/build/build_test.go (t.TempDir fixtures) exact
cmd/summer/docs.go (docs:build, docs:sync, docs:serve) controller (CLI) request-response cmd/summer/main.go:52-92 exact
cmd/summer/main.go (register in toolCommands()) config - cmd/summer/main.go:27-50 exact
cmd/summer/main_test.go (extend TestToolCommandNames) test - itself, lines 17-54 exact
modules/<m>/example_test.go (bonfire first, then party, backpack, compass, festival, phrasebook, towel, lagoon, wire, surf, postcard, bouncer, fetchguard, cabana, conga, lighthouse, flare, beachcomber, tide) test (Example funcs) - modules/bonfire/call_test.go (fixture shape only) partial, no Example* exists
docs/examples/blog/** (plugin, models, YAML, controller, console cmd, lang, tests) example plugin CRUD examples/hello/plugins/greeter/, examples/hello/hello_test.go role-match
scripts/check-phase11.1.sh gate script batch scripts/check-phase11.sh + scripts/check-phase10.2.sh exact
.gitignore (add /site/) config - .gitignore "# Go" block exact
CLAUDE.md Documentation section (D-13 bullet) docs rule - CLAUDE.md Documentation section exact
docs/**/*.md, docs/site.yaml content - modules/*/README.md (tone/structure) no code analog

Pattern Assignments

cmd/summer/docs.go (CLI, request-response)

Analog: cmd/summer/main.go lines 52-92. Commands are plain bonfire.Command values returned by a xxxCommand() func; flags via bonfire.Flag, Bare: true for booleans; errors returned, output via out.Printf.

func makePluginCommand() bonfire.Command {
	return bonfire.Command{
		Name:        "make:plugin",
		Description: "Scaffold a compiling plugin module from a vendor.plugin id",
		Args: []bonfire.Arg{{Name: "id", Description: "Plugin ID in vendor.plugin form", Required: true}},
		Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
			dir, err := os.Getwd()
			if err != nil {
				return err
			}
			created, err := build.MakePlugin(ctx, dir, id)
			...
			out.Printf("created %s\n", created)
			return nil
		},
	}
}

Bool flag parsing (for docs:sync --check etc.), cmd/summer/runtime.go:67-87:

if v, ok := in.Flag("once"); ok {
	if once, _ := strconv.ParseBool(v); once { ... }
}

Value flags (--out, --addr): in.Flag("out"); repeatable via Repeatable: true + in.Flags(name) (modules/conga/commands.go:33-41). Keep logic in internal/docsite (like build.App(ctx, dir, out)); the command only resolves cwd/flags and delegates.

Registration: append docsBuildCommand(), docsSyncCommand(), docsServeCommand() to the slice in toolCommands() (cmd/summer/main.go:27-50).

cmd/summer/main_test.go (extend)

Add "docs:build", "docs:sync", "docs:serve" to the want list at line 22 and --out etc. to helpWants (lines 28-37). The CLI-name checker should derive the command set from toolCommands() plus each module's RuntimeCommands (e.g. conga.RuntimeCommands, modules/lighthouse/centrifugo/commands.go:22), not a hardcoded list.

internal/docsite strict YAML (site.yaml, frontmatter)

Analog: modules/tide/manifest.go:85-104

func ParseManifest(raw []byte) (Manifest, error) {
	var m Manifest
	dec := yaml.NewDecoder(bytes.NewReader(raw), yaml.DisallowUnknownField())
	if err := dec.Decode(&m); err != nil {
		return Manifest{}, fmt.Errorf("tide: parse manifest: %w", err)
	}
	...
}

Split LoadX(path) (os.ReadFile + wrapped error with path) from ParseX(raw); error prefix docsite: .... Import github.com/goccy/go-yaml (already in go.mod).

internal/docsite theme assets

Analog: internal/build/scaffold.go:20-23

//go:embed stubs/*.tmpl
var stubFS embed.FS

var stubTmpl = template.Must(template.ParseFS(stubFS, "stubs/*.tmpl"))

Use html/template (not text/template) for pages; embed.FS for theme/*.css|js, copied verbatim into the output dir.

internal/docsite/*_test.go

Analog: internal/build/build_test.go (e.g. TestParseManifestRejectsDuplicatesAndInvalidPaths line 29, dir := t.TempDir()). Plain stdlib testing, t.Fatalf("x = %v, want ..."), no testify (repo convention). Smoke test builds the real docs/ tree into t.TempDir(). Failure-path fixtures written into temp dirs.

modules/<m>/example_test.go

No Example analog exists. Convention to establish: package <m>_test, functions ExampleNewRoot, ExampleCall, ... naming real identifiers, each ending in // Output:. Use // docs:start <region> / // docs:end <region> markers for src=path#region. Reusable fixture shape from modules/bonfire/call_test.go:11-36 (inline []Command{{Name: "acme:echo", ...}} + Call(ctx, cmds, name, args, &out)), rewritten with the bonfire. qualifier and context.Background() instead of t.Context(). Use neutral names (acme, blog). For DB-bound modules (lagoon, conga, beachcomber, lighthouse), prefer non-DB identifiers or regions in existing _test.go packages; do not start Postgres in Examples.

docs/examples/blog/**

Analog: examples/hello/plugins/greeter/{go.mod,plugin.go,plugin_test.go}, examples/hello/plugins/base/{config,lang,views}, and examples/hello/hello_test.go (package main, imports modules backpack, bonfire, compass, pact, party, phrasebook, postcard, surf, towel). The plugin layout leaves come from internal/build/scaffold.go pluginLeaves (models, classes, controllers, console, jobs, ...), so the scaffold-layout test can compare against that slice. Note: examples/hello uses its own go.mod per plugin; decide whether blog is its own module (then go test ./... at root will not cover it; the gate must run it explicitly, as check-phase11 does for the app).

scripts/check-phase11.1.sh

Analog: scripts/check-phase11.sh (latest) for structure; scripts/check-phase10.2.sh for refuse/expect_refusal.

  • Header comment describing the gate, then set -euo pipefail, ROOT="${PHASE11_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" (11.sh:19-21), PHASE_DIR=....
  • usage() heredoc listing modes, exit 2 (11.sh:33-45).
  • refuse() { echo "refuse: $*" >&2; return 1; } (10.2.sh:25-28).
  • expect_refusal name want ... : run the check in a scratch copy, fail if it passes, fail if stderr lacks want (10.2.sh:105-116).
  • run_self_test: bash -n "${BASH_SOURCE[0]}", mktemp -d, trap 'rm -rf ...' RETURN, copy README.md + module READMEs, plant violations (10.2.sh:118-130).
  • run_go: (cd "$ROOT" && go vet ./...) then tests (11.sh:621-627); each mode echoes phase11.1 <mode> passed.
  • Final case "${1:-}" in --self-test) ...;; --all) ...;; *) usage ;; esac (11.sh tail).
  • README-table check to reuse for --preconditions: grep -qF "| [$m](modules/$m/README.md) |" "$tree/README.md" (11.sh:~260). Modes per RESEARCH Q7: --preconditions --deps --forbidden --docs --claude --self-test --go --all. Semantic checks stay in Go tests.

.gitignore

Add /site/ under the # Go block (next to /dist/).

CLAUDE.md

Documentation section, 4 bullets ending with the go doc ./modules/<name> <Identifier> rule. Add the D-13 bullet and amend the go-doc bullet to name go test ./internal/docsite/.... Separate commit from code.

Shared Patterns

  • Errors: wrap with package prefix and %w (fmt.Errorf("tide: read manifest %s: %w", path, err)); commands return errors, main prints them.
  • Tests: stdlib testing only, t.TempDir(), t.Fatalf with got/want.
  • Neutral names: acme, blog; the forbidden list (fonoteka|płytarium|plytarium) as a constant shared by test and gate.
  • Module discovery: iterate modules/*/ dirs dynamically (like hygiene loop in check-phase11.sh around line 255-265) so Phase 11 modules are covered with no code changes.

No Analog Found

File Role Reason
goldmark pipeline / renderer in internal/docsite transform No Markdown rendering in repo; use RESEARCH.md (goldmark v1.8.6 parser/extension notes)
search index + search.js, theme-init.js frontend asset No plain-JS assets; admin uses Vue/Tailwind. Take tokens from admin/src/styles/main.css:83-125 per UI-SPEC
docs/**/*.md content, llms.txt/llms-full.txt formats content Follow UI-SPEC and RESEARCH (llmstxt.org, Mintlify block format)

Metadata

Analog search scope: cmd/summer, internal/build, modules/{bonfire,conga,lighthouse,tide}, examples/hello, scripts/check-phase*.sh, .gitignore Pattern extraction date: 2026-09-30