273 lines
26 KiB
Markdown
273 lines
26 KiB
Markdown
---
|
||
phase: 15-journal-plugin
|
||
verified: 2026-10-06T17:29:31Z
|
||
status: human_needed
|
||
score: 22/22 must-haves verified
|
||
covered_files:
|
||
- .planning/phases/15-journal-plugin/15-01-PLAN.md
|
||
- .planning/phases/15-journal-plugin/15-01-SUMMARY.md
|
||
- .planning/phases/15-journal-plugin/15-02-PLAN.md
|
||
- .planning/phases/15-journal-plugin/15-02-SUMMARY.md
|
||
- .planning/phases/15-journal-plugin/15-03-PLAN.md
|
||
- .planning/phases/15-journal-plugin/15-03-SUMMARY.md
|
||
- .planning/phases/15-journal-plugin/15-04-PLAN.md
|
||
- .planning/phases/15-journal-plugin/15-04-SUMMARY.md
|
||
- scripts/check-phase15.sh
|
||
covered_digest: "v2:sha256:dffb5e2bb692df38cfc2be6a36f6e3305b4d7d81304c9c6fa15bf86362f2aa80"
|
||
covered_files_note: "verification.fingerprint covers only paths under the summercms.go root. Sibling implementation read this pass: sm-journal-plugin d09edde (plugin.go, models, seven gormigrate steps, YAML admin, FormatHTML, /_journal/api/v1, commands, tests); sm-grzybyfunkcjonalne-app ff20e6c (summer.yaml, plugins.gen.go, go.work, gitlinks, boot_test.go, config/http.yaml). Host journal gitlink is d09edde. PHP pin /media/nvme/dev/golem15/fonoteka/plugins/golem15/journal HEAD 02110eb1c0c3861370b0b9b47b209a0702ac5d88, clean tree, not edited."
|
||
behavior_unverified: 0
|
||
overrides_applied: 0
|
||
decision_coverage:
|
||
honored: 23
|
||
total: 23
|
||
not_honored: []
|
||
deferred:
|
||
- truth: "REQUIREMENTS.md API-09 (all 154 Płytarium routes) is mapped to Phase 15 in the stale traceability table"
|
||
addressed_in: "Phase 20"
|
||
evidence: "Phase 20 goal: the parity harness is green on every manifest route. Plans 15-01..15-04 explicitly do not claim API-09; D-16 forbids adding Journal to the tide harness."
|
||
- truth: "REQUIREMENTS.md QA-05 (cutover: 154-route harness green against vue-fonoteka-app and fonoteka-mcp) is mapped to Phase 15 in the stale traceability table"
|
||
addressed_in: "Phase 20"
|
||
evidence: "Phase 20 goal is the Płytarium definition of done. Journal is not a Płytarium route set."
|
||
human_verification:
|
||
- test: "Boot sm-grzybyfunkcjonalne-app, sign in as an administrator holding golem15.journal.*, and open Journal in the admin SPA"
|
||
expected: "Journal nav appears; Posts/Categories/Tags list and create work; post content is an mlmarkdown field; a user without golem15.journal.access_posts cannot open Posts"
|
||
why_human: "Harvested from 15-VALIDATION.md manual row (D-19, SC-1). Named Go tests prove cabana HTTP 403/schema/create, not the running SPA chrome against the proof host."
|
||
- test: "Confirm the Gitea remotes git@git.golem15.com:golem15/sm-journal-plugin.git and git@git.golem15.com:golem15/sm-grzybyfunkcjonalne-app.git still exist"
|
||
expected: "git ls-remote against both remotes returns a HEAD SHA"
|
||
why_human: "Harvested from 15-VALIDATION.md. Local origin URLs match D-18/D-22; this pass could not complete git ls-remote within 8s (likely SSH/network)."
|
||
---
|
||
|
||
# Phase 15: Journal plugin Verification Report
|
||
|
||
**Phase Goal:** The Golem15 Journal plugin is ported to Go as `sm-journal-plugin` and mounts in a host application the same way `sm-user-plugin` does, so a blog can run on SummerCMS without the PHP plugin.
|
||
**Verified:** 2026-10-06T17:29:31Z
|
||
**Status:** human_needed
|
||
**Re-verification:** No — initial verification
|
||
|
||
**MVP note:** ROADMAP marks this phase `mode: mvp` but the ROADMAP goal is not in user-story form (`user-story.validate` → `valid: false`). PLAN.md goals are valid user stories. This report uses the PLAN story for User Flow Coverage and does not refuse verification.
|
||
|
||
**User story (from plans):** As an 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.
|
||
|
||
## User Flow Coverage
|
||
|
||
| Step | Expected | Evidence | Status |
|
||
|------|----------|----------|--------|
|
||
| Mount | Host lists journal beside user and translate as git submodules | `sm-grzybyfunkcjonalne-app/.gitmodules`, `git ls-files -s` mode `160000` for `plugins/golem15/{user,translate,journal}`; journal gitlink `d09edde`; `summer.yaml` + `plugins.gen.go` blank-import `sm-journal-plugin` | ✓ |
|
||
| Boot | Host Activate returns the three plugin IDs and assembles Journal routes | `boot_test.go:TestBootUserTranslateJournal` required by `check-phase15.sh --host` (PASS this pass) | ✓ |
|
||
| Public content | Anonymous GET lists published posts; categories/tags/rss exist | `TestJournalPublicList`, `TestJournalPublicCategories`, `TestJournalPublicTags`, `TestJournalRSS` in plugin suite (PASS this pass) | ✓ |
|
||
| Outcome | A blog can run on SummerCMS without the PHP plugin | Plugin module `git.golem15.com/golem15/sm-journal-plugin`, PHP pin clean at `02110eb1`, `--php` PASS; PHP components `journalPost` documented as Phase 16 successor in plugin README | ✓ |
|
||
|
||
## Goal Achievement
|
||
|
||
### Observable Truths
|
||
|
||
| # | Truth | Status | Evidence |
|
||
| --- | ------- | ---------- | -------------- |
|
||
| 1 | SC-1: Journal plugin repo exists with models, migrations and YAML admin screens, permission-gated | ✓ VERIFIED | `sm-journal-plugin` HEAD `d09edde`; models Post/Category/Tag/Settings; seven gormigrate IDs; controllers `golem15.journal.posts\|categories\|tags`; `TestPostsFormCompiles` (mlmarkdown/mltext); `TestPostsAdminForbidden` 403 without `access_posts` |
|
||
| 2 | SC-2: A host can mount the plugin as a submodule and serve public posts/categories/PHP-equivalent routes | ✓ VERIFIED | Host gitlink + `TestBootUserTranslateJournal` asserts GET/POST `/_journal/api/v1/posts`; `routes.go` public group; `TestJournalPublicList` PASS |
|
||
| 3 | SC-3: Plugin README stays application-neutral | ✓ VERIFIED | README uses `the application`, `acme`, `blog`; `--forbidden` greps README for fonoteka/płytarium/grzybyfunkcjonalne (PASS) |
|
||
| 4 | SC-4: Unit tests in the phase's last plan | ✓ VERIFIED | 15-04 added fillable/translatable/FormatHTML/migrations/API/search/integration tests; `check-phase15.sh --plugin` ran `go test ./...` and `-race` (PASS this pass) |
|
||
| 5 | D-01/D-02/D-03/D-07: Schema/models from PHP SHA `02110eb1`; PHP tree unmodified | ✓ VERIFIED | `check-phase15.sh --php`: HEAD `02110eb1c0c3861370b0b9b47b209a0702ac5d88`, porcelain empty |
|
||
| 6 | D-04: gormigrate creates `golem15_journal_*` tables, pivots, settings singleton, `backend_users.golem15_bloghub_author_slug`; rollback remigrates | ✓ VERIFIED | `updates/202610060001`–`007`; `TestJournalTables`; `TestJournalMigrationsRollbackAndRemigrate` in PLUGIN_REQUIRE (PASS) |
|
||
| 7 | D-05: RESEARCH §8 PHPUnit rows have named Go tests | ✓ VERIFIED | JOURNAL-001/002 `TestFillable`; JOURNAL-005 `TestJournal005DraftShow`; JOURNAL-006 `TestJournal006MediaUpload`; JOURNAL-003/004 substitute `TestFormatHTMLRejectsUnsafeHTML`; redactor_id `TestFillable` + `TestJournalAPIMassAssignRedactor` |
|
||
| 8 | D-06: HasLang catalogs exist for en and pl only | ✓ VERIFIED | `lang/en/lang.yaml`, `lang/pl/lang.yaml` only; `plugin.go` `HasLang` + `//go:embed lang` |
|
||
| 9 | D-08: Admin nav SVG is embedded | ✓ VERIFIED | `assets/images/journal-icon.svg` byte-identical to PHP; `TestJournalSVGEmbed` opens it from `AdminFS`; `NavigationItem` has no `IconSvg` (framework), so nav uses lucide `pencil` per RESEARCH |
|
||
| 10 | D-09/D-10: ID `golem15.journal`, Requires exactly `golem15.translate`, Post/Category Translatable/MorphName PHP strings, mltext/mlmarkdown | ✓ VERIFIED | `plugin.go` `Requires() []string{"golem15.translate"}`; `TestPostTranslatableSmoke`; `TestTranslatable`; `models/post/fields.yaml` `mltext`/`mlmarkdown` |
|
||
| 11 | D-11: post content YAML type is `mlmarkdown`; cabana markdown types not re-added this phase | ✓ VERIFIED | `TestPostsFormCompiles`; `TestCabanaMarkdownFieldTypes`; `git diff -- modules/cabana/field_markdown.go` empty; `--forbidden` refuses a dirty `field_markdown.go` |
|
||
| 12 | D-12: `search_use_typesense` defaults false; unpublished/gate-off not searchable; fresh save makes zero Typesense HTTP | ✓ VERIFIED | DDL `DEFAULT FALSE`; `TestSearchGateOff` must not skip (gate detector refuses skip); zero httptest hits this pass |
|
||
| 13 | D-13: `journal:export-posts` / `journal:import-posts` registered; Posts toolbar requires `golem15.journal.access_import_export` | ✓ VERIFIED | `TestJournalCommands` PASS; commands write JSON-per-slug files matching PHP ExportPosts/ImportPosts (not a Winter CSV framework — RESEARCH) |
|
||
| 14 | FormatHTML regenerates `content_html` on admin/API save with goldmark footnote/table/attribute + rejectUnsafe; cabana.RenderMarkdown not edited | ✓ VERIFIED | `controllers/posts.go` `preparePostWrite`; API `posts_helpers.go`; import path; `TestFormatHTMLRejectsUnsafeHTML`; `TestPostsAdminCreateSmoke` stores HTML |
|
||
| 15 | Without `access_other_posts`, list/form queries restrict to `user_id`; publish without `access_publish` is ForbiddenError | ✓ VERIFIED | `restrictToOwnPosts` in `controllers/posts.go`; `TestPostsAdminCreateSmoke/publish_without_access_publish` 403; JOURNAL-005 owner vs stranger vs `access_other_posts` |
|
||
| 16 | D-14: anonymous GET `/_journal/api/v1/posts`, `posts/{slug}`, `categories`, `tags`, `rss`; show is slug; per_page default 9 max 30 | ✓ VERIFIED | `routes.go`; `TestJournalPublicList` (per_page 9, drafts omitted); `TestJournalPerPageClamp`; `TestClampPerPage`; categories/tags/RSS named tests PASS |
|
||
| 17 | D-15: writes require cabana backend JWT (`aud=backend`); missing Bearer is JSON `{error:Authentication required}`; frontend audience rejected | ✓ VERIFIED | `requireBackendPrincipal`; `TestJournalWriteUnauthenticated`; `TestJournalWriteFrontendAudience`; `TestJournalFeaturedImageUnauthenticated` |
|
||
| 18 | D-16: Journal is not added to the Płytarium tide 154-route harness | ✓ VERIFIED | `TestNoTideJournalFixtures`; `--forbidden` greps fonoteka.go parity/tide and `modules/tide` for `/_journal/api/v1` (no hits) |
|
||
| 19 | D-17: buckets `journal-public-api` and `journal-api` Max 120; host CORS `_journal/api/*`; `supports_credentials` false | ✓ VERIFIED | `TestJournalBuckets`; `TestCORS`; `config/http.yaml`; 429 body remains surf `"Too Many Attempts."` (no Journal 429 writer) |
|
||
| 20 | D-18–D-23: proof host is `sm-grzybyfunkcjonalne-app` at the sibling path; three submodules; plugin module/ID/path | ✓ VERIFIED | `--layout` PASS; host not fonoteka.go / sm-summercmsio-app / in-module testhost; `go.mod` module `git.golem15.com/golem15/sm-journal-plugin`; origin URLs match D-18/D-22 |
|
||
| 21 | JOURNAL-005: unpublished or future `published_at` show is 404 with no `data` unless owner or `access_other_posts` | ✓ VERIFIED | `Show` in `controllers/api/posts.go`; `TestJournal005DraftShow`; `TestJournalEndToEnd` anonymous/stranger 404, owner/priv 200 |
|
||
| 22 | `scripts/check-phase15.sh --all` is fail-closed; 15-SECURITY-REVIEW.md closes every high T-15-* threat | ✓ VERIFIED | `--self-test` detector (fail/skip/zero/no-tests/race/missing-named); this pass executed every `--all` stage (self-test, php, layout, plugin, host, forbidden, security) — all PASS; review marks T-15-01..15 and T-15-SC mitigate |
|
||
|
||
**Score:** 22/22 truths verified (0 present, behavior-unverified)
|
||
|
||
### Deferred Items
|
||
|
||
Items not yet met but explicitly addressed in later milestone phases.
|
||
|
||
| # | Item | Addressed In | Evidence |
|
||
|---|------|-------------|----------|
|
||
| 1 | API-09 (154 Płytarium routes) stale map onto Phase 15 | Phase 20 | Phase 20 cutover goal; plans forbid claiming API-09; D-16 |
|
||
| 2 | QA-05 (cutover harness + Nuxt/MCP unchanged) stale map onto Phase 15 | Phase 20 | Phase 20 definition of done |
|
||
| 3 | Winter theme components `journalPost` / `journalPosts` / live site HTML | Phase 16 | CONTEXT deferred; plugin README names them as a later successor |
|
||
|
||
### Required Artifacts
|
||
|
||
| Artifact | Expected | Status | Details |
|
||
| -------- | ----------- | ------ | ------- |
|
||
| `../sm-journal-plugin/plugin.go` | compiled plugin, Requires translate, migrations/models | ✓ VERIFIED | ID `golem15.journal`; HasMigrations/Models/Lang/Commands/Routes; BucketProvider |
|
||
| `../sm-journal-plugin/updates/202610060001_create_golem15_journal_posts.go` | squashed posts DDL | ✓ VERIFIED | `golem15_journal_posts`; Rollback DROP |
|
||
| `../sm-journal-plugin/models/post.go` | TableName, MorphName, Translatable | ✓ VERIFIED | PHP class string `Golem15\Journal\Models\Post` |
|
||
| `../sm-grzybyfunkcjonalne-app/summer.yaml` | three compiled plugins including journal | ✓ VERIFIED | user, translate, journal |
|
||
| `../sm-grzybyfunkcjonalne-app/boot_test.go` | three-plugin boot | ✓ VERIFIED | `TestBootUserTranslateJournal` |
|
||
| `../sm-grzybyfunkcjonalne-app/config/http.yaml` | CORS `_journal/api/*` | ✓ VERIFIED | path present; `supports_credentials: false` |
|
||
| `../sm-journal-plugin/controllers/posts.go` | YAML controller, ListExtendQuery, FormatHTML | ✓ VERIFIED | `access_posts`; `preparePostWrite` |
|
||
| `../sm-journal-plugin/models/post/fields.yaml` | mlmarkdown content | ✓ VERIFIED | `type: mlmarkdown` |
|
||
| `../sm-journal-plugin/assets/images/journal-icon.svg` | embedded nav SVG | ✓ VERIFIED | bytes match PHP; go:embed in AdminFS |
|
||
| `../sm-journal-plugin/classes/format_html.go` | `func FormatHTML` | ✓ VERIFIED | goldmark + rejectUnsafe |
|
||
| `../sm-journal-plugin/console/export_posts.go` | `journal:export-posts` | ✓ VERIFIED | command name registered |
|
||
| `../sm-journal-plugin/models/settings/fields.yaml` | settings code journal | ✓ VERIFIED | `search_use_typesense` |
|
||
| `../sm-journal-plugin/routes.go` | `/_journal/api/v1` | ✓ VERIFIED | public GET + write groups |
|
||
| `../sm-journal-plugin/controllers/api/posts.go` | PHP `{error}` string JSON, slug show, draft 404 | ✓ VERIFIED | `Authentication required`; no `data` on draft 404 |
|
||
| `../sm-journal-plugin/controllers/api/media.go` | JOURNAL-006 media upload | ✓ VERIFIED | `media/upload`; `journal/` prefix |
|
||
| `../sm-journal-plugin/search.go` | beachcomber Gate | ✓ VERIFIED | reads ID=1 `search_use_typesense`; errors count as off |
|
||
| `../sm-journal-plugin/integration_test.go` | end-to-end | ✓ VERIFIED | `TestJournalEndToEnd` in package `journal_test` |
|
||
| `../sm-journal-plugin/models/fillable_test.go` | JOURNAL-001/002 | ✓ VERIFIED | `TestFillable` |
|
||
| `../sm-journal-plugin/controllers/api/posts_test.go` | clampPerPage unit | ✓ VERIFIED | HTTP JOURNAL-005 lives at plugin-root `journal_api_writes_test.go` (dispatch-authorized) |
|
||
| `scripts/check-phase15.sh` | fail-closed phase gate | ✓ VERIFIED | all stages PASS this pass |
|
||
| `.planning/phases/15-journal-plugin/15-SECURITY-REVIEW.md` | ASVS L1 | ✓ VERIFIED | T-15-01 present; `--security` PASS |
|
||
|
||
### Key Link Verification
|
||
|
||
| From | To | Via | Status | Details |
|
||
| ---- | --- | --- | ------ | ------- |
|
||
| `plugin.go` | `golem15.translate` | `Requires` | ✓ WIRED | `Requires() []string{"golem15.translate"}` |
|
||
| `models/post.go` | posts migration | `TableName` | ✓ WIRED | both `golem15_journal_posts` |
|
||
| `summer.yaml` | `plugins.gen.go` | `summer build` blank-import | ✓ WIRED | `_ "git.golem15.com/golem15/sm-journal-plugin"` |
|
||
| `controllers/posts.go` | `classes/format_html.go` | FormBeforeCreate/Update | ✓ WIRED | `classes.FormatHTML(post.Content)` |
|
||
| `admin_permissions.go` | `controllers/posts.go` | `RequiredPermissions` | ✓ WIRED | `golem15.journal.access_posts` |
|
||
| `console/export_posts.go` | `plugin.go` | `HasCommands` | ✓ WIRED | `console.ExportPosts` in `Commands()` |
|
||
| `routes.go` | `controllers/api/auth.go` | backend JWT on writes | ✓ WIRED | `requireBackendPrincipal` / `optionalBackendPrincipal` |
|
||
| `controllers/api/posts.go` | `classes/format_html.go` | store/update | ✓ WIRED | `posts_helpers.go` calls `FormatHTML` |
|
||
| `search.go` | `models/settings.go` | Gate ID=1 | ✓ WIRED | `SetGate` + `settingsSearchEnabled` |
|
||
| `integration_test.go` | `routes.go` | assembled handlers | ✓ WIRED | `TestJournalEndToEnd` hits public GET and Bearer POST |
|
||
| `check-phase15.sh` | plugin suite | `go -C` full, not `-short` | ✓ WIRED | `--plugin` PASS this pass |
|
||
| `15-SECURITY-REVIEW.md` | named tests | T-15-* | ✓ WIRED | `--security` requires mitigate on high IDs |
|
||
|
||
### Data-Flow Trace (Level 4)
|
||
|
||
| Artifact | Data Variable | Source | Produces Real Data | Status |
|
||
| -------- | ------------- | ------ | ------------------ | ------ |
|
||
| GET `/_journal/api/v1/posts` | `data` / `meta` | GORM `Model(&models.Post{})` + `lagoon.Paginate` | Yes — `TestJournalPublicList` seeds rows and asserts listed slug | ✓ FLOWING |
|
||
| Admin POST posts | stored `Post` + translate attributes | cabana form → GORM + `golem15_translate_attributes` | Yes — `TestPostsAdminCreateSmoke` / `TestJournalEndToEnd` | ✓ FLOWING |
|
||
| FormatHTML `content_html` | `post.ContentHTML` | goldmark convert of `content` | Yes — create smoke asserts markup, rejects script | ✓ FLOWING |
|
||
| Settings gate | `search_use_typesense` | `golem15_journal_settings` id=1 | Yes — default false in DDL; `TestSearchGateOff` | ✓ FLOWING |
|
||
| Nav SVG | AdminFS file | `//go:embed assets/images/journal-icon.svg` | Bytes present; SPA nav uses lucide `pencil` (no IconSvg field) | ✓ FLOWING (embed); SPA icon is lucide |
|
||
|
||
### Behavioral Spot-Checks
|
||
|
||
| Behavior | Command | Result | Status |
|
||
| -------- | ------- | ------ | ------ |
|
||
| PHP pin frozen and clean | `bash scripts/check-phase15.sh --php` | `phase15 php passed (02110eb1…)` | ✓ PASS |
|
||
| Host layout + gitlinks + CORS | `bash scripts/check-phase15.sh --layout` | `phase15 layout passed` | ✓ PASS |
|
||
| Plugin vet + full suite + race | `bash scripts/check-phase15.sh --plugin` | `phase15 plugin passed` (~85s) | ✓ PASS |
|
||
| Host vet + `TestBootUserTranslateJournal`/`TestCORS` + build | `bash scripts/check-phase15.sh --host` | `phase15 host passed` | ✓ PASS |
|
||
| Forbidden surfaces | `bash scripts/check-phase15.sh --forbidden` | `phase15 forbidden passed` | ✓ PASS |
|
||
| Security review + VALIDATION sign-off | `bash scripts/check-phase15.sh --security` | `phase15 security passed` | ✓ PASS |
|
||
| Detector fail-closed | `bash scripts/check-phase15.sh --self-test` | `phase15 self-test passed` | ✓ PASS |
|
||
|
||
### Probe Execution
|
||
|
||
No `scripts/*/tests/probe-*.sh`. Phase-declared gate is `scripts/check-phase15.sh`.
|
||
|
||
| Probe | Command | Result | Status |
|
||
| ----- | ------- | ------ | ------ |
|
||
| `scripts/check-phase15.sh --self-test` | `bash scripts/check-phase15.sh --self-test` | exit 0 | PASS |
|
||
| `scripts/check-phase15.sh --php` | `bash scripts/check-phase15.sh --php` | exit 0 | PASS |
|
||
| `scripts/check-phase15.sh --layout` | `bash scripts/check-phase15.sh --layout` | exit 0 | PASS |
|
||
| `scripts/check-phase15.sh --plugin` | `bash scripts/check-phase15.sh --plugin` | exit 0 | PASS |
|
||
| `scripts/check-phase15.sh --host` | `bash scripts/check-phase15.sh --host` | exit 0 | PASS |
|
||
| `scripts/check-phase15.sh --forbidden` | `bash scripts/check-phase15.sh --forbidden` | exit 0 | PASS |
|
||
| `scripts/check-phase15.sh --security` | `bash scripts/check-phase15.sh --security` | exit 0 | PASS |
|
||
|
||
`--all` is those seven stages in order. Each stage was executed in this verification process.
|
||
|
||
### Requirements Coverage
|
||
|
||
ROADMAP lists **Requirements: TBD**. REQUIREMENTS.md has **no Journal IDs**. Plan frontmatter IDs are CONTEXT decisions D-01–D-23. Every ID from plans is accounted for below.
|
||
|
||
| Requirement | Source Plan | Description | Status | Evidence |
|
||
| ----------- | ---------- | ----------- | ------ | -------- |
|
||
| D-01 | 01, 04 | Port contract PHP SHA `02110eb` | ✓ SATISFIED | `--php` HEAD match |
|
||
| D-02 | 01, 04 | Freeze at that SHA | ✓ SATISFIED | gate pins full SHA |
|
||
| D-03 | 01, 04 | Read frozen tree at fonoteka journal path | ✓ SATISFIED | `PHASE15_PHP` default path; tree present |
|
||
| D-04 | 01, 02, 04 | Models/tables/YAML/perms/commands/API | ✓ SATISFIED | migrations + YAML + routes + commands |
|
||
| D-05 | 04 | PHPUnit map | ✓ SATISFIED | named Go tests for §8 rows |
|
||
| D-06 | 01, 02, 04 | en+pl only | ✓ SATISFIED | two lang dirs; 02 prohibition |
|
||
| D-07 | 01, 04 | Do not edit PHP journal | ✓ SATISFIED | porcelain empty; test-tier prohibition wired in `--php` |
|
||
| D-08 | 02, 04 | Embed journal-icon.svg | ✓ SATISFIED | AdminFS + byte cmp |
|
||
| D-09 | 01, 04 | Do not port Translate inside Journal; Require translate not apparatus | ✓ SATISFIED | `Requires` exactly translate; no apparatus ID |
|
||
| D-10 | 01–04 | Translatable attributes | ✓ SATISFIED | Post/Category Translatable + MorphName |
|
||
| D-11 | 02, 04 | Use existing cabana markdown; no second field type | ✓ SATISFIED | mlmarkdown; field_markdown.go untouched |
|
||
| D-12 | 03, 04 | Typesense off by default | ✓ SATISFIED | `TestSearchGateOff` |
|
||
| D-13 | 02, 04 | Import/export CLI + toolbar (PHP JSON files, not Winter CSV) | ✓ SATISFIED | `TestJournalCommands` |
|
||
| D-14 | 03, 04 | Full `/_journal/api/v1` | ✓ SATISFIED | routes + public tests |
|
||
| D-15 | 03, 04 | Backend Bearer writes only | ✓ SATISFIED | 401 PHP shape; frontend aud rejected |
|
||
| D-16 | 03, 04 | Not tide | ✓ SATISFIED | `--forbidden` + `TestNoTideJournalFixtures` |
|
||
| D-17 | 01, 03, 04 | Limiters + CORS | ✓ SATISFIED | buckets + http.yaml |
|
||
| D-18 | 01, 04 | Proof host sm-grzybyfunkcjonalne-app | ✓ SATISFIED | sibling checkout + origin |
|
||
| D-19 | 01, 04 | Mount, migrate, admin, API (SPA chrome → human) | ✓ SATISFIED | boot + plugin HTTP; SPA listed under Human Verification |
|
||
| D-20 | 01, 04 | Local host path | ✓ SATISFIED | `/media/nvme/dev/golem15/summercms.io/summercms/sm-grzybyfunkcjonalne-app` |
|
||
| D-21 | 01, 04 | Submodules journal+translate+user | ✓ SATISFIED | gitlinks 160000 |
|
||
| D-22 | 01, 04 | Plugin remote/module/package/ID | ✓ SATISFIED | go.mod + plugin.go + origin |
|
||
| D-23 | 01, 04 | Local plugin checkout + host submodule replace | ✓ SATISFIED | sibling `sm-journal-plugin`; host `replace => ./plugins/golem15/journal` |
|
||
| API-09 | REQUIREMENTS.md map only | 154 Płytarium routes | deferred | Phase 20; not in any Phase 15 plan `requirements:` |
|
||
| QA-05 | REQUIREMENTS.md map only | Cutover harness | deferred | Phase 20; not in any Phase 15 plan `requirements:` |
|
||
|
||
**Orphaned REQUIREMENTS.md IDs mapped to Phase 15:** API-09, QA-05 — recorded under Deferred, not gaps. Plans treat them as Phase 20.
|
||
|
||
**Prohibitions (test-tier):** D-07, D-09, D-18, D-11, D-13 (no Pages/dashboard), D-06, D-15, D-14 (PHP error envelope), D-12, D-16 — each has wired `--php`/`--layout`/`--forbidden` or a named test. Not unverified.
|
||
|
||
### Decision Coverage
|
||
|
||
All trackable CONTEXT.md decisions are honored by shipped artifacts. **23/23 honored.**
|
||
|
||
### Test Quality Audit
|
||
|
||
| Test File | Linked Req | Active | Skipped | Circular | Assertion Level | Verdict |
|
||
|-----------|-----------|--------|---------|----------|-----------------|---------|
|
||
| `models/fillable_test.go` | D-05 JOURNAL-001/002 | yes | no | no | Value | OK |
|
||
| `journal_api_writes_test.go` | D-14/D-15/JOURNAL-005 | yes | no | no | Behavioral | OK |
|
||
| `journal_api_task3_test.go` | JOURNAL-006, RSS | yes | no | no | Behavioral | OK |
|
||
| `search_test.go` | D-12 | yes | no | no | Behavioral | OK |
|
||
| `classes/format_html_test.go` | JOURNAL-003/004 subst. | yes | no | no | Value | OK |
|
||
| `updates/migrations_test.go` | D-04 | yes | only `-short` | no | Behavioral | OK |
|
||
| `integration_test.go` | D-05/D-14/D-15 | yes | only `-short` | no | Behavioral | OK |
|
||
| empty `TestJournalEndToEnd` shims in other packages | gate `-run` hygiene | yes (no-op) | no | no | Existence | ⚠️ WARNING — real proof is `journal_test.TestJournalEndToEnd` |
|
||
|
||
**Disabled tests on requirements:** 0 blockers. `t.Skip("requires testcontainers postgres")` only when `-test.short`; full `--plugin` run is not `-short` and TestMain exits 1 if Docker fails.
|
||
**Circular patterns detected:** 0
|
||
**Insufficient assertions:** empty EndToEnd shims only (other tests cover the requirement)
|
||
|
||
### Anti-Patterns Found
|
||
|
||
| File | Line | Pattern | Severity | Impact |
|
||
| ---- | ---- | ------- | -------- | ------ |
|
||
| `plugin_test.go` (and sibling packages) | `TestJournalEndToEnd` empty | no-op test shim | ℹ️ Info | Keeps `go test ./... -run TestJournalEndToEnd` from printing `no tests to run`; real test is `integration_test.go` package `journal_test` |
|
||
| `updates/postgres_test.go` | rainlab table name in test | historical name check | ℹ️ Info | Test-only; `--forbidden` excludes `_test.go` |
|
||
| `go.mod` | require `sm-user-plugin` | test harness module | ℹ️ Info | Production `.go` files do not import it (`--forbidden` + `TestJournalEndToEndFixtureAbsentFromProduction`) |
|
||
| `15-SECURITY-REVIEW.md` | reviewer | executor self-review | ℹ️ Info | No dedicated auditor in this Cursor session; cited tests were re-run by `--plugin` this pass |
|
||
| production plugin sources | — | TBD/FIXME/XXX | none | No unreferenced debt markers |
|
||
|
||
### Human Verification Required
|
||
|
||
### 1. Journal admin SPA on the proof host
|
||
|
||
**Test:** Boot `sm-grzybyfunkcjonalne-app`. Sign in as an administrator holding `golem15.journal.*`. Open Journal nav. List and create a post whose content is `mlmarkdown`. Repeat without `golem15.journal.access_posts`.
|
||
**Expected:** Posts/Categories/Tags screens work; mlmarkdown editor is present; missing permission cannot open Posts.
|
||
**Why human:** Needs a running host, admin login, and visual confirmation. Automated tests cover HTTP 403/schema/create, not SPA chrome.
|
||
|
||
### 2. Gitea remotes still exist
|
||
|
||
**Test:** `git ls-remote git@git.golem15.com:golem15/sm-journal-plugin.git` and the proof-host remote.
|
||
**Expected:** Each remote returns a HEAD SHA.
|
||
**Why human:** Local `origin` URLs match D-18/D-22; this verifier could not complete `git ls-remote` within 8s.
|
||
|
||
### Gaps Summary
|
||
|
||
No blocking gaps. All 22 must-have truths are present, wired, and exercised by named tests or the fail-closed gate. Status is `human_needed` because harvested end-of-phase UAT (admin SPA and remote reachability) remains. ROADMAP/STATE were not marked complete.
|
||
|
||
PHPUnit XSS template tests (`\|raw_safe` on `.htm`) stay Phase 16 by RESEARCH; FormatHTML unsafe-tag tests substitute this phase and passed.
|
||
|
||
---
|
||
|
||
_Verified: 2026-10-06T17:29:31Z_
|
||
_Verifier: the agent (gsd-verifier)_
|