docs(15): create phase plan
This commit is contained in:
298
.planning/phases/15-journal-plugin/15-01-PLAN.md
Normal file
298
.planning/phases/15-journal-plugin/15-01-PLAN.md
Normal file
@@ -0,0 +1,298 @@
|
||||
---
|
||||
phase: 15-journal-plugin
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- ../sm-journal-plugin/go.mod
|
||||
- ../sm-journal-plugin/plugin.go
|
||||
- ../sm-journal-plugin/README.md
|
||||
- ../sm-journal-plugin/models/registry.go
|
||||
- ../sm-journal-plugin/models/post.go
|
||||
- ../sm-journal-plugin/models/category.go
|
||||
- ../sm-journal-plugin/models/tag.go
|
||||
- ../sm-journal-plugin/models/settings.go
|
||||
- ../sm-journal-plugin/models/post_translatable_smoke_test.go
|
||||
- ../sm-journal-plugin/updates/registry.go
|
||||
- ../sm-journal-plugin/updates/postgres_test.go
|
||||
- ../sm-journal-plugin/updates/202610060001_create_golem15_journal_posts.go
|
||||
- ../sm-journal-plugin/updates/202610060002_create_golem15_journal_categories.go
|
||||
- ../sm-journal-plugin/updates/202610060003_create_golem15_journal_tags.go
|
||||
- ../sm-journal-plugin/updates/202610060004_create_golem15_journal_posts_categories.go
|
||||
- ../sm-journal-plugin/updates/202610060005_create_golem15_journal_posts_tags.go
|
||||
- ../sm-journal-plugin/updates/202610060006_create_golem15_journal_settings.go
|
||||
- ../sm-journal-plugin/updates/202610060007_add_author_slug_to_backend_users.go
|
||||
- ../sm-journal-plugin/lang/en/lang.yaml
|
||||
- ../sm-journal-plugin/lang/pl/lang.yaml
|
||||
- ../sm-grzybyfunkcjonalne-app/summer.yaml
|
||||
- ../sm-grzybyfunkcjonalne-app/go.work
|
||||
- ../sm-grzybyfunkcjonalne-app/go.mod
|
||||
- ../sm-grzybyfunkcjonalne-app/.gitmodules
|
||||
- ../sm-grzybyfunkcjonalne-app/config/http.yaml
|
||||
- ../sm-grzybyfunkcjonalne-app/boot_test.go
|
||||
- ../sm-grzybyfunkcjonalne-app/plugins.gen.go
|
||||
- ../sm-grzybyfunkcjonalne-app/main.go
|
||||
autonomous: true
|
||||
requirements: [D-01, D-02, D-03, D-04, D-06, D-07, D-09, D-10, D-17, D-18, D-19, D-20, D-21, D-22, D-23]
|
||||
estimate:
|
||||
tokens: 90000
|
||||
raw_tokens: 90000
|
||||
tasks: 3
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "D-01/D-02/D-03: Go schema and models are derived only from the read-only PHP tree at SHA 02110eb1c0c3861370b0b9b47b209a0702ac5d88."
|
||||
- "D-04: gormigrate creates final golem15_journal_posts, golem15_journal_categories, golem15_journal_tags, golem15_journal_posts_categories, golem15_journal_posts_tags, and singleton golem15_journal_settings, plus backend_users.golem15_bloghub_author_slug."
|
||||
- "D-09/D-10: Plugin.ID is golem15.journal, Requires is exactly golem15.translate, Post and Category implement Translatable/TranslatableIndexes/MorphName with PHP class strings."
|
||||
- "D-18/D-19/D-20/D-21/D-22/D-23: sm-grzybyfunkcjonalne-app mounts user, translate, and journal submodules and Activate returns those three plugin IDs."
|
||||
- "D-17: host config/http.yaml CORS paths include _journal/api/* and keep supports_credentials false."
|
||||
- "D-06: plugin HasLang catalogs exist for en and pl only."
|
||||
artifacts:
|
||||
- path: "../sm-journal-plugin/plugin.go"
|
||||
provides: "compiled plugin registration, Requires translate, migrations and models"
|
||||
contains: "golem15.journal"
|
||||
- path: "../sm-journal-plugin/updates/202610060001_create_golem15_journal_posts.go"
|
||||
provides: "squashed posts DDL"
|
||||
contains: "golem15_journal_posts"
|
||||
- path: "../sm-journal-plugin/models/post.go"
|
||||
provides: "Post TableName, MorphName, Translatable, TranslatableIndexes"
|
||||
contains: "Golem15\\Journal\\Models\\Post"
|
||||
- path: "../sm-grzybyfunkcjonalne-app/summer.yaml"
|
||||
provides: "three compiled plugins including journal"
|
||||
contains: "golem15.journal"
|
||||
- path: "../sm-grzybyfunkcjonalne-app/boot_test.go"
|
||||
provides: "proof-host boot of user+translate+journal"
|
||||
contains: "TestBootUserTranslateJournal"
|
||||
- path: "../sm-grzybyfunkcjonalne-app/config/http.yaml"
|
||||
provides: "D-17 CORS path for the Journal API"
|
||||
contains: "_journal/api/*"
|
||||
key_links:
|
||||
- from: "../sm-journal-plugin/plugin.go"
|
||||
to: "../sm-translate-plugin/classes/translatable.go"
|
||||
via: "Requires golem15.translate so Post/Category Translatable storage works"
|
||||
pattern: "golem15.translate"
|
||||
- from: "../sm-journal-plugin/models/post.go"
|
||||
to: "../sm-journal-plugin/updates/202610060001_create_golem15_journal_posts.go"
|
||||
via: "TableName matches squashed posts table"
|
||||
pattern: "golem15_journal_posts"
|
||||
- from: "../sm-grzybyfunkcjonalne-app/summer.yaml"
|
||||
to: "../sm-grzybyfunkcjonalne-app/plugins.gen.go"
|
||||
via: "summer build blank-imports sm-journal-plugin"
|
||||
pattern: "sm-journal-plugin"
|
||||
prohibitions:
|
||||
- requirement_id: D-07
|
||||
category: safety
|
||||
statement: "This phase must not modify any file under the PHP journal tree at /media/nvme/dev/golem15/fonoteka/plugins/golem15/journal"
|
||||
status: resolved
|
||||
verification: test
|
||||
- requirement_id: D-09
|
||||
category: safety
|
||||
statement: "Journal must not port Golem15.Translate inside this plugin and must not Require an apparatus plugin ID"
|
||||
status: resolved
|
||||
verification: test
|
||||
- requirement_id: D-18
|
||||
category: architecture
|
||||
statement: "Proof host must not be fonoteka.go, sm-summercmsio-app, or an in-module testhost"
|
||||
status: resolved
|
||||
verification: test
|
||||
---
|
||||
|
||||
## Phase Goal
|
||||
|
||||
**As a** application developer, **I want to** mount `sm-journal-plugin` in a host the same way `sm-user-plugin` mounts, **so that** a blog can run on SummerCMS without the PHP plugin.
|
||||
|
||||
This plan's slice: clone the plugin, squash the schema, persist one translatable Post, and boot the proof host with three plugins.
|
||||
|
||||
<objective>
|
||||
Clone sm-journal-plugin, ship squashed golem15_journal_* schema and models with Translatable, and mount the plugin in sm-grzybyfunkcjonalne-app with CORS and a three-plugin boot smoke.
|
||||
|
||||
Purpose: prove one production Post round-trip through compiled plugin + translate + Postgres + proof-host Activate before admin YAML or public HTTP.
|
||||
Output: sibling plugin repo, host gitlink/workspace/CORS, TestBootUserTranslateJournal, TestPostTranslatableSmoke.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@~/.codex/gsd-core/workflows/execute-plan.md
|
||||
@~/.codex/gsd-core/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/15-journal-plugin/15-CONTEXT.md
|
||||
@.planning/phases/15-journal-plugin/15-RESEARCH.md
|
||||
@.planning/phases/15-journal-plugin/15-PATTERNS.md
|
||||
@.planning/notes/core-plugins-own-repos.md
|
||||
@../sm-translate-plugin/go.mod
|
||||
@../sm-translate-plugin/plugin.go
|
||||
@../sm-grzybyfunkcjonalne-app/summer.yaml
|
||||
@../sm-grzybyfunkcjonalne-app/boot_test.go
|
||||
</context>
|
||||
|
||||
## Spec-less probe fallback
|
||||
|
||||
Phase 15 has no mapped REQUIREMENTS.md IDs (ROADMAP lists TBD). Spec-less requirement probing is a visible skip this run; no probe predicates are generated. Acceptance is D-01 through D-23 plus RESEARCH validation rows. Do not claim API-09 or QA-05 (Phase 20).
|
||||
|
||||
## Schema push gate
|
||||
|
||||
Skipped: this phase does not touch Prisma, Drizzle, Payload, TypeORM, or Supabase schema files. Journal DDL is plugin gormigrate (DATA-02). D-11 is not a new field type.
|
||||
|
||||
## Assumption-delta decision
|
||||
|
||||
<assumption_delta_decision>
|
||||
signal: optional
|
||||
term: optional editor / optional Typesense
|
||||
decision: no-change
|
||||
rationale: Public GET stays anonymous with an optional backend principal overlay copied from PHP; writes still require backend JWT. Typesense remains a settings kill-switch defaulting off. The identity primary does not move.
|
||||
</assumption_delta_decision>
|
||||
|
||||
## Artifacts this phase produces
|
||||
|
||||
- Module `git.golem15.com/golem15/sm-journal-plugin`, package `journal`, `Plugin.ID() == "golem15.journal"`, `Requires() == []string{"golem15.translate"}`.
|
||||
- Squashed gormigrate IDs `202610060001` through `202610060007` for the six journal tables plus `backend_users.golem15_bloghub_author_slug`.
|
||||
- `models.Post`, `models.Category`, `models.Tag`, `models.Settings` with Fillable/MorphName/Translatable as RESEARCH §1–§2.
|
||||
- Proof-host submodule at `plugins/golem15/journal`, `go.work` use, go.mod require+replace, CORS `_journal/api/*`, `TestBootUserTranslateJournal`.
|
||||
- Application-neutral plugin README (`the application`, example `blog`).
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="tracer">
|
||||
<name>Task 1: Clone the plugin, persist one en/pl Post title, and boot the host with three plugins</name>
|
||||
<reversibility rating="one-way">D-18/D-22/D-23 submodule path plugins/golem15/journal and module git.golem15.com/golem15/sm-journal-plugin become the durable layout; moving them later needs coordinated gitlink and workspace changes. Locked in CONTEXT — do not re-ask.</reversibility>
|
||||
<precondition>git ls-remote git@git.golem15.com:golem15/sm-journal-plugin.git and git@git.golem15.com:golem15/sm-grzybyfunkcjonalne-app.git succeed; PHP journal HEAD is 02110eb1c0c3861370b0b9b47b209a0702ac5d88; ../sm-grzybyfunkcjonalne-app already mounts user and translate.</precondition>
|
||||
<files>../sm-journal-plugin/go.mod, ../sm-journal-plugin/plugin.go, ../sm-journal-plugin/models/registry.go, ../sm-journal-plugin/models/post.go, ../sm-journal-plugin/models/post_translatable_smoke_test.go, ../sm-journal-plugin/updates/registry.go, ../sm-journal-plugin/updates/postgres_test.go, ../sm-journal-plugin/updates/202610060001_create_golem15_journal_posts.go, ../sm-grzybyfunkcjonalne-app/summer.yaml, ../sm-grzybyfunkcjonalne-app/go.work, ../sm-grzybyfunkcjonalne-app/go.mod, ../sm-grzybyfunkcjonalne-app/.gitmodules, ../sm-grzybyfunkcjonalne-app/boot_test.go, ../sm-grzybyfunkcjonalne-app/plugins.gen.go, ../sm-grzybyfunkcjonalne-app/main.go</files>
|
||||
<read_first>.planning/phases/15-journal-plugin/15-CONTEXT.md (D-01 through D-04, D-09, D-10, D-18 through D-23), .planning/phases/15-journal-plugin/15-RESEARCH.md (Verified PHP pin, Tables, Translatable, Host wiring, Pitfall 8 and Pitfall 9), .planning/phases/15-journal-plugin/15-PATTERNS.md (JR/go.mod, plugin.go, post.go, updates, Proof host), .planning/notes/core-plugins-own-repos.md, ../sm-translate-plugin/go.mod, ../sm-translate-plugin/plugin.go, ../sm-translate-plugin/updates/202610060001_create_golem15_translate_locales.go, ../sm-translate-plugin/updates/postgres_test.go, ../sm-translate-plugin/classes/translatable.go, ../sm-grzybyfunkcjonalne-app/summer.yaml, ../sm-grzybyfunkcjonalne-app/go.work, ../sm-grzybyfunkcjonalne-app/go.mod, ../sm-grzybyfunkcjonalne-app/boot_test.go, /media/nvme/dev/golem15/fonoteka/plugins/golem15/journal/models/Post.php, /media/nvme/dev/golem15/fonoteka/plugins/golem15/journal/Plugin.php</read_first>
|
||||
<action>Clone git@git.golem15.com:golem15/sm-journal-plugin.git into /media/nvme/dev/golem15/summercms.io/summercms/sm-journal-plugin (D-22, D-23). Read PHP only from /media/nvme/dev/golem15/fonoteka/plugins/golem15/journal at SHA 02110eb1c0c3861370b0b9b47b209a0702ac5d88 (D-01, D-02, D-03); do not clone a second PHP tree and do not edit that tree (D-07).
|
||||
|
||||
Create module git.golem15.com/golem15/sm-journal-plugin, package journal, Go 1.27.0. Require summercms, gormigrate v2.1.7, gorm v1.31.2, and the same testcontainers pins translate uses. Sibling replace git.golem15.com/golem15/summercms => ../summercms.go (pitfall 9). Do not require sm-user-plugin. Do not add undecided libraries.
|
||||
|
||||
Register party.Plugin in init. ID golem15.journal. Requires exactly golem15.translate (D-09). Do not Require apparatus. Implement HasMigrations and HasModels. Copy translate's TestMain/testcontainers harness into updates/postgres_test.go so isolated plugin tests run against real Postgres and treat missing Docker as failure unless -short.
|
||||
|
||||
Ship gormigrate ID 202610060001_create_golem15_journal_posts with explicit CREATE/INDEX/UNIQUE for RESEARCH §1 posts columns (id, user_id, redactor_id, title, slug unique, excerpt, content, content_html, published_at, published default false, is_pinned default false, metadata JSONB, sources JSONB, nullable timestamps). Rollback DROP TABLE. Never schema auto-sync. Squash to final golem15_journal_* names only.
|
||||
|
||||
Implement models.Post: TableName golem15_journal_posts; MorphName the PHP class string Golem15\Journal\Models\Post (D-10, pitfall 10); Translatable title, content, content_html, excerpt, metadata; TranslatableIndexes slug. Admin/API Fillable later; this tracer must not mass-assign redactor_id, content_html, or user_id from a map. jsonable metadata and sources as JSONB.
|
||||
|
||||
Smoke TestPostTranslatableSmoke: migrate translate then journal (D-19 migrations run), seed en/pl via translate migrations, insert one Post, SetTranslated English on host columns and Polish title/slug through translate helpers, read Polish back. This is the production tracer, not the Plan 04 matrix.
|
||||
|
||||
On the D-18 host at D-20, add gitlink plugins/golem15/journal to the journal remote, go.work use ./plugins/golem15/journal, go.mod require+replace to ./plugins/golem15/journal, summer.yaml id golem15.journal module git.golem15.com/golem15/sm-journal-plugin after translate (D-21). Regenerate plugins.gen.go and main.go with summer build (build ./cmd/summer from this repo into a temp binary, run it with cwd the host); do not hand-author those files after the first generation.
|
||||
|
||||
Rename/extend host TestBootUserTranslate to TestBootUserTranslateJournal: len(plugins)==3, IDs golem15.user then golem15.translate then golem15.journal (or topological equivalent that includes all three), journal is allowed in PluginIDs, migrations non-empty for journal. Remove the production-list exclusion of the journal plugin ID (pitfall 8). Do not yet assert admin controller IDs or /_journal/api/v1 routes (Plans 02 and 03).</action>
|
||||
<verify>
|
||||
<automated>git ls-remote git@git.golem15.com:golem15/sm-journal-plugin.git && git -C /media/nvme/dev/golem15/fonoteka/plugins/golem15/journal rev-parse HEAD && go -C ../sm-journal-plugin vet ./... && go -C ../sm-journal-plugin test ./... -count=1 -v -run '^(TestPostTranslatableSmoke)$' && go -C ../sm-grzybyfunkcjonalne-app vet ./... && go -C ../sm-grzybyfunkcjonalne-app test ./... -count=1 -v -run '^(TestBootUserTranslateJournal)$'</automated>
|
||||
<fails_when>Non-zero exit; PHP SHA is not 02110eb1c0c3861370b0b9b47b209a0702ac5d88; output contains "--- FAIL", "--- SKIP", or "no tests to run"; either named PASS line is absent; journal remote is missing.</fails_when>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- Local checkout exists at ../sm-journal-plugin with origin git@git.golem15.com:golem15/sm-journal-plugin.git.
|
||||
- Plugin module/package/ID match D-22 and Requires is exactly golem15.translate.
|
||||
- Migration 202610060001 creates golem15_journal_posts; plugin source does not create rainlab-era journal table names.
|
||||
- Post.MorphName is Golem15\Journal\Models\Post; TranslatableIndexes contains slug.
|
||||
- TestPostTranslatableSmoke stores English on the host row and Polish in golem15_translate_attributes.
|
||||
- Host summer.yaml, go.work, go.mod replace, .gitmodules, and plugins.gen.go all name sm-journal-plugin / golem15.journal.
|
||||
- TestBootUserTranslateJournal activates three plugins including golem15.journal.
|
||||
- git diff -- /media/nvme/dev/golem15/fonoteka/plugins/golem15/journal is empty.
|
||||
</acceptance_criteria>
|
||||
<done>A compiled journal plugin persists one translatable Post and the proof host boots user, translate, and journal.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Add categories, tags, pivots, settings singleton, and author_slug</name>
|
||||
<reversibility rating="costly">backend_users.golem15_bloghub_author_slug is a plugin-owned ALTER of a framework table; dropping it later needs a coordinated rollback in every host that migrated Journal.</reversibility>
|
||||
<files>../sm-journal-plugin/models/category.go, ../sm-journal-plugin/models/tag.go, ../sm-journal-plugin/models/settings.go, ../sm-journal-plugin/models/registry.go, ../sm-journal-plugin/updates/registry.go, ../sm-journal-plugin/updates/202610060002_create_golem15_journal_categories.go, ../sm-journal-plugin/updates/202610060003_create_golem15_journal_tags.go, ../sm-journal-plugin/updates/202610060004_create_golem15_journal_posts_categories.go, ../sm-journal-plugin/updates/202610060005_create_golem15_journal_posts_tags.go, ../sm-journal-plugin/updates/202610060006_create_golem15_journal_settings.go, ../sm-journal-plugin/updates/202610060007_add_author_slug_to_backend_users.go</files>
|
||||
<read_first>.planning/phases/15-journal-plugin/15-RESEARCH.md (Tables, Fillable, Settings singleton, companion author_slug, assumption A4), .planning/phases/15-journal-plugin/15-PATTERNS.md (category.go, tag.go, settings.go, updates table), ../sm-translate-plugin/updates/registry.go, ../fonoteka.go/plugins/golem15/user/models/user_group.go, ../fonoteka.go/plugins/golem15/user/updates/202609220005_extend_users.go, modules/cabana/contracts.go (BackendUser TableName), modules/cabana/example_controller_test.go (BlogSettings), /media/nvme/dev/golem15/fonoteka/plugins/golem15/journal/models/Category.php, /media/nvme/dev/golem15/fonoteka/plugins/golem15/journal/models/Tag.php, /media/nvme/dev/golem15/fonoteka/plugins/golem15/journal/models/Settings.php, /media/nvme/dev/golem15/fonoteka/plugins/golem15/journal/models/settings/fields.yaml</read_first>
|
||||
<action>Expand from the proven posts table. Add gormigrate IDs 202610060002 through 202610060007 registered in updates.All in ID order (D-04).
|
||||
|
||||
Categories: columns id, name, slug unique indexed, code nullable, description, parent_id indexed nullable, nest_left, nest_right, nest_depth, timestamps. Keep nest_* in DDL; no NestedTree reorder UI this phase. Category Fillable only name, slug, code, description, parent_id (JOURNAL-002 — nest_* not fillable). Translatable name, description; TranslatableIndexes slug; MorphName Golem15\Journal\Models\Category.
|
||||
|
||||
Tags: id, name, slug unique indexed, description, timestamps. Fillable only name, slug, description (JOURNAL-001). Tag is not translatable.
|
||||
|
||||
Pivots golem15_journal_posts_categories and golem15_journal_posts_tags with the PHP pair of FKs. Post belongsToMany categories and tags through those tables. Post belongsTo cabana.BackendUser on user_id. redactor_id is a naked integer with no sm-user-plugin import.
|
||||
|
||||
Settings: dedicated table golem15_journal_settings ID=1, not PHP system_settings. Typed columns from Settings.php rules plus fields.yaml: show_all_posts bool default true, use_rich_editor bool default false, search_use_typesense bool default false, search_title_weight int default 5, search_excerpt_weight int default 3, search_content_weight int default 1, rss_enabled bool default true, rss_include_content bool default false, rss_title, rss_description, rss_language default en-us, rss_copyright, rss_image_url, rss_posts_per_feed int default 20. Fillable those columns; Rules match PHP. use_rich_editor is stored for later; compile-time ignore is Plan 02.
|
||||
|
||||
Author slug: ALTER TABLE backend_users ADD COLUMN IF NOT EXISTS golem15_bloghub_author_slug TEXT UNIQUE. Do not put this column in lagoon framework migrations. Rollback drops the column only if safe (IF EXISTS).
|
||||
|
||||
Add TestJournalTables that migrates the journal set and asserts the six golem15_journal_* table names plus backend_users.golem15_bloghub_author_slug (D-04). Do not seed posts. Seed Uncategorized only if the frozen PHP seeder at this pin still inserts it (A4); RESEARCH grep found none — skip seed rows.
|
||||
|
||||
AttachRelations on Post for featured_images and content_images as attachMany using the same MorphName PHP class string and system_files. Do not import sm-user-plugin to type redactor.</action>
|
||||
<verify>
|
||||
<automated>go -C ../sm-journal-plugin vet ./... && go -C ../sm-journal-plugin test ./updates -count=1 -v -run '^(TestJournalTables)$'</automated>
|
||||
<fails_when>Non-zero exit; output contains "--- FAIL", "--- SKIP", or "no tests to run"; lacks "--- PASS: TestJournalTables".</fails_when>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- After migrate, information_schema lists golem15_journal_posts, golem15_journal_categories, golem15_journal_tags, both pivot tables, and golem15_journal_settings.
|
||||
- backend_users has golem15_bloghub_author_slug.
|
||||
- Category.Fillable is exactly name, slug, code, description, parent_id.
|
||||
- Tag.Fillable is exactly name, slug, description.
|
||||
- Settings table defaults search_use_typesense to false (D-12 storage ready).
|
||||
- No rainlab-era journal table names appear in updates/*.go.
|
||||
- models package does not import sm-user-plugin.
|
||||
</acceptance_criteria>
|
||||
<done>All D-04 tables exist and Category/Tag/Settings fillable boundaries match the PHP pin.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Add en/pl phrasebook, neutral README, and host CORS</name>
|
||||
<files>../sm-journal-plugin/lang/en/lang.yaml, ../sm-journal-plugin/lang/pl/lang.yaml, ../sm-journal-plugin/plugin.go, ../sm-journal-plugin/README.md, ../sm-grzybyfunkcjonalne-app/config/http.yaml</files>
|
||||
<read_first>.planning/phases/15-journal-plugin/15-CONTEXT.md (D-06, D-17), .planning/phases/15-journal-plugin/15-RESEARCH.md (CORS, phrasebook, Pitfall 13), .planning/phases/15-journal-plugin/15-PATTERNS.md (Phrasebook + README, Proof host CORS), ../sm-translate-plugin/lang/en/lang.yaml, ../sm-translate-plugin/README.md, ../sm-grzybyfunkcjonalne-app/config/http.yaml, /media/nvme/dev/golem15/fonoteka/plugins/golem15/journal/lang/en/lang.php, /media/nvme/dev/golem15/fonoteka/plugins/golem15/journal/lang/pl/lang.php, CLAUDE.md Documentation section</read_first>
|
||||
<action>Implement pact.HasLang. Port plugin.*, journal.*, post.*, category.*, tag.* UI keys from PHP lang/en/lang.php and lang/pl/lang.php into lang/en/lang.yaml and lang/pl/lang.yaml (D-06). Do not add the other 19 PHP locales. Phrasebook catalogs are UI strings, not model translation JSON.
|
||||
|
||||
Write plugin README following translate: H1 name, one-sentence summary, import line, Overview, Features, Usage, API reference, Dependencies, Testing. Say the application or host application; example names blog and acme. Do not name the proof host or Płytarium (pitfall 13). Document MorphName PHP class strings. Note Winter component pages (journalPost, journalPosts, and similar) as a Phase 16 successor, not implemented here.
|
||||
|
||||
Add CORS path _journal/api/* to host config/http.yaml alongside existing _user/api/* (D-17). Keep supports_credentials false. Do not invent handler-level CORS headers.
|
||||
|
||||
Host README may name itself; plugin README must not.</action>
|
||||
<verify>
|
||||
<automated>go -C ../sm-journal-plugin vet ./... && go -C ../sm-journal-plugin test ./... -short -count=1 && python3 -c 'import pathlib,sys; t=pathlib.Path("../sm-journal-plugin/README.md").read_text(); bad=["grzybyfunkcjonalne","Płytarium","fonoteka.go","plytarium"];
|
||||
sys.exit(1 if any(b.lower() in t.lower() for b in bad) else 0)' && grep -F "_journal/api/*" ../sm-grzybyfunkcjonalne-app/config/http.yaml && test -f ../sm-journal-plugin/lang/en/lang.yaml && test -f ../sm-journal-plugin/lang/pl/lang.yaml</automated>
|
||||
<fails_when>Non-zero exit; plugin tests FAIL; README python check exits 1; grep does not print _journal/api/*; either lang YAML is missing.</fails_when>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- lang/en/lang.yaml and lang/pl/lang.yaml exist and plugin LangFS embeds them.
|
||||
- No lang directory other than en and pl is added.
|
||||
- Plugin README uses the application / blog and does not name the proof host or Płytarium.
|
||||
- Host http.yaml CORS paths include both _user/api/* and _journal/api/*.
|
||||
- supports_credentials remains false.
|
||||
</acceptance_criteria>
|
||||
<done>Operators have en/pl UI catalogs, a neutral plugin README, and host CORS ready for /_journal/api/v1 in Plan 03.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| Plugin gormigrate → Postgres | Persistent blog schema and a column on backend_users |
|
||||
| Host gitlinks → compiled binary | Remote plugin commits become trusted build inputs |
|
||||
| Model Fillable → GORM | Untrusted maps must not set redactor_id, nest_*, user_id |
|
||||
| PHP tree → planner/executor | Frozen contract is read-only |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-15-04 | Elevation of Privilege | Category/Tag Fillable | high | mitigate | Allow-lists name/slug/description (+ category code, parent_id); nest_* and redactor_id excluded; tests in Plan 04 |
|
||||
| T-15-05 | Tampering | gormigrate DDL | high | mitigate | Explicit CREATE/ALTER from frozen SHA; no schema auto-sync; squash to golem15_journal_* only |
|
||||
| T-15-06 | Tampering | MorphName | high | mitigate | Hard-code PHP class strings; never reflect.Type.String; smoke writes translate attributes under that morph |
|
||||
| T-15-SC | Tampering | package installs | high | mitigate | No new external packages; goldmark wait until Plan 02 FormatHTML if imported |
|
||||
|
||||
ASVS L1: high threats are blocked by implementation and become fail-closed tests in Plan 04. T-15-01..T-15-03 are Journal HTTP threats owned by Plan 03 (VALIDATION map).
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
Run the three task commands. Confirm PHP journal git diff is empty. Confirm go -C ../sm-journal-plugin vet ./... and host TestBootUserTranslateJournal pass.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Plugin repo exists at D-23 with compiling module and squashed tables.
|
||||
- One Post en/pl round-trip works through translate storage.
|
||||
- Proof host Activate returns three plugins including golem15.journal.
|
||||
- CORS path _journal/api/* is present.
|
||||
- No PHP edits, no apparatus Require, no extra locales, no admin YAML or public routes yet.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/15-journal-plugin/15-01-SUMMARY.md` when done
|
||||
</output>
|
||||
Reference in New Issue
Block a user