Files
summercms/.planning/phases/15-journal-plugin/15-VERIFICATION.md
2026-10-06 19:30:41 +02:00

26 KiB
Raw Blame History

phase, verified, status, score, covered_files, covered_digest, covered_files_note, behavior_unverified, overrides_applied, decision_coverage, deferred, human_verification
phase verified status score covered_files covered_digest covered_files_note behavior_unverified overrides_applied decision_coverage deferred human_verification
15-journal-plugin 2026-10-06T17:29:31Z human_needed 22/22 must-haves verified
.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
v2:sha256:dffb5e2bb692df38cfc2be6a36f6e3305b4d7d81304c9c6fa15bf86362f2aa80 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. 0 0
honored total not_honored
23 23
truth addressed_in evidence
REQUIREMENTS.md API-09 (all 154 Płytarium routes) is mapped to Phase 15 in the stale traceability table Phase 20 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 addressed_in evidence
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 Phase 20 Phase 20 goal is the Płytarium definition of done. Journal is not a Płytarium route set.
test expected why_human
Boot sm-grzybyfunkcjonalne-app, sign in as an administrator holding golem15.journal.*, and open Journal in the admin SPA 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 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 expected why_human
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 git ls-remote against both remotes returns a HEAD SHA 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
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)