From a9237c8ec3a5473a897debdab4676304d257fd46 Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Thu, 29 Oct 2026 22:57:25 +0100 Subject: [PATCH] docs(34): phase verification report --- .../34-plugin-marketplace/34-VERIFICATION.md | 193 ++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 .planning/phases/34-plugin-marketplace/34-VERIFICATION.md diff --git a/.planning/phases/34-plugin-marketplace/34-VERIFICATION.md b/.planning/phases/34-plugin-marketplace/34-VERIFICATION.md new file mode 100644 index 00000000..009a3956 --- /dev/null +++ b/.planning/phases/34-plugin-marketplace/34-VERIFICATION.md @@ -0,0 +1,193 @@ +--- +phase: 34-plugin-marketplace +verified: 2026-10-29T23:40:00Z +status: gaps_found +score: 51/52 must-haves verified +covered_files: [".planning/phases/34-plugin-marketplace/34-01-PLAN.md", ".planning/phases/34-plugin-marketplace/34-01-SUMMARY.md", ".planning/phases/34-plugin-marketplace/34-02-PLAN.md", ".planning/phases/34-plugin-marketplace/34-02-SUMMARY.md", ".planning/phases/34-plugin-marketplace/34-03-PLAN.md", ".planning/phases/34-plugin-marketplace/34-03-SUMMARY.md", ".planning/phases/34-plugin-marketplace/34-04-PLAN.md", ".planning/phases/34-plugin-marketplace/34-04-SUMMARY.md", ".planning/phases/34-plugin-marketplace/34-05-PLAN.md", ".planning/phases/34-plugin-marketplace/34-05-SUMMARY.md", ".planning/phases/34-plugin-marketplace/34-06-PLAN.md", ".planning/phases/34-plugin-marketplace/34-06-SUMMARY.md", "README.md", "admin/src/api/schema.d.ts", "admin/src/api/types.ts", "admin/src/app/pageTitle.ts", "admin/src/app/router.ts", "admin/src/components/shell/AppShell.vue", "admin/src/components/shell/Breadcrumbs.vue", "admin/src/components/shell/PluginRail.vue", "admin/src/state/useSettings.ts", "admin/src/views/PluginsView.vue", "cmd/summer/main.go", "cmd/summer/plugin_commands_test.go", "docs/architecture/go-modules-and-workspaces.md", "docs/architecture/introduction.md", "docs/backend/admin-spa.md", "docs/backend/users-and-permissions.md", "docs/console/scaffolding.md", "docs/plugins/marketplace.md", "internal/build/build.go", "internal/build/build_test.go", "internal/build/fixtures_test.go", "internal/build/install.go", "internal/build/install_smoke_test.go", "internal/build/install_test.go", "internal/build/manifest.go", "internal/build/manifest_append_test.go", "internal/build/new.go", "internal/build/plugindirs.go", "internal/build/plugindirs_test.go", "internal/build/plugininfo_smoke_test.go", "internal/build/rollback.go", "internal/build/scaffold.go", "internal/build/source.go", "internal/build/testdata/new-no-plugin/plugins.gen.go.golden", "internal/build/testdata/new/main.go.golden", "internal/build/testdata/new/plugins.gen.go.golden", "internal/build/update.go", "modules/cabana/README.md", "modules/cabana/admin_openapi.go", "modules/cabana/backend_screens.go", "modules/cabana/http.go", "modules/cabana/navigation.go", "modules/cabana/openapi_conformance_test.go", "modules/cabana/plugins.go", "modules/cabana/plugins_smoke_test.go", "modules/cabana/plugins_test.go", "modules/cabana/security_coverage_test.go", "modules/cabana/token_routes_test.go", "modules/kiosk/README.md", "modules/kiosk/check.go", "modules/kiosk/checkt_test.go", "modules/kiosk/client.go", "modules/kiosk/client_test.go", "modules/kiosk/derive.go", "modules/kiosk/derive_test.go", "modules/kiosk/example_test.go", "modules/kiosk/identity.go", "modules/kiosk/identity_test.go", "modules/kiosk/kiosk.go", "modules/kiosk/manifest.go", "modules/kiosk/manifest_smoke_test.go", "modules/kiosk/manifest_test.go", "modules/kiosk/registry.go", "modules/kiosk/testdata/consts/README.md", "modules/kiosk/testdata/consts/go.mod", "modules/kiosk/testdata/consts/ids/ids.go", "modules/kiosk/testdata/consts/nested/README.md", "modules/kiosk/testdata/consts/nested/plugin.go", "modules/kiosk/testdata/consts/plugin.go", "modules/kiosk/testdata/consts/vendor.go", "modules/kiosk/testdata/framework/go.mod", "modules/kiosk/testdata/literal/README.md", "modules/kiosk/testdata/literal/go.mod", "modules/kiosk/testdata/literal/plugin.go", "modules/kiosk/testdata/literal/plugin.yaml", "modules/kiosk/testdata/other/README.md", "modules/kiosk/testdata/other/go.mod", "modules/kiosk/testdata/other/plugin.go", "modules/kiosk/version.go", "modules/kiosk/version_test.go", "modules/party/README.md", "modules/party/info.go", "modules/party/info_test.go", "modules/phrasebook/backend/lang/en/lang.yaml", "modules/phrasebook/backend/lang/pl/lang.yaml", "scripts/check-phase34.sh"] +covered_digest: "v2:sha256:0c6a14151d83c6cc9f8421f1e375f9768ea6efd43bdcc426ac498ec1c2c3de87" +behavior_unverified: 0 +overrides_applied: 0 +gaps: + - truth: "ROADMAP SC-1: Every public sm-*-plugin repo carries a plugin.yaml manifest (ID, module path, name, description, required framework version, required plugins) that the framework validates, and anyone can fetch the repo anonymously." + status: partial + reason: "The 20 sibling-checkout repositories carry a generated, checked plugin.yaml and fetch anonymously. Four further public Go SummerCMS plugin repositories on git.golem15.com (golem15/sm-feedback-plugin, golem15/sm-golem-plugin, golem15/sm-presentations-plugin, golem15/sm-quizzes-plugin; all private=false, all with a go.mod module path) have no plugin.yaml. 34-02-PLAN scoped them out as 'user decision 5' (no sibling checkout), but no verification override records that narrowing of the roadmap contract, and no later phase picks them up." + artifacts: + - path: "https://git.golem15.com/golem15/sm-feedback-plugin" + issue: "public plugin repo, no plugin.yaml, no TestPluginManifest" + - path: "https://git.golem15.com/golem15/sm-golem-plugin" + issue: "public plugin repo, no plugin.yaml, no TestPluginManifest" + - path: "https://git.golem15.com/golem15/sm-presentations-plugin" + issue: "public plugin repo, no plugin.yaml, no TestPluginManifest" + - path: "https://git.golem15.com/golem15/sm-quizzes-plugin" + issue: "public plugin repo, no plugin.yaml, no TestPluginManifest" + missing: + - "Either: clone the four repositories next to the framework and repeat the 34-02 rollout (summer plugin:manifest, manifest_test.go with kiosk.CheckT, framework v0.6.0 require), add them to PLUGIN_REPOS in scripts/check-phase34.sh and to the release ledger" + - "Or: record user decision 5 as a verification override in this file (suggested YAML in the report) so SC-1 reads 'every public plugin in the launch set'" +human_verification: + - test: "After the user-gated deploy, open https://summercms.io/marketplace: list, a search, a category link and a plugin detail page, in light and dark themes" + expected: "Plugins from the registry with install command, README and versions; search and category filter work; layout matches the summercms.io look" + why_human: "Visual check against the live deployed site; the code is covered by pnpm test:marketplace and the Node SSR smoke against the real binary" + - test: "Production ingest on rome: registry-add the fourteen launch URLs, run summercms:registry-ingest, curl /api/registry/v1/plugins (CORS, 401 on POST ingest, 403 on POST plugins)" + expected: "Each repository ingested from its new plugin.yaml tag; API answers as in the release log step 7" + why_human: "Needs pushed tags, git on the server and the deployed binary" + - test: "Admin Settings -> System -> Plugins in an application built against the live registry, light and dark themes: Installed tab (versions, sources, update badge with copy) and Marketplace tab (search, category, install command with copy, no install button)" + expected: "Screen renders, copy puts the exact summer command on the clipboard, registry-down notice when the registry is unreachable" + why_human: "Visual and clipboard behaviour in a real browser; vitest covers the component" + - test: "Run `summer plugin:install golem15.faq` on a scratch application on a real terminal, then `--source`, then a private plugin by ssh/https git URL, then `summer plugin:update`" + expected: "Plan printed as id@version (mode), interactive confirmation prompt, module/submodule installed and registered in summer.yaml/go.mod/go.work; git URL never contacts the registry for its own repo" + why_human: "Real terminal prompt, user git credentials and the live registry; tests use httptest and file:// fixtures" + - test: "Review the version/compatibility semantics of the code-review fixes CR-01 (kiosk.Newer/Comparable, pseudo-version installs never downgraded), CR-02 (plugins sharing an installed module pinned at its version) and WR-10 (marketplace compatible marker matches plugin:install for dev/unknown framework versions)" + expected: "Rules match the intended policy; regression tests TestUpdatePseudoVersion, TestInstallSharedModule and cabana 'marketplace compatibility' pass (they do)" + why_human: "Flagged by 34-REVIEW-FIX.md as logic changes needing a human decision on semantics" +--- + +# Phase 34: Plugin marketplace Verification Report + +**Phase Goal:** People browse the public Golem15 plugins at summercms.io/marketplace and add one to their application with a single command, `summer plugin:install `. The same command installs private plugins straight from a git URL without contacting summercms.io. The admin SPA gains a read-only Plugins screen listing installed plugins and searching the marketplace, which shows the install command with a copy button. v1 has no paid plugins, no developer registration and no installs from the admin panel. +**Verified:** 2026-10-29T23:40:00Z +**Status:** gaps_found +**Re-verification:** No — initial verification + +## Goal Achievement + +### Roadmap Success Criteria + +| # | Truth | Status | Evidence | +|---|-------|--------|----------| +| SC-1 | Every public sm-*-plugin repo carries a validated plugin.yaml and fetches anonymously | ✗ PARTIAL (gap) | 20/20 sibling repos: plugin.yaml + manifest_test.go (`kiosk.CheckT(t, ".")`) tracked, framework v0.6.0, `summer plugin:manifest --check` passes (gate `--plugins` PASS), anonymous `git ls-remote` OK for all 20. Forge API lists 4 more public Go plugin repos (feedback, golem, presentations, quizzes) without plugin.yaml; scoped out by "user decision 5" in 34-02-PLAN with no override | +| SC-2 | summercms.io ingests manifest/README/versions on tag, serves registry API, renders /marketplace list/search/category/detail; presentation fields edited in admin | ✓ VERIFIED (code) | `registry/ingest.go` (bare partial clone, `kiosk.Parse`/`kiosk.Validate`, `breakwater.Render` README), `routes_registry.go` (GET plugins, {id}, versions with `cors:` profile; POST ingest separate group), `controllers/api/registry.go` (kiosk envelopes, Cache-Control, ETag), `schedule.go` hourly `--queue`, `models/plugin/fields.yaml` (category, featured, sort_order, icon, screenshots editable; facts preview-only), vue `pages/marketplace/index.vue` + `[vendor]/[name].vue`. Docker suites TestRegistrySmoke/APIShapes/IngestEndpoint pass. Live deploy is a human item | +| SC-3 | `plugin:install golem15.` (module; `--source` submodule) and `plugin:install ` (submodule) register like plugin:add and check requirements | ✓ VERIFIED | `cmd/summer/main.go` wires `build.PlanInstall`/`ApplyInstall`/`Update`; `install.go` uses `EffectiveFramework` + `kiosk.NewestCompatible`, `--yes` rule; `source.go` submodule + git URL + go.work edits; TestInstallSmoke (Docker stage) passes incl. module, source, giturl, giturl_offline, nogit, urls, update, rollback | +| SC-4 | Admin Plugins screen: installed plugins with versions and updates; Marketplace tab searches registry, install command with copy | ✓ VERIFIED | `cabana/http.go` GET `/plugins`, `/plugins/marketplace` only (no install endpoint); `plugins.go` uses `party.Installed`, `kiosk.RegistryURL/NewClient/NewestCompatible/Newer`; `PluginsView.vue` Installed + Marketplace tabs, copy buttons, tab hidden when `registry.enabled` false; route `/system/plugins`; dist rebuilt (`check-admin-dist.sh` PASS) | +| SC-5 | New code has unit tests in the last plan | ✓ VERIFIED | kiosk, build, cabana, party, cmd/summer, admin, site plugin, changelog-post and vue tests exist and pass; gate stages all PASS | + +### Plan must-haves (summary) + +| Plan | Truths | Status | Notes | +|------|--------|--------|-------| +| 34-01 kiosk, plugin:manifest, PluginInfo | 10 | 10 ✓ | `scaffold.go:368` delegates to `kiosk.ReadIdentity`; generated main calls `party.Describe` before `party.Activate` (`build.go:139`); `plugin:manifest` calls `kiosk.Check/Generate/Write`; README table lists kiosk; docs checker passes | +| 34-02 plugin.yaml rollout | 7 | 7 ✓ (as scoped) | 21 manifests in 20 repos (pgstripe/subscriptions nested), chatbots requires chat v0.1.3 (no v0.0.0), all unpushed (ahead >= 1), no tags at HEAD. Scope vs roadmap: see SC-1 gap | +| 34-03 registry and /marketplace | 9 | 9 ✓ | WR-05/07/08/09 fixes present; changelog-post registry token only (WR-04, docs follow-up 997585b) | +| 34-04 plugin:install / plugin:update | 9 | 9 ✓ | `registryFor` reads `plugins.registry_url`, empty disables; fetchguard client with cap/timeout; CR-01, CR-02, WR-02, WR-03, WR-11 fixes and tests present | +| 34-05 admin Plugins screen | 6 | 6 ✓ | `PermissionViewPlugins = "backend.view_plugins"`; settings entry gated; openapi `--check` PASS; WR-06 bounded cache (256), WR-10 compatibility | +| 34-06 tests, gate, ledger | 6 | 6 ✓ | `scripts/check-phase34.sh --self-test` detectors PASS; 34-release-log.md complete with Executed steps empty; 34-VALIDATION per-task map filled, Wave 0 ticked | + +**Score:** 51/52 truths verified (5 roadmap SCs + 47 plan truths; 0 present-but-behavior-unverified) + +### Required Artifacts + +| Artifact | Status | Details | +|----------|--------|---------| +| `modules/kiosk/{manifest,identity,check,registry,version,client}.go` | ✓ VERIFIED | 23-621 lines each, all contain the required symbols | +| `modules/party/info.go` | ✓ VERIFIED | `Describe`, `Installed`; used by generated main and cabana | +| `internal/build/{plugindirs,install,source,update,rollback}.go` | ✓ VERIFIED | `EffectiveFramework`, `PlanInstall`, `go work edit`, `Update`, snapshot/undo | +| `modules/cabana/plugins.go` | ✓ VERIFIED | 599 lines, wired in `http.go:345-346` | +| `admin/src/views/PluginsView.vue` + `admin/tests/views/PluginsView.test.ts` | ✓ VERIFIED | Routed in `router.ts:76`, built into `modules/boardwalk/dist` | +| `../sm-*-plugin/plugin.yaml`, `manifest_test.go` (20 repos) | ✓ VERIFIED | Tracked, clean, up to date | +| site plugin `registry/ingest.go`, `controllers/api/registry.go`, `updates/202610300001_*`, `registry_smoke_test.go` | ✓ VERIFIED | Docker tests pass | +| vue `changelog/app/pages/marketplace/index.vue`, `composables/useRegistry.ts` | ✓ VERIFIED | Types mirror kiosk JSON tags | +| `../sm-summercmsio-app/scripts/changelog-post.py` | ✓ VERIFIED | manifest check and ingest-now; 51 unittest cases in gate `--site` | +| `scripts/check-phase34.sh` | ✓ VERIFIED | Fail-closed stages, self-test | + +### Key Link Verification + +| From | To | Via | Status | +|------|----|-----|--------| +| kiosk/identity.go | build/scaffold.go | `kiosk.ReadIdentity` | WIRED | +| build/build.go | party/info.go | `party.Describe(app, PluginInfo, FrameworkVersion)` before `party.Activate` | WIRED | +| cmd/summer/main.go | kiosk/check.go | `kiosk.Check/Generate/Write` | WIRED | +| cmd/summer/main.go | build/install.go | `build.PlanInstall` then `build.ApplyInstall` | WIRED | +| build/install.go | kiosk/client.go | `kiosk.NewestCompatible` vs `EffectiveFramework` | WIRED | +| cabana/plugins.go | party/info.go | `app.Lookup[party.Installed]()` | WIRED | +| cabana/plugins.go | kiosk/client.go | `kiosk.RegistryURL`, `NewClient`, `NewestCompatible`, `BaseVersion` | WIRED | +| PluginsView.vue | cabana/plugins.go | `api.GET('/plugins')`, `api.GET('/plugins/marketplace')` | WIRED | +| site ingest.go | kiosk/manifest.go | `kiosk.Parse` + `kiosk.Validate` | WIRED | +| site controllers/api/registry.go | kiosk/registry.go | `kiosk.ListResponse/DetailResponse/VersionsResponse/ErrorResponse` | WIRED | +| sm-*-plugin/manifest_test.go | kiosk/check.go | `kiosk.CheckT(t, ".")` | WIRED (20/20) | +| check-phase34.sh | phase tests | required names in go test -json | WIRED | + +### Data-Flow Trace (Level 4) + +| Artifact | Data | Source | Real data | Status | +|----------|------|--------|-----------|--------| +| PluginsView Installed tab | `overview.plugins` | cabana `/plugins` <- `party.Installed` <- generated `PluginInfo` from plugin.yaml + git describe at build | Yes | ✓ FLOWING | +| PluginsView Marketplace tab | `market.data` | cabana `/plugins/marketplace` <- kiosk.Client <- registry API | Yes | ✓ FLOWING | +| /marketplace pages | `registry.list/detail/versions` | useRegistry -> /api/registry/v1 -> registry/query.go (DB) <- ingester | Yes | ✓ FLOWING | + +### Behavioral Spot-Checks + +| Behavior | Command | Result | Status | +|----------|---------|--------|--------| +| go vet + short suite | `go vet ./... && go test -short ./...` | exit 0 | ✓ PASS | +| Manifest check on a plugin and a nested plugin | `summer plugin:manifest --check ../sm-faq-plugin`, `../sm-pgstripe-plugin/subscriptions` | "is up to date", rc=0 | ✓ PASS | +| Commands registered | `summer list` | plugin:add/install/manifest/update listed | ✓ PASS | +| Review-fix regression tests | `go test -short ./internal/build -run 'TestUpdatePseudoVersion|TestInstallSharedModule|TestLegacyFramework|TestResolveInstall'`; cabana `TestPluginsEndpoints|TestPluginsScreenSmoke`; kiosk all | ok | ✓ PASS | +| Anonymous fetch | `git ls-remote https://git.golem15.com/golem15/.git` x20 | refs returned for all | ✓ PASS | +| Nothing tagged | `git tag --points-at HEAD` in 20 repos; `tag -l 'v0.6*'` | empty | ✓ PASS | + +### Probe Execution + +| Probe | Command | Result | Status | +|-------|---------|--------|--------| +| `scripts/check-phase34.sh --all` | `bash scripts/check-phase34.sh --all` | self-test, go, plugins (sm-user-plugin TestRegisterCORSPath skip printed), site, admin, docs, marker, additive PASS; docker FAIL on `modules/wristband` TestOIDCSubjectIsRandomSmoke (`sub = "30badada-..."`; the test rejects a random UUID containing "ada") | see next row | +| `scripts/check-phase34.sh --docker` (rerun) | `bash scripts/check-phase34.sh --docker` | `phase34 docker passed` (framework `go test ./...`, TestInstallSmoke, site TestRegistrySmoke/APIShapes/IngestEndpoint); the wristband test passes alone | PASS | + +The wristband failure is a pre-existing flake outside the phase (no wristband change since v0.5.3): `oidc_security_test.go:150` fails whenever the random v4 UUID happens to contain "ada" (about 0.7% of runs). + +### Requirements Coverage + +REQUIREMENTS.md has no IDs for Phase 34 ("Requirements: TBD"). Phase-local IDs from plan frontmatter: + +| ID | Plans | Status | Evidence | +|----|-------|--------|----------| +| SC-1 | 01, 02, 06 | ✗ PARTIAL | see gap | +| SC-2 | 03, 06 | ✓ SATISFIED | registry, API, admin, pages, tests | +| SC-3 | 04, 06 | ✓ SATISFIED | install/update + TestInstallSmoke | +| SC-4 | 01, 05, 06 | ✓ SATISFIED | cabana endpoints + PluginsView | +| SC-5 | 06 | ✓ SATISFIED | unit tests, gate | +| D-01..D-05 | 01, 02, 06 | ✓ SATISFIED | generated manifest, const reader, overrides block, `--check`/CheckT, Go-style minimums | +| D-06, D-07 | 01, 05 | ✓ SATISFIED | PluginInfo baked, git describe / build-info versions | +| D-08..D-12 | 04, 06 | ✓ SATISFIED | newest compatible, transitive deps + `--yes`, plugin:update, git URL submodule-only, `--offline` | +| D-13..D-16 | 03, 06 | ✓ SATISFIED | pull ingester + ingest-now, curated listing, version rows, public API v1 | +| D-17, D-18 | 04, 05 | ✓ SATISFIED | SPA talks only to cabana; `plugins.registry_url` (empty disables) | +| D-19 | 05 | ✓ SATISFIED | `backend.view_plugins`, superusers pass | + +No orphaned IDs. + +### Anti-Patterns Found + +| File | Line | Pattern | Severity | Impact | +|------|------|---------|----------|--------| +| (phase files, 3 repos) | - | TBD/FIXME/XXX | - | none found | +| internal/build/build_test.go | 1137 | "TODO: run summer make:model" | ℹ️ Info | expected scaffold output in a test, not a debt marker | +| modules/wristband/oidc_security_test.go | 150 | random UUID substring check | ℹ️ Info | flaky (outside phase), failed one `--all` docker run | + +### Human Verification Required + +1. **Live /marketplace:** after the deploy, list, search, category and detail pages in light and dark themes. +2. **Production ingest:** registry-add the fourteen launch URLs, ingest, curl the API (release log step 7). +3. **Admin Plugins screen:** Installed and Marketplace tabs against the live registry, light and dark themes, copy to clipboard. +4. **Real-terminal installs:** `summer plugin:install golem15.faq`, `--source`, a private git URL over ssh/https, and `summer plugin:update` with the interactive prompt. +5. **Fix semantics:** CR-01, CR-02 and WR-10 version and compatibility rules (34-REVIEW-FIX.md flags them); their regression tests pass. + +### Gaps Summary + +One gap, and it is about scope rather than code. The machinery for SC-1 is complete and verified: the framework generates and validates plugin.yaml, and 20 public plugin repositories carry a generated, checked manifest and can be fetched anonymously. But git.golem15.com has four more public SummerCMS plugin repositories with no plugin.yaml: sm-feedback-plugin, sm-golem-plugin, sm-presentations-plugin and sm-quizzes-plugin. 34-02-PLAN left them out under "user decision 5" because they have no sibling checkout. That narrows the roadmap's "every public sm-*-plugin repo", and nothing records the narrowing as an override. No later phase covers them. + +**This looks intentional.** To accept the deviation, add to this file's frontmatter: + +```yaml +overrides: + - must_have: "Every public sm-*-plugin repo carries a plugin.yaml manifest (ID, module path, name, description, required framework version, required plugins) that the framework validates, and anyone can fetch the repo anonymously." + reason: "User decision 5 (2026-10-29): the launch set is the public plugins checked out next to the framework; sm-feedback, sm-golem, sm-presentations and sm-quizzes get plugin.yaml later" + accepted_by: "Jakub" + accepted_at: "" +``` + +The other route is to repeat the 34-02 rollout for the four repositories, which is a small, mechanical change. + +Everything else is in place. No code change of the phase was found missing, stubbed or unwired. All 13 code-review fixes are committed with regression tests. The release (v0.6.0 tag, plugin tags, pointer bumps, deploy) is user-gated in 34-release-log.md, and nothing is pushed or tagged. + +--- + +_Verified: 2026-10-29T23:40:00Z_ +_Verifier: Claude (msd-verifier)_