# Decision: Shared core plugins live in their own sm-*-plugin repos **Date:** 2026-10-02 **Status:** Accepted **Context:** quick task 261002-esz (extract golem15.user from fonoteka.go) **Supersedes:** the Phase 1 deferred item "Extraction of shared stack plugins into their own repos — only when a second app (keios.eu) needs one" (`.planning/phases/01-framework-kernel-foundation/01-CONTEXT.md`) ## Decision Shared core plugins live in their own repositories, `git.golem15.com/golem15/sm--plugin`, starting with the user plugin and followed by blog, pages, payment and the other cross-project Golem15 plugins as they are ported. - The Go module path equals the repo path: `git.golem15.com/golem15/sm--plugin`. The Go package and the plugin ID keep their plain names, so the user plugin is still package `user` and plugin `golem15.user`. - Each application mounts them as git submodules at `plugins//`, lists them in `go.work` and adds a `require` plus a local `replace git.golem15.com/golem15/sm--plugin => ./plugins//` to its `go.mod`, and names the module in `summer.yaml`. - The plugin's own `go.mod` replaces the framework with `../../../../summercms.go`, which resolves when the plugin is mounted at `/plugins//` next to the framework checkout. - Application-specific plugins, such as `golem15.fonoteka`, stay in the application repository. ## Trigger The Phase 1 deferral waited for a second application. One now exists: sm-summercmsio-app, which already mounts its site plugin sm-summercmsio-plugin as a submodule at `plugins/golem15/summercms` (the layout precedent this note generalises). The Journal port is next and needs the same user plugin. Keeping one copy per core plugin avoids forks between applications. ## How it was done for golem15.user - The plugin's history was split out of fonoteka.go with `git subtree split --prefix=plugins/golem15/user` (21 commits, tree byte-identical to the in-tree directory) and pushed as `master` of `git@git.golem15.com:golem15/sm-user-plugin.git`. - fonoteka.go mounts it back as a submodule at the same path, `plugins/golem15/user`, so `go.work` did not change. - The module was renamed to `git.golem15.com/golem15/sm-user-plugin` in the plugin repo, and every importer in fonoteka.go (app, parity, the fonoteka plugin, both `go.mod` files, `summer.yaml`, the regenerated `plugins.gen.go`) followed. - No behaviour change: plugin ID, tables, migration IDs, config keys `golem15.user.*`, routes and payloads are unchanged. The plugin gained a `go mod tidy` for standalone module mode and a README. ## Workflow - Change a submodule plugin inside its checkout (`git -C /plugins//`): commit there and push its `master` first, then commit the bumped pointer in the application repo as a separate commit. - Never stage files inside a submodule from the application repo. - Core plugin contracts (routes, payloads, tables, migration IDs, config keys) stay non-breaking unless the user asks, because several applications mount the same plugin. ## Consequences and follow-ups - A plugin README never names a consuming application; it says "the application" or "host application" and uses neutral examples. - `TestRegisterCORSPath` in the user plugin reads the host application's `config/http.yaml`, so the plugin's full test suite runs only inside an application checkout. Making it self-contained is a follow-up. - Two code comments in the user plugin (`models/organisation.go`, `updates/10_organisations.go`) still name the first consuming application; they were left unchanged in the pure move. - fonoteka.go itself becoming `sm-fonoteka-app` is a separate pending rename.