- new note .planning/notes/core-plugins-own-repos.md: shared core plugins live in sm-<name>-plugin repos mounted as submodules - 01-CONTEXT deferral points to the note; PROJECT constraint and Key Decisions row - ROADMAP Phase 12 repos and the 12-01 entry, and Phase 12 plans 12-01, 12-02, 12-05 name sm-user-plugin and the submodule commit workflow
3.6 KiB
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-<name>-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-<name>-plugin. The Go package and the plugin ID keep their plain names, so the user plugin is still packageuserand plugingolem15.user. - Each application mounts them as git submodules at
plugins/<vendor>/<name>, lists them ingo.workand adds arequireplus a localreplace git.golem15.com/golem15/sm-<name>-plugin => ./plugins/<vendor>/<name>to itsgo.mod, and names the module insummer.yaml. - The plugin's own
go.modreplaces the framework with../../../../summercms.go, which resolves when the plugin is mounted at<app>/plugins/<vendor>/<name>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 asmasterofgit@git.golem15.com:golem15/sm-user-plugin.git. - fonoteka.go mounts it back as a submodule at the same path,
plugins/golem15/user, sogo.workdid not change. - The module was renamed to
git.golem15.com/golem15/sm-user-pluginin the plugin repo, and every importer in fonoteka.go (app, parity, the fonoteka plugin, bothgo.modfiles,summer.yaml, the regeneratedplugins.gen.go) followed. - No behaviour change: plugin ID, tables, migration IDs, config keys
golem15.user.*, routes and payloads are unchanged. The plugin gained ago mod tidyfor standalone module mode and a README.
Workflow
- Change a submodule plugin inside its checkout (
git -C <app>/plugins/<vendor>/<name>): commit there and push itsmasterfirst, 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.
TestRegisterCORSPathin the user plugin reads the host application'sconfig/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-appis a separate pending rename.