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 lackswant(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 echoesphase11.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,mainprints them. - Tests: stdlib
testingonly,t.TempDir(),t.Fatalfwith 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