14 KiB
Phase 1: Framework kernel foundation — Research
Researched: 2026-09-16
Domain: Go 1.27 compiled plugin kernel, layered configuration, CLI and watch loop
Confidence: High for documented library behavior; medium for proposed API shapes until the examples/hello integration proves them.
<user_constraints>
User Constraints
The locked decisions are D-01 through D-17 in 01-CONTEXT.md. In particular: two binaries and one bonfire command kernel; canonical module path git.golem15.com/golem15/summercms; root go.work plus permanent examples/hello; bare plugin-ID config namespaces; the exact six-level config priority; required Plugin plus optional capability interfaces; deterministic lifecycle and event dispatch; hand-rolled output; fsnotify watch loop. The framework repository must not import Płytarium or grow the Phase 3 HTTP/ORM/auth stack.
The context leaves the exact event and service APIs, file manifest format, and package dependency direction to implementation. Defer model/migration/controller scaffolding, admin config editing, shared-plugin extraction, and all surf/lagoon/bouncer implementation.
</user_constraints>
<architectural_responsibility_map>
Architectural Responsibility Map
Single-tier CLI and backend framework. cmd/summer owns tool entry, generated app main.go owns runtime entry, party owns plugin ordering, backpack owns app-scoped services, compass owns configuration, festival owns events, and bonfire owns commands and terminal output. The generated app binary is the integration boundary.
</architectural_responsibility_map>
<research_summary>
Summary
Build the first executable path through examples/hello: explicit summer.yaml manifest → generated plugins.gen.go → app main → config load → all plugin Register calls → all Boot calls → a namespaced plugin command. Extend that path with configuration precedence, optional service integration, event dispatch, and summer dev; do not build unused framework layers first. The source-of-truth design is the phase context, with project research supplying examples and pitfalls.
go.work can develop multiple modules together, but a root go test ./... does not by itself prove nested example modules. Plans need separate go test/go vet checks from examples/hello and a built-binary invocation. Go's toolchain directive needs a concrete release, so toolchain go1.27.0 matches the installed go1.27.0 and fulfills D-05 without an invalid wildcard. The go/toolchain directive behavior is documented by Go, and workspace module membership by the Go workspace tutorial.
Primary recommendation: Keep every package API driven by a real hello plugin and its CLI path, then verify the kernel with reordered plugin input, config precedence, non-TTY output, generated code stability, and a measured watch rebuild. </research_summary>
<standard_stack>
Standard Stack
| Component | Phase 1 choice | Use |
|---|---|---|
| Compiler/workspace | Go 1.27.0, go.work |
Root framework plus separate hello app/plugin modules. |
| Config | github.com/knadh/koanf/v2, file/confmap/env v2 providers, koanf YAML parser |
Ordered deep merges, typed section decode, dot paths. |
| CLI | github.com/spf13/cobra |
One root command constructor shared by tool and app. |
| Terminal | golang.org/x/term plus standard library |
TTY detection, password prompt, simple widgets. |
| Watch | github.com/fsnotify/fsnotify |
Observe app workspace directories and rebuild. |
| Tests | go test, go test -race, go vet; testify only if helpful |
Unit and example integration coverage. |
The context authorizes these dependencies. Use checked module tags and commit go.sum; do not use @latest in repeatable plan commands. The koanf docs confirm successive Load calls merge into the existing tree. The current env/v2 provider has Opt{Prefix, TransformFunc, EnvironFunc} and can inject an environment source in tests. The earlier project STACK.md's unversioned env-provider example is a sketch, not a required import path.
Package Legitimacy Audit
All Phase 1 external packages below have live package pages and tagged module versions as of 2026-09-16; none is assumed. These are verified available tags, not a claim that no newer tag exists.
| Module | Checked tag | Primary source |
|---|---|---|
github.com/knadh/koanf/v2 |
v2.3.6 |
pkg.go.dev |
github.com/knadh/koanf/providers/file |
v1.2.1 |
pkg.go.dev |
github.com/knadh/koanf/providers/confmap |
v1.0.1 |
pkg.go.dev |
github.com/knadh/koanf/providers/env/v2 |
v2.0.1 |
pkg.go.dev |
github.com/knadh/koanf/parsers/yaml |
v1.1.1 |
pkg.go.dev |
github.com/spf13/cobra |
v1.10.2 |
pkg.go.dev |
github.com/fsnotify/fsnotify |
v1.10.1 |
pkg.go.dev |
golang.org/x/term |
v0.46.0 |
pkg.go.dev |
| </standard_stack> |
<architecture_patterns>
Architecture Patterns
summer.yaml → summer build → generated imports + app main → go build → hello binary
↓
config → Register all → Boot all
↓
bonfire root → plugin command → Output
summer dev → fsnotify/debounce → same build function → restart hello binary
Package boundaries
pactholds neutral plugin and capability contracts. IfPlugin.Register/Boottake an app type, define a narrow app-facing interface inpactor place the contract inpartysopact → backpack → pactcannot occur.backpackimplements the interface;partyorchestrates it.backpack.Appowns config, services and event bus per instance. Typed lookup may be a generic method on a concrete type with Go 1.27, or a generic helper; no package-global service state.festival.Bususes typed struct event identity, synchronous listeners, priority descending with stable registration order, and a snapshot of listeners at dispatch. Define collision behavior for collect payloads before implementation (recommended: later invoked listener wins for duplicate keys), then test it.partymay have aninit()registration slice as the sole allowed process-global registry. Copy it into each app, reject duplicate IDs, validateRequires()against the complete set, topologically order with deterministic tie-breaking, then run every Register before any Boot. Return missing/cycle errors with plugin IDs.compassbuilds a fresh tree on Reload. Load plugin defaults → sorted base section files → sorted env section files → env variables → persisted overrides → runtime Set. Keep runtime overrides separate so Reload clears them; protect live reads/writes with a lock or immutable snapshot swap. Decode YAML under logicalgolem15.fonoteka.*paths consistently withSUMMER_GOLEM15__FONOTEKA__*.bonfireconstructs cobra commands with injectedInputandOutput, sets writers explicitly and executes with context. Cobra supportsAddCommand,ExecuteContext,SetOut, andSetErr. One kernel factory avoids separate tool/app wiring.
Watch loop
fsnotify does not recursively watch subdirectories and recommends watching parent directories rather than individual files because editors replace files atomically. Walk the workspace initially, add new directories on Create, ignore generated binaries/.git, debounce bursts, terminate/reap the old process, rebuild through the summer build function, and start only a successful new binary. Drain both Events and Errors. See the fsnotify README.
MVP boundary
The roadmap marks Phase 1 mvp, and the generic GSD Walking Skeleton recipe would include DB/UI. That conflicts with the explicit Phase 1 boundary in CONTEXT.md, which excludes ORM, router, and UI until Phase 3. For this phase the real user is a framework developer: the vertical proof is a generated, bootable hello binary with a working plugin command and live rebuild. SKELETON.md should record this phase-specific boundary and hand the DB/HTTP/UI slice to Phase 3.
</architecture_patterns>
<common_pitfalls>
Common Pitfalls
| Risk | Detection and prevention |
|---|---|
| Import cycles from optional capability signatures | Compile the minimal hello plugin before adding more capabilities; keep future adapters out of pact imports. |
| Non-deterministic generated imports or lifecycle | Manifest is an ordered list; regenerate idempotently; use explicit tie-breaking and reordered-input test. |
Env key ambiguity and .env clobbering |
Split only on __, preserve single underscores, lowercase path segments; do not overwrite os.LookupEnv values. |
| Config merge semantics hidden by filenames | Sort files, validate duplicate sections and malformed YAML, test all six priority layers including persisted overrides and Reload. |
| Nested modules skipped by root checks | Run checks in both root and examples/hello; execute built app binary. |
| Watcher churn or stale process | Test create/rename/write bursts, rebuild failure, restart, cancellation, and measured rebuild latency. |
| Terminal tests hang in CI | Inject streams and terminal capability; test non-TTY tables, progress, spinner and prompts with pipes. x/term supplies terminal detection and password/raw-mode primitives. |
| Phase 1 grows into a generic CMS | Require each kernel API to have a hello consumer or an explicit Phase 3 handoff; defer unused adapters. |
| </common_pitfalls> |
<validation_architecture>
Validation Architecture
The phase has no existing Go tests. Execution should create small smoke tests alongside each functional slice and reserve a final, dedicated unit-test plan for complete behavior coverage, per CLAUDE.md. After every implementation task, run go vet ./... && go test ./... at the framework root and, after hello exists, go vet ./... && go test ./... from examples/hello. The last plan adds go test -race ./... for both modules and an integration script/test that runs summer build and the generated hello binary in a temporary workspace. Avoid tests that require an interactive terminal.
| Requirement | Automated evidence |
|---|---|
| KERN-01 | Table-driven deep-merge, six-layer precedence, env mapping, .env, Set/Persist/Reload, typed decode tests. |
| KERN-02/03/05/08 | Reordered lifecycle input, missing/cycle/duplicate ID, optional interface, HasPlugin and typed service lookup tests. |
| KERN-04 | Stable codegen golden test, plugin:add workspace/manifest test, hello go build and boot smoke. |
| KERN-06/07 | Three dispatch modes, ordering, errors/panics, app isolation, context propagation and race tests. |
| KERN-09 | Temp workspace create/write/rename and restart test, latency line assertion. |
| CLI-01 | Cobra command discovery and output capture, non-TTY widgets/prompts and color policy tests. |
The fast feedback command should take seconds once dependencies are cached. CI can measure actual duration rather than assume a fixed latency threshold. Test a single plugin source change with summer dev and record the measured rebuild time; the acceptance criterion asks for a measurement, not an arbitrary pass threshold.
</validation_architecture>
<open_questions>
Open Questions (RESOLVED)
- Exact optional capability method signatures for Phase 3+ — RESOLVED: Phase 1 implements the type-asserted
HasConfigandHasCommandsadapters and documents the remaining named capability families. Their payload methods are declared when the first relevant phase has concrete consumer types, per the context's interleaved kernel rule. No[]anyplaceholders are introduced just to fill an interface list. - Plugin config representation with dotted IDs — RESOLVED: Each plugin's embedded
config/config.yamlcontains keys relative to its ID;compassmerges it at the logical pathgolem15.fonotekausing koanfMergeAt. Additional embedded section files, if used later, merge atgolem15.fonoteka.<filename>. App-levelconfig/<section>.yamlretains filename-as-section behavior. This avoids relying on a dotted YAML root key while preserving the D-06/D-08 public paths. - Dev watch process ownership across platforms — RESOLVED: Use
exec.CommandContextand an explicit child stop/reap path. Test Linux in this environment and keep build/process launch behind small test hooks; no platform-specific daemon API is required for Phase 1. </open_questions>
- Project decisions: 01-CONTEXT.md, ARCHITECTURE.md, PITFALLS.md, STACK.md,
../modules/summer-compass/README.md,../modules/summer-bonfire/README.md. - Primary references: Go toolchain, Go workspaces, koanf, koanf env/v2, Cobra, fsnotify, x/term.
Phase: 01-framework-kernel-foundation
Ready for planning: yes