docs(34): phase verification report

This commit is contained in:
Jakub Zych
2026-10-29 22:57:25 +01:00
parent 7b60c17138
commit a9237c8ec3

View File

@@ -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 <id>`. 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.<name>` (module; `--source` submodule) and `plugin:install <git-url>` (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/<repo>.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: "<ISO timestamp>"
```
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)_