Files
summercms/.planning/phases/15-journal-plugin/15-CONTEXT.md
2026-10-06 01:28:58 +02:00

15 KiB

Phase 15: Journal plugin - Context

Gathered: 2026-10-06 Status: Ready for planning

## Phase Boundary

The Golem15 Journal plugin is ported to Go as sm-journal-plugin and mounted in sm-grzybyfunkcjonalne-app the same way sm-user-plugin is mounted in a host application. A blog can run on SummerCMS without the PHP plugin: models, migrations, YAML-driven admin screens, permissions, import/export, and /_journal/api/v1 (public reads and backend-Bearer writes) match the frozen PHP tree.

The first real site (HTML, reusable blog views, live grzybyfunkcjonalne.pl pages) is Phase 16. This phase only has to prove the plugin mounts, migrates, the admin screens work, and the API answers.

summercms.go is touched only when a framework feature is missing. The one named framework addition is a cabana markdown field (and preview). Apparatus is already dissolved into the framework.

Out of scope: sm-translate-plugin itself (insert as its own phase before this one; Journal depends on it), Winter.Pages menu item types, the dashboard report widget, Apparatus backend personal API tokens (g15_), sitemap, and any edit to wn-journal-plugin.

## Implementation Decisions

PHP source of truth

  • D-01: The port contract is github.com/golem15com/wn-journal-plugin at git SHA 02110eb (2026-08-27), the pin currently checked out in Płytarium. That tree is newer than grzybyfunkcjonalne.pl (2ad25e0) and the Winter starter (7237ed0); it includes tags, /_journal/api/v1, Typesense, and the security fixes. — Reversibility: costly — every model, table, YAML key and route in the Go plugin is copied from this tree.
  • D-02: Freeze at 02110eb. Planner and researcher must not silently pick a newer master. — Reversibility: reversible — a later discussion can re-pin.
  • D-03: Downstream agents read the frozen source from /media/nvme/dev/golem15/fonoteka/plugins/golem15/journal (already at that SHA). Do not clone a second copy unless that path is missing or the SHA does not match.
  • D-04: Binding from that tree: models, tables, YAML forms/lists/filters, permission codes, console commands, and /_journal/api/v1 request/response shapes. Same bar as the user plugin port.
  • D-05: PHPUnit under tests/ (security access/XSS/mass-assignment plus PostRedactor) is a behavioral spec. Researcher maps each test to a Go case; the phase's last plan covers them.
  • D-06: The Go plugin ships English and Polish phrasebook files only. The other 19 PHP locales stay in PHP until a later request.
  • D-07: This phase does not edit or PR wn-journal-plugin. An urgent PHP upgrade on a live Winter site before that site migrates is allowed as operations. If it lands before plan or execute, re-pin the SHA rather than mixing trees.
  • D-08: Embed the same admin nav SVG (assets/images/journal-icon.svg) in the Go plugin.

What ships this phase

  • D-09: Do not port Golem15.Translate inside Phase 15. Insert sm-translate-plugin as its own roadmap phase before Phase 15 ($gsd-phase after this discussion). — Reversibility: one-way — Journal's translatable fields and Phase 16 blogs assume that plugin exists first.
  • D-10: Once that translate phase exists, Journal depends on it. Title, slug, content and the other PHP $translatable attributes stay translatable, so Phase 16 blogs work. — Reversibility: costly — the storage shape and admin fields couple to the translate plugin's contract.
  • D-11: Add a cabana markdown field (and preview) in summercms.go this phase, the same "framework first" pattern as Phase 12.1. Journal's post content uses it instead of a textarea stub. — Reversibility: costly — a new field type in the typed admin schema and generated TS types.
  • D-12: Port search_use_typesense, keep it off by default, and wire it to the Phase 11 search stack when enabled. A fresh install never contacts Typesense. — Reversibility: reversible — the setting and the hook can sit unused.
  • D-13: CSV import/export and the journal:export-posts / journal:import-posts commands ship now (admin + CLI). Winter.Pages menu item types and the dashboard report widget are deferred (no pages plugin, no dashboard widgets in the admin SPA).

Public HTTP

  • D-14: Ship the full /_journal/api/v1 surface this phase: anonymous GET posts, posts/{slug}, categories, tags, rss; backend-authenticated POST/PUT/DELETE posts, featured-image and media upload. Shapes follow API.md and routes.php in the frozen tree. — Reversibility: one-way — Phase 16 views and any importer will call this contract.
  • D-15: Write routes authenticate with the Phase 9 backend Bearer guard (admin session). They do not use frontend sm-user-plugin user.api_token (wrong identity) and they do not fold Apparatus PersonalApiToken / g15_ tokens (still .planning/todos/pending/backend-admin-api-tokens.md). — Reversibility: costly — clients that later expect g15_ tokens would need a second guard.
  • D-16: Proof is API.md plus the mapped PHPUnit tests. Journal is not added to the Płytarium tide 154-route harness.
  • D-17: Keep PHP's two limiter buckets (journal-public-api and journal-api, 120/min) and the CORS path _journal/api/*.

Where it boots

  • D-18: The proof host is the empty repo git@git.golem15.com:golem15/sm-grzybyfunkcjonalne-app.git (already created). Not an in-module testhost and not fonoteka.go / sm-summercmsio-app. — Reversibility: one-way — the Go module path and submodule layout land in that app.
  • D-19: Phase 15 proof on that host: Journal mounts, migrations run, admin screens work, /_journal/api/v1 answers. Live site HTML and reusable blog views stay Phase 16.
  • D-20: Local checkout: /media/nvme/dev/golem15/summercms.io/summercms/sm-grzybyfunkcjonalne-app/ (sibling of summercms.go, same as fonoteka.go and sm-summercmsio-app).
  • D-21: The app mounts sm-journal-plugin, sm-translate-plugin and sm-user-plugin (submodules at plugins/golem15/{journal,translate,user}), following .planning/notes/core-plugins-own-repos.md.
  • D-22: The plugin remote git@git.golem15.com:golem15/sm-journal-plugin.git already exists and is public. Module path git.golem15.com/golem15/sm-journal-plugin, package journal, plugin ID golem15.journal.
  • D-23: Local plugin checkout: /media/nvme/dev/golem15/summercms.io/summercms/sm-journal-plugin/ (sibling of summercms.go). The host mounts it as a submodule at plugins/golem15/journal with a go.work replace, same as sm-user-plugin in fonoteka.go.

Claude's Discretion

  • Markdown field YAML type name, Go identifiers and SPA registry keys, provided they follow existing cabana fail-loud rules and update the module README, docs/, admin OpenAPI, generated TS types and committed dist/ in the same change.
  • Plan count and split, subject to the plan-count checkpoint and "unit tests are the last plan". Framework markdown work may land in an early plan and be tagged if a framework version bump is needed.
  • Exact app wiring (summer.yaml, go.work, embed vs not) inside sm-grzybyfunkcjonalne-app, as long as D-18 to D-21 hold.
  • How Winter component pages (journalPost, journalPosts, …) are documented as a Phase 16 successor — they are not implemented here.
  • Run the security-review agent: the phase touches the plugin API (pact), authorization (golem15.journal.*, draft visibility) and public HTTP.

<canonical_refs>

Canonical References

Downstream agents MUST read these before planning or implementing.

Roadmap and prior decisions

  • .planning/ROADMAP.md § Phase 15 — goal, repos, success criteria
  • .planning/ROADMAP.md § Phase 16 — site HTML and reusable views stay there
  • .planning/PROJECT.md — core plugin contracts; PHP originals are not changed
  • .planning/notes/core-plugins-own-repos.md — sm-<name>-plugin layout, module path, submodule mount
  • .planning/notes/apparatus-dissolved-into-framework.md — why Journal does not depend on an Apparatus plugin
  • .planning/phases/12.1-user-plugin-admin-screens/12.1-CONTEXT.md — YAML admin screens, permissions, framework-first extras
  • .planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-CONTEXT.md — typed schema, filters (D-12 already used Journal's config_filter.yaml), backend Bearer
  • .planning/phases/11-jobs-realtime-and-search-infrastructure/11-CONTEXT.md — search stack Journal wires when Typesense is enabled
  • .planning/todos/pending/backend-admin-api-tokens.md — Apparatus PersonalApiToken stays deferred (D-15)
  • .planning/todos/pending/sitemap-plugin-port.md — sitemap is Phase 16 / its own plugin

PHP contract (read-only) — /media/nvme/dev/golem15/fonoteka/plugins/golem15/journal at SHA 02110eb

  • Plugin.php — requires Translate + Apparatus, permissions, navigation, settings, Scout/Typesense guard, Pages menu events
  • routes.php — public and token-auth /_journal/api/v1 groups, limiter names
  • API.md — request/response contract for the JSON API
  • UPGRADE.md — breaking-change notes already in this pin
  • README.md — feature list and Winter components (components are Phase 16)
  • models/Post.php, Category.php, Tag.php, Settings.php, PostImport.php, PostExport.php
  • controllers/Posts.php, Categories.php, Tags.php, controllers/api/
  • controllers/posts/config_filter.yaml and sibling YAML under controllers/ and models/
  • console/ExportPosts.php, console/ImportPosts.php
  • formwidgets/JournalMarkdown.php, MLJournalMarkdown.php
  • tests/security/, tests/unit/models/PostRedactorTest.php
  • assets/images/journal-icon.svg
  • lang/en/, lang/pl/

Go references

  • ../fonoteka.go/plugins/golem15/user/ (sm-user-plugin) — mount pattern, plugin.go, admin controllers, README
  • modules/cabana/ — list/form/filter schema, field registry; markdown field lands here
  • modules/pact/capabilities.go — HasAdminControllers, permissions, navigation
  • docs/backend/admin-controllers.md, docs/backend/forms.md, docs/backend/lists-and-filters.md
  • docs/examples/blog/ — in-root toy blog only; not the Journal port and not the host
  • ../sm-summercmsio-app/ — sibling host wiring precedent (go.work, summer.yaml, submodules)

New remotes (already created)

  • git@git.golem15.com:golem15/sm-journal-plugin.git — public, empty
  • git@git.golem15.com:golem15/sm-grzybyfunkcjonalne-app.git — empty proof host

First blog PHP site (Phase 16; do not port the theme here)

  • /media/nvme/dev/golem15/grzybyfunkcjonalne.pl — living Winter site; its Journal submodule is an older ancestor of 02110eb

</canonical_refs>

<code_context>

Existing Code Insights

Reusable Assets

  • sm-user-plugin (../fonoteka.go/plugins/golem15/user/): compiled plugin init, summer.yaml listing, submodule + go.work replace, pact.HasAdminControllers, permission codes, phrasebook en/pl, README that never names a consuming application.
  • cabana: list, form, filters (switch, daterange, model scopes — Journal's config_filter.yaml was a Phase 9 reference), relation, fileupload, datepicker, bulk/record actions, preview, row state. No markdown field yet (D-11).
  • Phase 11 search / Typesense client: Journal's optional search_use_typesense hook (D-12).
  • Phase 9 backend Bearer guard: Journal write API (D-15).
  • docs/examples/blog: scaffold layout only. Do not treat it as Golem15.Journal.

Established Patterns

  • Shared core plugins live in git.golem15.com/golem15/sm-<name>-plugin, package name plain (journal), plugin ID golem15.journal, mounted at plugins/golem15/journal.
  • Unknown YAML keys and unregistered actions fail boot. Writes use requireAjax under the backend guard with RequiredPermissions.
  • Framework changes update module README, docs/, admin OpenAPI, generated TS types and committed boardwalk dist/ in the same change. Neutral names only (acme, blog).
  • PHP originals are not changed. Shipped migrations are append-only. Unit tests are the last plan. go vet and go test ./... stay green in every repo this phase writes.

Integration Points

  • New repo sm-journal-plugin: models, updates, admin YAML, /_journal/api/v1, commands, lang, embedded icon.
  • New host sm-grzybyfunkcjonalne-app: summer.yaml + go.work requiring framework (local replace to ../summercms.go), submodules for journal, translate and user.
  • summercms.go modules/cabana + admin SPA: markdown field type (D-11).
  • Translate plugin (future phase before this one): Journal Requires it; translatable attributes (D-10).
  • Phase 16 consumes the API and admin; it does not redefine them.

</code_context>

## Specific Ideas
  • "We'll start fast" — use the empty sm-grzybyfunkcjonalne-app repo as the proof that Journal is migrated, rather than a disposable testhost.
  • Both remotes already exist: sm-journal-plugin (public) and sm-grzybyfunkcjonalne-app.
  • Urgent PHP Journal upgrades on a live Winter site before migration are allowed; re-pin the SHA if that happens before we plan or execute.
  • Frontend user.api_token was considered for Journal writes and rejected after clarifying it is not Apparatus PersonalApiToken.
## Deferred Ideas
  • Insert sm-translate-plugin as a new phase before Phase 15 ($gsd-phase). Journal will depend on it (D-09, D-10).
  • Phase 16: grzybyfunkcjonalne.pl HTML, reusable blog views, Winter component successors (journalPost, journalPosts, RSS page, author/tags pages).
  • Winter.Pages menu item types (journal-category, journal-post, …) until a pages plugin exists.
  • Admin dashboard report widget until the SPA has dashboard widgets.
  • Apparatus backend personal API tokens (g15_) — todo backend-admin-api-tokens.md.
  • Remaining 19 PHP locales beyond en and pl.
  • WYSIWYG / redactor as a second editor mode, unless research shows the markdown field must cover PHP's switch this phase.

Reviewed Todos (not folded)

  • sitemap-plugin-port.md: next blog / Phase 16; user chose leave on backlog.
  • 2026-10-01-benchmark-the-application-on-wintercms-vs-summercms.md, nest-framework-packages-under-modules.md, bonfire-duplicate-command-names.md, lagoon-readme-after-commit-callback-order.md, mailblocker-with-mailing.md, per-module-readmes-after-nest.md, readme-go-fences-src.md, refresh-fonoteka-readme.md, rewrite-summercms-readme.md, scaffold-admin-controller-incomplete.md, scaffold-same-second-migration-order.md, wristband-neutral-resource-default.md, scaffold-generated-header-and-command-deps.md, backend-admin-api-tokens.md: keyword matches only, not this phase.

Phase: 15-journal-plugin Context gathered: 2026-10-06