From f1f1d55fa73a8bde4de520d8e7410881015d0d73 Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Thu, 29 Oct 2026 17:13:43 +0100 Subject: [PATCH] docs(34-01): complete plugin.yaml, registry contract and build metadata plan --- .../34-plugin-marketplace/34-01-SUMMARY.md | 295 ++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100644 .planning/phases/34-plugin-marketplace/34-01-SUMMARY.md diff --git a/.planning/phases/34-plugin-marketplace/34-01-SUMMARY.md b/.planning/phases/34-plugin-marketplace/34-01-SUMMARY.md new file mode 100644 index 00000000..4b856ed8 --- /dev/null +++ b/.planning/phases/34-plugin-marketplace/34-01-SUMMARY.md @@ -0,0 +1,295 @@ +--- +phase: 34-plugin-marketplace +plan: 01 +subsystem: plugins +tags: [kiosk, plugin.yaml, registry-api, go-list, git-describe, codegen, party] + +requires: + - phase: 34-plugin-marketplace (planning) + provides: CONTEXT D-01..D-19, RESEARCH F1..F10, Task 1 decision P34-01..P34-04 +provides: + - "modules/kiosk: plugin.yaml types (Manifest, Requires, Overrides), Marshal, Parse, Validate, Derive, Generate, Write, Check, StaleError, CheckT/TB" + - "kiosk.ReadIdentity: static, const-following ID()/Requires() reader shared with plugin:add" + - "kiosk registry v1 wire types (RegistryAPIPath, RegistryPlugin, RegistryPluginDetail, RegistryVersion, envelopes, errors, ingest) and version rules (ValidVersion, CompareVersions, BaseVersion, Satisfies, NewestCompatible)" + - "summer plugin:manifest [dir] --check --pretend --name --description" + - "party.Info, party.Installed, party.Describe; generated PluginInfo and FrameworkVersion in plugins.gen.go" + - "build.EffectiveFramework" +affects: [34-02 plugin repo rollout, 34-03 registry server, 34-04 install CLI, 34-05 admin Plugins screen, 34-06 tests, every app's committed main.go/plugins.gen.go] + +actuals: + tokens: 36000 + tasks: 5 + commits: 4 +plan_head_before: 824e3d5882c2e0b96a7d402338b9238d1c3f6ae9 +plan_head_after: e8d3d724f51d19683b25221c0e666d7fedafa78e + +tech-stack: + added: [] + patterns: + - "Static AST resolution of string constants across packages and modules (go/parser + go list -m -json), never running plugin code" + - "Generated file plus --check / CheckT drift guard" + - "Build metadata resolution that degrades to ID-only with a warning and never fails summer build" + +key-files: + created: + - modules/kiosk/kiosk.go + - modules/kiosk/manifest.go + - modules/kiosk/derive.go + - modules/kiosk/identity.go + - modules/kiosk/check.go + - modules/kiosk/registry.go + - modules/kiosk/version.go + - modules/kiosk/README.md + - modules/kiosk/manifest_smoke_test.go + - modules/kiosk/checkt_test.go + - modules/kiosk/testdata/ (literal, consts, consts/nested, other, framework fixtures) + - modules/party/info.go + - internal/build/plugindirs.go + - internal/build/plugininfo_smoke_test.go + - docs/plugins/marketplace.md + modified: + - cmd/summer/main.go + - internal/build/build.go + - internal/build/new.go + - internal/build/scaffold.go + - internal/build/build_test.go + - internal/build/testdata/new/main.go.golden + - internal/build/testdata/new/plugins.gen.go.golden + - internal/build/testdata/new-no-plugin/plugins.gen.go.golden + - examples/hello/main.go + - examples/hello/plugins.gen.go + - modules/party/README.md + - README.md + - docs/console/scaffolding.md + - docs/architecture/go-modules-and-workspaces.md + - docs/architecture/introduction.md + +key-decisions: + - "Task 1 answered as-planned: P34-01 plugin.yaml format, P34-02 registry API v1 shape, P34-03 names (module kiosk, party.Info/Installed/Describe, PluginInfo/FrameworkVersion, plugins.registry_url, backend.view_plugins, /system/plugins, token scope registry) and P34-04 v0.6.0 as framework release and plugin framework minimum adopted unchanged" + - "kiosk runs go commands with GOWORK=off: a manifest describes the plugin module on its own; a required module missing from the module cache is fetched with go mod download" + - "ReadIdentity returns the ID even when Requires() cannot be read, so plugin:add keeps working for any plugin whose ID it could read before" + - "Requires marshals to JSON as {} when empty, so the registry never sends null for requires" + - "plugin versions: module cache -> module version; own git repo -> git describe --tags --match v[0-9]* --always (no --dirty); in-app -> empty" + +patterns-established: + - "Plugin metadata is derived, never typed: plugin.yaml from sources, PluginInfo from plugin.yaml + go list + git" + - "Registry JSON shape exists once, as kiosk wire types" + +requirements-completed: [SC-1, SC-4, D-01, D-02, D-03, D-04, D-05, D-06, D-07, D-16] + +coverage: + - id: D1 + description: "summer plugin:manifest generates, previews and checks plugin.yaml from plugin.go, go.mod and README (literal IDs)" + requirement: D-01 + verification: + - kind: unit + ref: "modules/kiosk/manifest_smoke_test.go#TestManifestSmoke/literal" + status: pass + - kind: other + ref: "go run ./cmd/summer plugin:manifest /acme && go run ./cmd/summer plugin:manifest --check /acme" + status: pass + human_judgment: false + - id: D2 + description: "Constant, cross-package, cross-module and nested plugin identities resolve statically; non-const expressions are errors naming ID()/Requires(); plugin:add registers const-ID plugins" + requirement: D-02 + verification: + - kind: unit + ref: "modules/kiosk/manifest_smoke_test.go#TestManifestSmoke/consts,nested,errors" + status: pass + - kind: unit + ref: "internal/build/build_test.go#TestAddPluginConstID" + status: pass + - kind: other + ref: "summer plugin:manifest --pretend on ../sm-mfa-plugin, ../sm-paymentgateway-plugin, ../sm-pgstripe-plugin/subscriptions" + status: pass + human_judgment: false + - id: D3 + description: "--name/--description overrides persist, clear with an empty value, and H1 == ID demands --name" + requirement: D-03 + verification: + - kind: unit + ref: "modules/kiosk/manifest_smoke_test.go#TestManifestSmoke/overrides" + status: pass + human_judgment: false + - id: D4 + description: "--check and kiosk.CheckT fail on a missing or stale plugin.yaml; Validate enforces the format" + requirement: D-04 + verification: + - kind: unit + ref: "modules/kiosk/manifest_smoke_test.go#TestManifestSmoke/checkt,validate" + status: pass + - kind: unit + ref: "modules/kiosk/checkt_test.go#TestPluginManifest" + status: pass + human_judgment: false + - id: D5 + description: "Go-style minimums: v0.0.0 plugin requirement is an error, pseudo-version warns, soft dependency has the empty minimum" + requirement: D-05 + verification: + - kind: unit + ref: "modules/kiosk/manifest_smoke_test.go#TestManifestSmoke/errors" + status: pass + - kind: other + ref: "summer plugin:manifest --pretend ../sm-chatbots-plugin exits 1 with 'not a released version'" + status: pass + human_judgment: false + - id: D6 + description: "Registry v1 wire types encode the confirmed JSON shape, ignore unknown fields; strict version rules" + requirement: D-16 + verification: + - kind: unit + ref: "modules/kiosk/manifest_smoke_test.go#TestManifestSmoke/registry,versions" + status: pass + human_judgment: false + - id: D7 + description: "summer build bakes PluginInfo and FrameworkVersion, generated main calls party.Describe before Activate, failures fall back to IDs" + requirement: D-06 + verification: + - kind: unit + ref: "internal/build/plugininfo_smoke_test.go#TestPluginInfoSmoke/generate,main,fallback,describe" + status: pass + - kind: integration + ref: "cd examples/hello && go test ./..." + status: pass + human_judgment: false + - id: D8 + description: "Versions per D-07 and the effective framework version (local checkout tag, go.mod require, git failure warning)" + requirement: D-07 + verification: + - kind: unit + ref: "internal/build/plugininfo_smoke_test.go#TestPluginInfoSmoke/dirs,framework" + status: pass + human_judgment: false + - id: D9 + description: "Docs: kiosk README, party README, docs/plugins/marketplace.md, scaffolding and go-modules pages; docs checker passes" + verification: + - kind: other + ref: "go run ./cmd/summer docs:build --check; go test ./cmd/summer -run 'TestCommandsDocumented|TestDocsTree'" + status: pass + human_judgment: true + rationale: "Docs prose quality and wording are a reviewer's call; the checker only proves identifiers, links and snippets" + +duration: 22min +completed: 2026-10-29 +status: complete +--- + +# Phase 34 Plan 01: plugin.yaml, registry contract and build metadata Summary + +**New framework module `kiosk` generates and checks each plugin's plugin.yaml from its own sources (resolving constant IDs across packages and modules without running code), holds the registry API v1 wire types and version rules, and `summer build` now bakes PluginInfo and FrameworkVersion into plugins.gen.go, which the generated main publishes with `party.Describe`.** + +## Performance + +- **Duration:** about 22 min +- **Started:** 2026-10-29T15:50:20Z +- **Completed:** 2026-10-29T16:12:19Z +- **Tasks:** 5 (Task 1 was a decision answered before dispatch; Tasks 2-5 executed) +- **Files modified:** 44 (29 created, 15 modified) + +## Task 1 decision (recorded verbatim) + +The user answered Task 1 through the orchestrator before dispatch: **"as-planned"**. P34-01 (plugin.yaml format), P34-02 (registry API v1 shape), P34-03 (names: module `kiosk`, `party.Info`/`Installed`/`Describe`, `PluginInfo`/`FrameworkVersion`, `plugins.registry_url`, `backend.view_plugins`, `/system/plugins`, token scope `registry`) and P34-04 (v0.6.0 as the framework release and the plugin framework minimum) are adopted exactly as written. The user re-confirmed v0.6.0 knowing that it raises every plugin's framework minimum. + +No name differs from the plan's text. The plan adds these extra exported names, which 34-03..34-05 can use: +- `kiosk.IngestResult`: the `data` object of `kiosk.IngestResponse` (`repository`, `queued`). The plan described the shape but did not name the type. +- `kiosk.RegistryErrorNotFound` (`not_found`) and `kiosk.RegistryErrorInvalidQuery` (`invalid_query`): the error codes as constants. +- `kiosk.Requires.MarshalJSON` and `kiosk.Requires.MarshalYAML`. +- `kiosk.StaleError.Error`. + +## Accomplishments + +- `summer plugin:manifest [dir]` writes, previews (`--pretend`) and checks (`--check`, exit 1 and prints the expected file) plugin.yaml with the P34-01 field order and header. `--name`/`--description` persist in `overrides:`, and an empty value clears them. +- `kiosk.ReadIdentity` resolves literals, same-package constants (in any non-test file), constants of another package in the same module and constants of another module (`go list -m -json`, `go mod download -json` when the module is not in the cache). It handles named imports and unnamed imports whose package name differs from the path. `plugin:add` now uses it, so const-ID plugins register (research F1). +- Required-plugin minimums come from go.mod requires. Plugins in the same module and soft dependencies get the empty minimum, `v0.0.0` is an error and a pseudo-version gives a warning. +- `kiosk.CheckT(t, dir)` gives plugin repos a one-line `go test` guard. kiosk does not import `testing`. +- Registry v1 contract: `RegistryAPIPath` and every response/request type, checked by golden JSON in the tests, plus strict `vX.Y.Z` rules and `NewestCompatible`. +- `summer build` runs one `go list -e -json` over the summer.yaml packages and reads each plugin.yaml. Versions follow D-07: module version, `git describe` without `--dirty` for a plugin in its own repository, empty for an in-app plugin. It also computes `EffectiveFramework`, bakes all of this into plugins.gen.go, and warns and falls back to ID-only entries on any failure. +- Every sibling plugin derives a manifest with `--pretend` except the three chat plugins (chatbots, chatchannels, chatvideo), which require `sm-chat-plugin v0.0.0` (research F8, expected; 34-02 fixes those go.mod files). No file in any sibling repository was written. + +## Task Commits + +1. **Task 1: Confirm format, API shape, names, release number**: no commit (answer "as-planned" recorded above) +2. **Task 2: plugin.yaml manifest and summer plugin:manifest (tracer)**: `ec6a195` (feat) +3. **Task 3: constant plugin IDs, overrides and the CheckT helper**: `51ec2f9` (feat) +4. **Task 4: registry v1 wire types and version rules**: `4916ee3` (feat) +5. **Task 5: bake plugin metadata and the framework version into plugins.gen.go**: `e8d3d72` (feat) + +**Plan metadata:** the docs commit containing this SUMMARY. + +## Files Created/Modified + +- `modules/kiosk/kiosk.go`: FileName, SchemaVersion, FrameworkModule, Header +- `modules/kiosk/manifest.go`: Manifest, Requires, Overrides, Marshal, Parse, Validate +- `modules/kiosk/derive.go`: DeriveOptions, Derive (go mod edit -json, README via goldmark) +- `modules/kiosk/identity.go`: Identity, ReadIdentity, module listing/download, required-ID minimums +- `modules/kiosk/check.go`: Generate, Write, Check, StaleError, TB, CheckT +- `modules/kiosk/registry.go`, `modules/kiosk/version.go`: registry v1 contract and version rules +- `modules/kiosk/manifest_smoke_test.go`, `modules/kiosk/checkt_test.go`, `modules/kiosk/testdata/`: TestManifestSmoke (literal, consts, nested, errors, overrides, checkt, validate, registry, versions) and fixtures +- `modules/party/info.go`: Info, Source constants, Installed, Describe +- `internal/build/plugindirs.go`: go list resolver, plugin.yaml reading, git describe, EffectiveFramework +- `internal/build/build.go`, `internal/build/new.go`: generate takes buildMeta; PluginInfo/FrameworkVersion; Describe in main +- `internal/build/scaffold.go`: pluginIDFromGo delegates to kiosk.ReadIdentity +- `cmd/summer/main.go`: plugin:manifest command; `summer build` shows `warning:` lines with out.Warning +- `examples/hello/main.go`, `examples/hello/plugins.gen.go`, three `summer new` goldens: regenerated +- Docs: `modules/kiosk/README.md`, `modules/party/README.md`, root `README.md` row, `docs/plugins/marketplace.md` (new), `docs/console/scaffolding.md`, `docs/architecture/go-modules-and-workspaces.md`, `docs/architecture/introduction.md` + +## Decisions Made + +- kiosk's go commands run with `GOWORK=off`. A manifest describes the plugin module as consumers see it, and a workspace above a plugin directory could otherwise make `go list -m` fail. +- When deriving minimums, only required modules whose go.mod mentions the framework are searched for plugin.go files, and only those are downloaded. This keeps third-party modules out of the walk. +- `ReadIdentity` prefers the `ID()` in plugin.go and reads `Requires()` from the same receiver type. That avoids picking up an unrelated `ID()` method, for example on a model, in the same package. +- PluginInfo leaves empty fields out of each composite literal and uses `party.Source*` constants. The summer new goldens bake IDs only with an empty FrameworkVersion, so they stay deterministic. +- A plugin.yaml whose `id` differs from the summer.yaml ID is ignored with a warning (defensive, T-34-03). + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 2 - Missing critical] Corrupt existing plugin.yaml gives an actionable error** +- **Found during:** Task 2 +- **Issue:** A hand-damaged plugin.yaml made regeneration fail with a bare YAML parse error. +- **Fix:** The error now says to delete the file and rerun, passing `--name`/`--description` again. +- **Files modified:** modules/kiosk/derive.go +- **Commit:** ec6a195 + +**2. [Rule 3 - Blocking] Docs checker requires go code blocks in docs/ to come from compiled tests** +- **Found during:** Task 3 +- **Issue:** The "Testing the manifest in your plugin" snippet failed `docs:build --check` ("go code block has no src= reference"). +- **Fix:** Added `modules/kiosk/checkt_test.go` (`TestPluginManifest`, calls `kiosk.CheckT` on the literal fixture) and committed the fixture's generated `testdata/literal/plugin.yaml`. The page references it with `src=`, and the prose explains that a plugin passes `"."`. +- **Files modified:** modules/kiosk/checkt_test.go, modules/kiosk/testdata/literal/plugin.yaml, docs/plugins/marketplace.md +- **Commit:** 51ec2f9 + +**3. [Rule 3 - Blocking] `requireGit` already declared in internal/build tests** +- **Found during:** Task 5 +- **Fix:** Reused the existing `requireGit` and named the identity-isolated helper `runTestGit`. +- **Files modified:** internal/build/plugininfo_smoke_test.go +- **Commit:** e8d3d72 + +**4. [Rule 2 - Missing critical] Additions outside the plan's file list** +- `modules/kiosk/testdata/consts/vendor.go`: proves that a constant in another non-test file of the same package resolves. +- `docs/architecture/introduction.md`: kiosk added to the module overview table, which lists every module. +- `cmd/summer/main.go` in Task 5: `summer build` prints metadata `warning:` lines with out.Warning. +- `internal/build/build_test.go` in Task 5: the existing `generatePluginsGen` call sites were updated to the new signature. +- The `plugin:manifest` examples were reworded so that `grep '"plugin:manifest"'` prints exactly one line, as the acceptance criteria require. + +**5. [Process] TDD order in Task 3** +- Task 3's behavior subtests were written right after the implementation, in the same task, not before it. Tasks 4 and 5 followed RED-then-GREEN: their tests failed to compile on the missing types before implementation. Each task is one commit, as the plan's Execution units table requires. + +**Total deviations:** 5 (2 Rule 2, 2 Rule 3, 1 process). **Impact:** none on the contract; all names match the plan. + +## Issues Encountered + +- `gofmt -l cmd/summer` lists pre-existing alignment differences in `cmd/summer/main.go`, `runtime.go` and `runtime_test.go` that this plan did not introduce. Left untouched (scope boundary). + +## Next Phase Readiness / notes for later plans + +- 34-02: `sm-chatbots-plugin`, `sm-chatchannels-plugin` and `sm-chatvideo-plugin` require `sm-chat-plugin v0.0.0`. They must require a tag before `plugin:manifest` succeeds. Thirteen repos whose README H1 is the ID need `--name` once. +- Every app regenerates main.go and plugins.gen.go on its first build after upgrading (research F10). `FrameworkVersion` in a committed plugins.gen.go changes when the locally replaced framework checkout gains a tag (examples/hello now bakes `v0.5.3`). Commit the regeneration together with the bump. +- `plugin:manifest` and `CheckT` may run `go mod download` for a required plugin module that is not in the module cache. They need the network once per version. + +## Self-Check: PASSED + +- All key files exist (kiosk sources, README, tests, party/info.go, plugindirs.go, plugininfo_smoke_test.go, docs/plugins/marketplace.md). +- Commits ec6a195, 51ec2f9, 4916ee3 and e8d3d72 are in history; `git rev-list --count 824e3d5..HEAD` = 4 before this docs commit. +- `go vet ./...`, `go test -short ./...`, `cd examples/hello && go test ./...`, `go test ./cmd/summer -run 'TestCommandsDocumented|TestDocsTree'` and `summer docs:build --check` all pass. +- The acceptance criteria of Tasks 2-5 were re-run and pass. The pgstripe/subscriptions, mfa and paymentgateway pretend runs print the expected id/package lines; chatbots exits 1 with "not a released version".