|
|
|
|
@@ -0,0 +1,135 @@
|
|
|
|
|
# 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`.
|
|
|
|
|
```go
|
|
|
|
|
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`:
|
|
|
|
|
```go
|
|
|
|
|
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`
|
|
|
|
|
```go
|
|
|
|
|
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
|
|
|
|
|
//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
|