Six plans (tracer generator, site UX and checkers, content A, content B, acme/blog walkthrough, unit tests). SC4/DOCS-04 narrowed to docs/ pages per D-18; README Go fence conversion logged as a todo.
273 lines
26 KiB
Markdown
273 lines
26 KiB
Markdown
---
|
|
phase: 11.1-summercms-documentation-for-humans-and-ai-agents
|
|
plan: 05
|
|
type: execute
|
|
wave: 5
|
|
depends_on: ["11.1-04"]
|
|
files_modified:
|
|
- docs/examples/blog/plugin.go
|
|
- docs/examples/blog/routes.go
|
|
- docs/examples/blog/registry.gen.go
|
|
- docs/examples/blog/blog_test.go
|
|
- docs/examples/blog/postgres_test.go
|
|
- docs/examples/blog/scaffold_layout_test.go
|
|
- docs/examples/blog/classes/doc.go
|
|
- docs/examples/blog/config/config.yaml
|
|
- docs/examples/blog/console/doc.go
|
|
- docs/examples/blog/console/publish.go
|
|
- docs/examples/blog/console/publish_test.go
|
|
- docs/examples/blog/controllers/doc.go
|
|
- docs/examples/blog/controllers/posts.go
|
|
- docs/examples/blog/controllers/posts_test.go
|
|
- docs/examples/blog/controllers/posts/config_form.yaml
|
|
- docs/examples/blog/controllers/posts/config_list.yaml
|
|
- docs/examples/blog/jobs/doc.go
|
|
- docs/examples/blog/lang/en/lang.yaml
|
|
- docs/examples/blog/middleware/doc.go
|
|
- docs/examples/blog/models/doc.go
|
|
- docs/examples/blog/models/post.go
|
|
- docs/examples/blog/models/post_test.go
|
|
- docs/examples/blog/models/posts/fields.yaml
|
|
- docs/examples/blog/models/posts/columns.yaml
|
|
- docs/examples/blog/updates/doc.go
|
|
- docs/examples/blog/updates/20260101000000_create_acme_blog_posts.go
|
|
- docs/examples/blog/updates/20260101000100_add_published_at.go
|
|
- docs/examples/blog/updates/updates_test.go
|
|
- docs/examples/blog/views/mail/welcome.htm
|
|
- docs/setup/porting-a-plugin.md
|
|
- docs/setup/coming-from-wintercms.md
|
|
- docs/index.md
|
|
- cmd/summer/docs_test.go
|
|
autonomous: true
|
|
requirements: [DOCS-04, DOCS-07]
|
|
assumption_delta_decision: no-change
|
|
user_setup: []
|
|
|
|
estimate:
|
|
tokens: 110000
|
|
raw_tokens: 110000
|
|
tasks: 3
|
|
confidence: low
|
|
|
|
must_haves:
|
|
truths:
|
|
- "Per D-10 and DOCS-07, docs/setup/porting-a-plugin.md takes a WinterCMS `acme/blog` plugin (Plugin.php, a Post model, version.yaml updates, routes.php, a backend Posts controller with fields.yaml and columns.yaml, an artisan command) to a compiled SummerCMS plugin, showing the WinterCMS side as php/yaml fences and the SummerCMS side only as src= copies of docs/examples/blog."
|
|
- "Per D-16, docs/examples/blog is an ordinary package tree of the root module (import path git.golem15.com/golem15/summercms/docs/examples/blog), with no go.mod of its own, covered by root `go vet ./...` and `go test ./...`."
|
|
- "Per D-07, every Go fence and every YAML fence on the walkthrough page carries src= into docs/examples/blog and matches the source byte for byte; every referenced sub-package directory has _test.go files that exercise it."
|
|
- "The -short-safe tests activate the plugin through party and backpack, assert its routes are registered with surf, run the console command with bonfire into a buffer, and check the admin controller declaration; the Docker tests migrate up on an ICU pl-PL database, create, list and publish posts through the model, the route handler and the command, then roll back the last migration and confirm the column is gone."
|
|
- "TestScaffoldLayout runs build.MakePlugin, MakeModel, MakeAdminController, MakeCommand and MakeMigration for acme.blog into a copy of examples/hello and asserts that the relative file set of docs/examples/blog (without _test.go files, with 14-digit migration timestamps normalised) equals the scaffolder's output (without go.mod and go.sum)."
|
|
- "The walkthrough's writes go through an explicit fill allow-list (lagoon.Fill) and the admin controller declares a permission; the route handler uses parameterised queries only."
|
|
- "Per D-11, the plugin, its tests and the page use only neutral names (acme, blog)."
|
|
- "The walkthrough page lists the exact `summer make:*` commands that produced the layout, and the command checker accepts every `summer` and `./bin/<app>` token on it (blog:publish is collected from the docs/examples command literal)."
|
|
- "Scaffolder oddities found while porting (for example same-second migration ordering by file name, or the generated-code header on files the developer edits) are described on the page as they are and logged under .planning/todos/pending/, without changing internal/build."
|
|
- statement: "A WinterCMS developer can follow the walkthrough from top to bottom and end with the same plugin (manual review)."
|
|
verification: backstop
|
|
prohibitions:
|
|
- requirement_id: DOCS-07
|
|
category: safety
|
|
status: resolved
|
|
verification: judgment
|
|
resolution: "Model writes use lagoon.Fill with an allow-list, the admin controller declares a permission, and the handler binds parameters."
|
|
reason: "Developers copy walkthrough code into production plugins; an unsafe pattern in the canonical example spreads to every port."
|
|
statement: "The walkthrough must not demonstrate a write path without an explicit fill allow-list or an admin controller without a declared permission."
|
|
artifacts:
|
|
- path: "docs/examples/blog/plugin.go"
|
|
provides: "the acme.blog plugin"
|
|
contains: "acme.blog"
|
|
- path: "docs/examples/blog/scaffold_layout_test.go"
|
|
provides: "TestScaffoldLayout pinning the walkthrough to the scaffolder"
|
|
contains: "MakeAdminController"
|
|
- path: "docs/examples/blog/postgres_test.go"
|
|
provides: "Docker-backed migrate, CRUD, route, command and rollback tests"
|
|
contains: "ICU_LOCALE"
|
|
- path: "docs/setup/porting-a-plugin.md"
|
|
provides: "the porting walkthrough page"
|
|
contains: "src=docs/examples/blog/"
|
|
key_links:
|
|
- from: "docs/setup/porting-a-plugin.md"
|
|
to: "docs/examples/blog/plugin.go"
|
|
via: "go fence src= references kept in sync by the snippet check"
|
|
pattern: "src=docs/examples/blog/plugin\\.go"
|
|
- from: "docs/examples/blog/scaffold_layout_test.go"
|
|
to: "internal/build/artifact.go"
|
|
via: "calls build.Make* into a temp app and compares file sets"
|
|
pattern: "build\\.Make"
|
|
- from: "docs/examples/blog/registry.gen.go"
|
|
to: "docs/examples/blog/models/post.go"
|
|
via: "generated accessors list models, migrations, commands and admin controllers"
|
|
pattern: "generatedModels"
|
|
---
|
|
|
|
<objective>
|
|
Build the `acme/blog` porting walkthrough (D-10, D-16, DOCS-07): a real, compiled and tested SummerCMS plugin under `docs/examples/blog` with a model, two migrations, a route, an admin controller with its YAML, a console command and a language file, plus `docs/setup/porting-a-plugin.md` that walks a WinterCMS developer through it using only `src=` copies. A scaffold-layout test pins the tree to what `summer make:*` really emits.
|
|
|
|
Purpose: success criterion 5's walkthrough, verified under criterion 4.
|
|
|
|
Output: `docs/examples/blog/**` (package tree, tests, YAML), the walkthrough page, links from the concept map and index.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@~/.claude/gsd-core/workflows/execute-plan.md
|
|
@~/.claude/gsd-core/templates/summary.md
|
|
</execution_context>
|
|
|
|
<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-RESEARCH.md
|
|
@.planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-04-SUMMARY.md
|
|
@CLAUDE.md
|
|
@internal/build/scaffold.go
|
|
@internal/build/artifact.go
|
|
@cmd/summer/main_test.go
|
|
@examples/hello/plugins/greeter/plugin.go
|
|
@modules/lagoon/postgres_test.go
|
|
|
|
Scaffolder facts (probed at planning time by running the tool in a scratch copy of examples/hello): `make:plugin acme.blog`, `make:model acme.blog Post`, `make:admin-controller acme.blog Posts`, `make:command acme.blog Publish` and `make:migration acme.blog AddPublishedAt` produce, relative to the plugin directory: `plugin.go`, `routes.go`, `registry.gen.go`, `go.mod`, `go.sum`, `classes/doc.go`, `config/config.yaml`, `console/doc.go`, `console/publish.go`, `controllers/doc.go`, `controllers/posts.go`, `controllers/posts/config_form.yaml`, `controllers/posts/config_list.yaml`, `jobs/doc.go`, `lang/en/lang.yaml`, `middleware/doc.go`, `models/doc.go`, `models/post.go`, `models/posts/fields.yaml`, `models/posts/columns.yaml`, `updates/doc.go`, `updates/<14-digit timestamp>_create_acme_blog_posts.go`, `updates/<timestamp>_add_published_at.go`, `views/mail/welcome.htm`. The admin YAML sits under the controller's snake name (`models/posts/`), and the generated admin controller names the model `Posts`. Two migrations created in the same second are listed in `registry.gen.go` in file-name order (`add_published_at` before `create_...`). TestScaffoldLayout is the source of truth if the scaffolder output differs at execution time.
|
|
|
|
Authoring rules for the page are those of plans 11.1-03 and 11.1-04 (strict frontmatter, ASCII headings, `pkg.Ident` spans, src= fences filled by `summer docs:sync`, neutral names). Stage only each task's files.
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="tracer">
|
|
<name>Task 1: Tracer: the blog plugin activates and serves its posts route, and the walkthrough page shows it from verified source</name>
|
|
<files>docs/examples/blog/plugin.go, docs/examples/blog/routes.go, docs/examples/blog/registry.gen.go, docs/examples/blog/blog_test.go, docs/examples/blog/classes/doc.go, docs/examples/blog/config/config.yaml, docs/examples/blog/console/doc.go, docs/examples/blog/controllers/doc.go, docs/examples/blog/jobs/doc.go, docs/examples/blog/lang/en/lang.yaml, docs/examples/blog/middleware/doc.go, docs/examples/blog/models/doc.go, docs/examples/blog/models/post.go, docs/examples/blog/models/post_test.go, docs/examples/blog/updates/doc.go, docs/examples/blog/updates/20260101000000_create_acme_blog_posts.go, docs/examples/blog/updates/updates_test.go, docs/examples/blog/views/mail/welcome.htm, docs/setup/porting-a-plugin.md, cmd/summer/docs_test.go</files>
|
|
<read_first>
|
|
- internal/build/scaffold.go and internal/build/stubs/*.tmpl (plugin.go, routes.go, registry and doc.go shapes)
|
|
- internal/build/artifact.go (MakeModel and migration stubs)
|
|
- cmd/summer/main_test.go copyHelloApp and TestMakeCommandsViaCLI (how to run the scaffolder against a copy of examples/hello)
|
|
- examples/hello/plugins/greeter/plugin.go (a hand-finished plugin)
|
|
- modules/surf/README.md (routes, pact.Router, BuildRouter or Assemble for tests), modules/lagoon/README.md (Fill, Paginate), modules/wire/README.md
|
|
- docs/setup/coming-from-wintercms.md (tone and PHP fence style)
|
|
</read_first>
|
|
<action>
|
|
Per D-10 and D-16, start from real scaffolder output so the layout is honest.
|
|
|
|
1. Generate the skeleton: build the tool (`go build -o <tmp>/summer ./cmd/summer`), copy examples/hello into a temp dir with its framework `replace` pointed at the repository root (as `copyHelloApp` does), run `make:plugin acme.blog` and `make:model acme.blog Post` there, then copy `plugins/blog/` into `docs/examples/blog/` without `go.mod` and `go.sum`. Rename the migration to the fixed timestamp `20260101000000_create_acme_blog_posts.go` (and its gormigrate ID to match) and rewrite every import path to `git.golem15.com/golem15/summercms/docs/examples/blog/...`. Keep the generated-code header lines as the scaffolder writes them.
|
|
2. Finish the model and migration: `models.Post` gains `Title`, `Slug` (unique) and `Body`; the publication timestamp arrives in Task 2 with the second migration. The create migration creates `acme_blog_posts` with those columns. Add a `models.Post` fill allow-list (title, slug, body) used through `lagoon.Fill`.
|
|
3. Route: in `routes.go`, `GET /api/blog/posts` lists posts newest first as JSON through `lagoon.Paginate` and `wire.WriteJSON`, using parameterised queries only and taking the `*gorm.DB` from the app the way framework plugins do (read lagoon's README). Declare it through `pact.HasRoutes`.
|
|
4. Tests (-short-safe, `blog_test.go` in package `blog_test`): `TestPluginActivates` activates the plugin through `party`/`backpack` and checks ID and capabilities; `TestRoutesRegistered` builds the router with surf and asserts the `GET /api/blog/posts` route exists without touching a database. `models/post_test.go`: `TestPostTableAndFill` (TableName and that `lagoon.Fill` with the allow-list drops an `id` key). `updates/updates_test.go`: `TestMigrationIDs` (IDs start with their 14-digit timestamps and are in ascending order in `registry.gen.go`'s list).
|
|
5. Page `docs/setup/porting-a-plugin.md` (title "Porting a plugin", section `setup`, order 50): intro (what we port and why a neutral `acme/blog`), then sections "Plugin registration" (a `php` fence of `Plugin.php` and a `go src=docs/examples/blog/plugin.go#Plugin` style fence), "The Post model" (`php` fence of `models/Post.php`, `go src=docs/examples/blog/models/post.go#Post`), "Migrations" (`yaml` fence of WinterCMS `version.yaml`, `go src=` of the create migration), "Routes" (`php` fence of `routes.php`, `go src=` of the route declaration and handler). Fill all src= fences with `go run ./cmd/summer docs:sync`.
|
|
6. Append `setup/porting-a-plugin` to `TestDocsRequiredPages`.
|
|
</action>
|
|
<verify>
|
|
<automated>go vet ./... && go test -short ./docs/examples/... -count=1 -v && go test ./cmd/summer -run '^(TestDocsTree|TestDocsRequiredPages)$' -count=1</automated>
|
|
<fails_when>non-zero exit, a "--- FAIL" line, or "no tests to run"</fails_when>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- `test ! -e docs/examples/blog/go.mod` (in-root package, D-16).
|
|
- `go list ./docs/examples/blog/...` prints `git.golem15.com/golem15/summercms/docs/examples/blog` among its lines.
|
|
- `go test -short ./docs/examples/blog -run '^(TestPluginActivates|TestRoutesRegistered)$' -count=1 -v` prints two `--- PASS` lines.
|
|
- `grep -c 'src=docs/examples/blog/' docs/setup/porting-a-plugin.md` prints at least 4.
|
|
- `grep -n 'lagoon.Fill' docs/examples/blog/routes.go docs/examples/blog/models/post.go docs/examples/blog/models/post_test.go` finds a match.
|
|
- `go run ./cmd/summer docs:build --check` exits 0.
|
|
</acceptance_criteria>
|
|
<done>A compiled acme.blog plugin with a model, a migration and a route lives in the root module, its fast tests pass, and the walkthrough page shows it through verified src= copies.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: The walkthrough adds the admin controller, the console command and a second migration, and proves them against Postgres</name>
|
|
<files>docs/examples/blog/controllers/posts.go, docs/examples/blog/controllers/posts_test.go, docs/examples/blog/controllers/posts/config_form.yaml, docs/examples/blog/controllers/posts/config_list.yaml, docs/examples/blog/models/posts/fields.yaml, docs/examples/blog/models/posts/columns.yaml, docs/examples/blog/models/post.go, docs/examples/blog/console/publish.go, docs/examples/blog/console/publish_test.go, docs/examples/blog/updates/20260101000100_add_published_at.go, docs/examples/blog/registry.gen.go, docs/examples/blog/lang/en/lang.yaml, docs/examples/blog/postgres_test.go, docs/setup/porting-a-plugin.md</files>
|
|
<read_first>
|
|
- internal/build/artifact.go MakeAdminController, MakeCommand, MakeMigration stubs
|
|
- modules/cabana/README.md and modules/pact/README.md (AdminController, AdminPermissioned, config_form/config_list keys, fields.yaml and columns.yaml)
|
|
- modules/bonfire/README.md (Command, Arg, Output)
|
|
- modules/lagoon/postgres_test.go (TestMain behaviour, dedicatedDB with the ICU pl-PL template) and modules/lagoon/README.md (Migrate, RollbackLast)
|
|
- docs/examples/blog (Task 1 state)
|
|
</read_first>
|
|
<action>
|
|
1. Scaffold the rest the same way as Task 1 (`make:admin-controller acme.blog Posts`, `make:command acme.blog Publish`, `make:migration acme.blog AddPublishedAt` in the temp app), copy the new files in, rename the migration to `20260101000100_add_published_at.go` (ID to match) and update `registry.gen.go` so migrations are listed create first, then add_published_at.
|
|
2. Finish them: the admin controller points at the `Post` model, declares a permission (for example `acme.blog.access_posts`) and uses config_form.yaml and config_list.yaml; `models/posts/fields.yaml` has title, slug, body and published_at fields and `columns.yaml` lists title, slug and published_at, all in WinterCMS YAML syntax. The migration adds `published_at TIMESTAMPTZ NULL` and its Rollback drops it; `models.Post` gains `PublishedAt *time.Time`. `console/publish.go` becomes `blog:publish` with a required `slug` argument that sets `published_at` on the matching post (parameterised) and prints `published <slug>`, returning an error for an unknown slug. `lang/en/lang.yaml` carries the plugin name, the permission label and the field labels, referenced from the YAML.
|
|
3. Tests: `controllers/posts_test.go` (`TestPostsControllerDeclaration`: ID, ConfigDir, permission; and compile the admin YAML through cabana if it can do so without a database, otherwise assert the YAML parses and move the compile check into the Docker test). `console/publish_test.go` (`TestPublishCommandShape`: name, argument, `bonfire.Call` with no slug returns an error). `postgres_test.go` (package `blog_test`): a `TestMain` that starts a testcontainers Postgres and creates a database with `TEMPLATE template0 ENCODING 'UTF8' LOCALE_PROVIDER icu ICU_LOCALE 'pl-PL'` (lagoon's idiom), skipping the container only under `testing.Short()` and failing when Docker is unavailable in a full run; `TestMigrateUpAndRollback` (lagoon.Migrate with the plugin, columns exist, `lagoon.RollbackLast` removes `published_at`, Migrate again), `TestPostsRouteAgainstDatabase` (seed through `lagoon.Fill`, GET /api/blog/posts through httptest returns them), `TestPublishCommandAgainstDatabase` (`bonfire.Call` of `blog:publish` sets `published_at` and prints `published <slug>`).
|
|
4. Page: add sections "Admin controller" (`php` fence of `controllers/Posts.php`, `go src=` of the controller, `yaml src=docs/examples/blog/controllers/posts/config_form.yaml`, `config_list.yaml`, `models/posts/fields.yaml`, `columns.yaml`), "Console command" (`php` fence of an artisan command, `go src=` of `console/publish.go`, a `sh` fence running `./bin/acme blog:publish hello-world`), "Adding a column" (the second migration, `summer migrate:rollback --plugin acme.blog`). Run `go run ./cmd/summer docs:sync`.
|
|
</action>
|
|
<verify>
|
|
<automated>go vet ./... && go test ./docs/examples/... -count=1 -v</automated>
|
|
<fails_when>non-zero exit, a "--- FAIL" line, a "--- SKIP" line for a Postgres test in this full run, or "no tests to run"</fails_when>
|
|
<automated>go test ./cmd/summer -run '^(TestDocsTree|TestDocsRequiredPages)$' -count=1</automated>
|
|
<fails_when>non-zero exit or a "FAIL" line</fails_when>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- `go test ./docs/examples/blog -run '^(TestMigrateUpAndRollback|TestPostsRouteAgainstDatabase|TestPublishCommandAgainstDatabase)$' -count=1 -v` prints three `--- PASS` lines.
|
|
- `grep -c 'yaml src=docs/examples/blog/' docs/setup/porting-a-plugin.md` prints at least 4.
|
|
- `grep -n 'ICU_LOCALE' docs/examples/blog/postgres_test.go` finds a match.
|
|
- `grep -n '"blog:publish"' docs/examples/blog/console/publish.go` finds a match.
|
|
- `grep -En 'Permission|permission' docs/examples/blog/controllers/posts.go` finds a match.
|
|
- `go run ./cmd/summer docs:build --check` exits 0.
|
|
</acceptance_criteria>
|
|
<done>The walkthrough plugin has an admin controller with WinterCMS-style YAML, a console command and a reversible second migration, all exercised against a real Postgres, and the page shows them from verified source.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 3: The walkthrough is pinned to the scaffolder, lists the exact make commands, and is linked from the concept map and index</name>
|
|
<files>docs/examples/blog/scaffold_layout_test.go, docs/setup/porting-a-plugin.md, docs/setup/coming-from-wintercms.md, docs/index.md, cmd/summer/docs_test.go</files>
|
|
<read_first>
|
|
- internal/build/scaffold.go MakePlugin and pluginLeaves, internal/build/artifact.go Make* signatures (`go doc ./internal/build`)
|
|
- cmd/summer/main_test.go copyHelloApp (replace rewrite for a temp app)
|
|
- docs/examples/blog (Task 2 state) and docs/setup/porting-a-plugin.md
|
|
- .planning/todos/pending/redacting-slog-handler.md (todo format)
|
|
</read_first>
|
|
<action>
|
|
1. `docs/examples/blog/scaffold_layout_test.go` (package `blog_test`, -short-safe, no network): `TestScaffoldLayout` copies examples/hello into `t.TempDir()` with its framework `replace` pointed at the repository root, calls `build.MakePlugin(ctx, dir, "acme.blog")`, `build.MakeModel(ctx, dir, "acme.blog", "Post", false)`, `build.MakeAdminController(ctx, dir, "acme.blog", "Posts")`, `build.MakeCommand(ctx, dir, "acme.blog", "Publish")` and `build.MakeMigration(ctx, dir, "acme.blog", "AddPublishedAt")`, collects the relative file set under `plugins/blog` without `go.mod` and `go.sum`, collects the relative file set of `docs/examples/blog` without `_test.go` files, replaces a leading 14-digit timestamp in `updates/` file names with a fixed token in both, and fails with the symmetric difference when the sets differ.
|
|
2. Page: add a "Scaffold it yourself" section with the exact commands in order (`summer make:plugin acme.blog`, `summer make:model acme.blog Post`, `summer make:migration acme.blog AddPublishedAt`, `summer make:admin-controller acme.blog Posts`, `summer make:command acme.blog Publish`, `summer plugin:add plugins/blog`, `summer build`) and what each writes, a note that the in-repository copy has no go.mod because it lives in the framework module (show the go.mod a real plugin gets as a `text` fence), and a closing "Checklist" of what changed from WinterCMS. Describe the scaffolder oddities as they are (the generated admin controller names the model after the controller; migrations created in the same second are ordered by file name; generated files carry a generated-code header that `summer make` uses to rebuild the accessors) and log each one that is a real gap as a todo under `.planning/todos/pending/` in a separate planning commit, without changing internal/build.
|
|
3. Link the walkthrough from `docs/setup/coming-from-wintercms.md` (intro paragraph) and `docs/index.md`.
|
|
4. Run `go run ./cmd/summer docs:sync` and `go run ./cmd/summer docs:build --check`; fix every problem.
|
|
</action>
|
|
<verify>
|
|
<automated>go vet ./... && go test -short ./docs/examples/blog -run '^TestScaffoldLayout$' -count=1 -v && go test ./cmd/summer -run '^(TestDocsTree|TestDocsRequiredPages|TestDocsAIOutputsInSync)$' -count=1</automated>
|
|
<fails_when>non-zero exit, a "--- FAIL" line, or "no tests to run"</fails_when>
|
|
<automated>scripts/check-phase11.1.sh --docs && scripts/check-phase11.1.sh --forbidden</automated>
|
|
<fails_when>non-zero exit or a line starting "refuse:"</fails_when>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- `go test -short ./docs/examples/blog -run '^TestScaffoldLayout$' -count=1 -v` prints `--- PASS: TestScaffoldLayout`.
|
|
- `grep -c 'summer make:' docs/setup/porting-a-plugin.md` prints at least 5.
|
|
- `grep -n 'porting-a-plugin.md' docs/setup/coming-from-wintercms.md docs/index.md` finds a match in each file.
|
|
- `! grep -rniE 'fonoteka|p(l|ł)ytarium' docs` (no match).
|
|
- `git diff --name-only 9033d81 -- internal/build` prints nothing (scaffolder unchanged by this phase).
|
|
- `go run ./cmd/summer docs:build --check` exits 0.
|
|
</acceptance_criteria>
|
|
<done>The walkthrough's layout is pinned to the scaffolder by a test, the page lists the exact commands and the oddities are logged, and the concept map and index link it.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<threat_model>
|
|
## Trust Boundaries
|
|
|
|
| Boundary | Description |
|
|
|----------|-------------|
|
|
| walkthrough code → developers' plugins | The canonical example is copied into real plugins |
|
|
| walkthrough tests → Postgres | Docker tests create and drop databases |
|
|
|
|
## STRIDE Threat Register
|
|
|
|
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
|
|-----------|----------|-----------|----------|-------------|-----------------|
|
|
| T-11.1-16 | Tampering / Elevation of privilege | docs/examples/blog routes, models, controllers | medium | mitigate | Writes through lagoon.Fill with an allow-list (TestPostTableAndFill), admin controller declares a permission (TestPostsControllerDeclaration), handler and command use bound parameters only |
|
|
| T-11.1-17 | Denial of service (test isolation) | docs/examples/blog/postgres_test.go | low | mitigate | testcontainers Postgres with a dedicated ICU database per run; never a developer or shared database |
|
|
| T-11.1-SC | Tampering | npm/pip/cargo/go installs | high | accept | This plan adds no module or package; testcontainers-go is already in go.mod |
|
|
</threat_model>
|
|
|
|
<verification>
|
|
- `go vet ./... && go test ./docs/examples/... -count=1` green with Docker; `go test -short ./docs/examples/...` green without.
|
|
- `go run ./cmd/summer docs:build --check` exits 0; `scripts/check-phase11.1.sh --docs --forbidden` pass.
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- SC5: the `acme/blog` porting walkthrough exists and its code (models, migrations, routes, admin controller, console command) is verified under criterion 4.
|
|
- SC4: every Go and YAML snippet on the walkthrough page is a src= copy of code that compiles and runs under `go test ./...`.
|
|
</success_criteria>
|
|
|
|
## Artifacts this phase produces
|
|
|
|
- Package tree `git.golem15.com/golem15/summercms/docs/examples/blog` with sub-packages `models`, `updates`, `controllers`, `console`, `classes`, `jobs`, `middleware`; symbols `blog.Plugin`, `models.Post`, `updates.CreatePosts`, `updates.AddPublishedAt`, `controllers.PostsController`, `console.PublishCommand`.
|
|
- Application command declared by the example: `blog:publish <slug>`; route `GET /api/blog/posts`; permission `acme.blog.access_posts` (or the name chosen, recorded in the SUMMARY).
|
|
- Tests: `TestPluginActivates`, `TestRoutesRegistered`, `TestPostTableAndFill`, `TestMigrationIDs`, `TestPostsControllerDeclaration`, `TestPublishCommandShape`, `TestMigrateUpAndRollback`, `TestPostsRouteAgainstDatabase`, `TestPublishCommandAgainstDatabase`, `TestScaffoldLayout`.
|
|
- Page `docs/setup/porting-a-plugin.md`; links from `docs/setup/coming-from-wintercms.md` and `docs/index.md`; any scaffolder gap todos under `.planning/todos/pending/`.
|
|
|
|
<output>
|
|
Create `.planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-05-SUMMARY.md` when done
|
|
</output>
|