docs(01): create phase plan
This commit is contained in:
145
.planning/phases/01-framework-kernel-foundation/01-01-PLAN.md
Normal file
145
.planning/phases/01-framework-kernel-foundation/01-01-PLAN.md
Normal file
@@ -0,0 +1,145 @@
|
||||
---
|
||||
phase: 01-framework-kernel-foundation
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- go.mod
|
||||
- go.sum
|
||||
- go.work
|
||||
- party/registry.go
|
||||
- backpack/app.go
|
||||
- pact/capabilities.go
|
||||
- compass/config.go
|
||||
- bonfire/command.go
|
||||
- bonfire/root.go
|
||||
- internal/build/build.go
|
||||
- cmd/summer/main.go
|
||||
- examples/hello/summer.yaml
|
||||
- examples/hello/go.mod
|
||||
- examples/hello/main.go
|
||||
- examples/hello/plugins.gen.go
|
||||
- examples/hello/hello_test.go
|
||||
- examples/hello/plugins/base/go.mod
|
||||
- examples/hello/plugins/base/plugin.go
|
||||
- examples/hello/plugins/greeter/go.mod
|
||||
- examples/hello/plugins/greeter/plugin.go
|
||||
- examples/hello/config/app.yaml
|
||||
autonomous: true
|
||||
requirements: [KERN-01, KERN-02, KERN-03, KERN-04, CLI-01]
|
||||
must_haves:
|
||||
truths:
|
||||
- "D-01 D-02: An installed `summer` tool builds a separate hello app binary, and both entry points use the same `bonfire` root-command constructor."
|
||||
- "D-03 D-04 D-05: The permanent hello app has separate plugin modules in root `go.work`, imports `git.golem15.com/golem15/summercms`, and uses a concrete Go 1.27 toolchain directive."
|
||||
- "D-15 D-17: The hello app boots plugins and executes `greeter:hello` through a `bonfire.Command` adapter with injected Input and Output."
|
||||
- "The generated import list preserves the explicit manifest order while the plugin registry orders lifecycle by `Requires()`."
|
||||
artifacts:
|
||||
- path: internal/build/build.go
|
||||
provides: Manifest-driven code generation and app build
|
||||
- path: party/registry.go
|
||||
provides: Required plugin interface and activation
|
||||
- path: bonfire/root.go
|
||||
provides: Shared cobra command construction
|
||||
- path: examples/hello/summer.yaml
|
||||
provides: Ordered hello plugin manifest
|
||||
key_links:
|
||||
- from: cmd/summer/main.go
|
||||
to: bonfire/root.go
|
||||
via: Shared root command constructor
|
||||
- from: examples/hello/main.go
|
||||
to: party/registry.go
|
||||
via: Generated app activation with ordered plugin IDs
|
||||
- from: examples/hello/plugins.gen.go
|
||||
to: examples/hello/plugins/greeter/plugin.go
|
||||
via: Blank import triggers self-registration
|
||||
---
|
||||
|
||||
<objective>
|
||||
As a framework developer, I can build a tiny app from two compiled plugins and run its namespaced hello command, so the first kernel API is proven by a real binary.
|
||||
|
||||
Purpose: Establish the thinnest executable path that future Phase 1 work can extend.
|
||||
Output: Framework tool, shared runtime command kernel, plugin registry, minimal config, root workspace, and permanent `examples/hello` app.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.codex/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.codex/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/REQUIREMENTS.md
|
||||
@.planning/phases/01-framework-kernel-foundation/01-CONTEXT.md
|
||||
@.planning/phases/01-framework-kernel-foundation/01-RESEARCH.md
|
||||
@.planning/phases/01-framework-kernel-foundation/01-VALIDATION.md
|
||||
@../modules/summer-compass/README.md
|
||||
@../modules/summer-bonfire/PLAN.md
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 1: Make the hello app boot and dispatch a plugin command</name>
|
||||
<files>go.mod, go.sum, go.work, party/registry.go, backpack/app.go, pact/capabilities.go, compass/config.go, bonfire/command.go, bonfire/root.go, examples/hello/go.mod, examples/hello/main.go, examples/hello/summer.yaml, examples/hello/plugins.gen.go, examples/hello/hello_test.go, examples/hello/plugins/base/go.mod, examples/hello/plugins/base/plugin.go, examples/hello/plugins/greeter/go.mod, examples/hello/plugins/greeter/plugin.go, examples/hello/config/app.yaml</files>
|
||||
<read_first>go.mod, go.sum, go.work, .planning/phases/01-framework-kernel-foundation/01-CONTEXT.md, .planning/phases/01-framework-kernel-foundation/01-RESEARCH.md, .planning/research/ARCHITECTURE.md, ../modules/summer-bonfire/PLAN.md, party/registry.go, backpack/app.go, pact/capabilities.go, compass/config.go, bonfire/command.go, bonfire/root.go, examples/hello/go.mod, examples/hello/main.go, examples/hello/summer.yaml, examples/hello/plugins.gen.go, examples/hello/hello_test.go, examples/hello/plugins/base/go.mod, examples/hello/plugins/base/plugin.go, examples/hello/plugins/greeter/go.mod, examples/hello/plugins/greeter/plugin.go, examples/hello/config/app.yaml</read_first>
|
||||
<behavior>The hello binary loads `app.name` from `config/app.yaml`, runs Register for both plugins before Boot, and executes `greeter:hello` through an injected Output writer.</behavior>
|
||||
<action>Implement the smallest real boot path. Pin `toolchain go1.27.0` in root and generated/example modules and root `go.work` (D-05). Put the required `Plugin` interface (`ID`, `Requires`, `Register`, `Boot`) and `Register`/`Activate` in `party`, with `Register(*backpack.App) error` and `Boot(*backpack.App) error`; `backpack` must not import `party`. `Activate` must resolve IDs from the manifest order, topologically sort Requires, run every Register before any Boot, and expose the resulting plugins to command collection. Define `pact.HasCommands` returning `[]bonfire.Command` and `pact.HasConfig` returning `fs.FS` as optional type-asserted capabilities; reserve the remaining KERN-03 capability names in documentation for their first consumers. `bonfire.Command` has Name, Description, flag/argument definition and `Run(ctx, Input, Output) error`; its root factory accepts command values, not a party registry, to avoid an import cycle (D-02, D-17). Use `cobra` v1.10.2, `koanf/v2` v2.3.6 and `koanf/parsers/yaml` v1.1.1 with an injected output writer. Create two hello plugin modules with IDs `golem15.hello` and `golem15.greeter`; greeter Requires hello and provides `greeter:hello`. Create `summer.yaml` with `module`, `binary`, and an ordered `plugins` list of `{id, module}` entries; write initial `main.go` and `plugins.gen.go` from those two entries so the app already runs before codegen is automated in Task 2. Add root `go.work` entries for framework, app and both plugins. Keep compass at a working `config/app.yaml` section load and dot-path String lookup here; Plan 02 fills the full precedence contract. Write the smoke test before implementation inside this task, but commit only once both root and hello module tests are green.</action>
|
||||
<verify><automated>go vet ./... && go test ./... && (cd examples/hello && go vet ./... && go test ./...)</automated></verify>
|
||||
<acceptance_criteria>
|
||||
- `party.Plugin` has ID, Requires, Register and Boot; `backpack` has no import of `party`.
|
||||
- `bonfire.NewRoot` or equivalent takes a slice of `bonfire.Command` and is used by the hello main.
|
||||
- `examples/hello` contains two independently versioned plugin modules and a working `greeter:hello` command.
|
||||
- Root and hello `go vet ./...` and `go test ./...` exit 0.
|
||||
</acceptance_criteria>
|
||||
<done>The initial hello command executes through a real app activation path with no package import cycle.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Generate and build the hello app from its ordered manifest</name>
|
||||
<files>internal/build/build.go, cmd/summer/main.go, examples/hello/summer.yaml, examples/hello/main.go, examples/hello/plugins.gen.go, examples/hello/hello_test.go, go.sum</files>
|
||||
<read_first>go.mod, go.sum, go.work, party/registry.go, bonfire/root.go, examples/hello/go.mod, examples/hello/main.go, examples/hello/summer.yaml, examples/hello/plugins.gen.go, examples/hello/hello_test.go, .planning/phases/01-framework-kernel-foundation/01-CONTEXT.md, .planning/phases/01-framework-kernel-foundation/01-RESEARCH.md, internal/build/build.go, cmd/summer/main.go</read_first>
|
||||
<action>Implement `summer build` in the framework-owned `cmd/summer` (D-01, D-04). Parse `examples/hello/summer.yaml` as an explicit ordered `plugins` list of `{id, module}` entries, reject duplicate/invalid entries, generate app-root `main.go` and `plugins.gen.go` containing blank imports plus an ordered plugin-ID slice, and invoke `go build -o bin/hello .` with `exec.CommandContext` in the app directory. Keep generated bytes stable and rewrite only when changed. `main.go` calls the same `bonfire` root constructor as the tool, then activates the manifest IDs; never rely on Go package init order for lifecycle order. Tool `build` prints measured build duration. `go install ./cmd/summer` builds a reusable tool while the app binary remains a distinct artifact. Update the smoke test to run the built binary's `greeter:hello` command and assert its config value. Do not add HTTP, DB, auth or full scaffolding.</action>
|
||||
<verify><automated>go vet ./... && go test ./... && (cd examples/hello && go vet ./... && go test ./... && go run ../../cmd/summer build && ./bin/hello greeter:hello)</automated></verify>
|
||||
<acceptance_criteria>
|
||||
- `summer build` creates `examples/hello/plugins.gen.go` with imports in `summer.yaml` order and creates an executable `examples/hello/bin/hello`.
|
||||
- Two consecutive `summer build` calls produce byte-identical generated files.
|
||||
- `./bin/hello greeter:hello` exits 0 and prints a value from `config/app.yaml`.
|
||||
- The tool does not import an example plugin package or contain a hard-coded example module path.
|
||||
</acceptance_criteria>
|
||||
<done>A developer can build and run the independent hello app through the installed framework tool.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|---|---|
|
||||
| App manifest → generated Go and build process | A repository-controlled YAML file supplies module import paths and output name. |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
||||
|---|---|---|---|---|
|
||||
| T-01-01 | Tampering | `internal/build` manifest parser | mitigate | Validate module path syntax and duplicate IDs before generating code; render imports with Go string quoting. |
|
||||
| T-01-02 | Elevation of privilege | `summer build` process launch | mitigate | Use `exec.CommandContext("go", ...)` with separate args and fixed app working directory; never invoke a shell string from YAML. |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- Run `go vet ./...` and `go test ./...` in the root and hello modules after each task commit.
|
||||
- Run `summer build` twice; compare generated file hashes and execute `bin/hello greeter:hello`.
|
||||
- Confirm the app binary imports the framework module but the framework tool does not import hello.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- A separate `summer` tool and generated hello binary build under Go 1.27.0.
|
||||
- The hello plugin command runs from the app binary using shared `bonfire` wiring.
|
||||
- Two plugins Register before either Boots, irrespective of manifest order.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/01-framework-kernel-foundation/01-01-SUMMARY.md` after completion.
|
||||
</output>
|
||||
168
.planning/phases/01-framework-kernel-foundation/01-02-PLAN.md
Normal file
168
.planning/phases/01-framework-kernel-foundation/01-02-PLAN.md
Normal file
@@ -0,0 +1,168 @@
|
||||
---
|
||||
phase: 01-framework-kernel-foundation
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on: ["01-01"]
|
||||
files_modified:
|
||||
- go.mod
|
||||
- go.sum
|
||||
- compass/config.go
|
||||
- compass/env.go
|
||||
- compass/persist.go
|
||||
- party/registry.go
|
||||
- pact/capabilities.go
|
||||
- backpack/app.go
|
||||
- backpack/services.go
|
||||
- festival/bus.go
|
||||
- towel/context.go
|
||||
- examples/hello/plugins/base/plugin.go
|
||||
- examples/hello/plugins/base/config/config.yaml
|
||||
- examples/hello/plugins/greeter/plugin.go
|
||||
- examples/hello/plugins/optional/go.mod
|
||||
- examples/hello/plugins/optional/plugin.go
|
||||
- examples/hello/plugins/optional/config/config.yaml
|
||||
- examples/hello/summer.yaml
|
||||
- examples/hello/go.mod
|
||||
- examples/hello/plugins.gen.go
|
||||
- examples/hello/hello_test.go
|
||||
- examples/hello/config/env/development/app.yaml
|
||||
- go.work
|
||||
autonomous: true
|
||||
requirements: [KERN-01, KERN-02, KERN-03, KERN-05, KERN-06, KERN-07, KERN-08]
|
||||
must_haves:
|
||||
truths:
|
||||
- "D-06 D-07 D-08 D-09: `golem15.optional.*` config resolves from embedded plugin defaults, sorted base/env YAML, `SUMMER_` variables, persisted overrides and runtime Set in the specified priority; Reload clears Set."
|
||||
- "D-10: A missing required plugin or cycle stops app boot with an error naming the involved plugin IDs and exits non-zero."
|
||||
- "D-11: An app-scoped HasPlugin check and typed `backpack` lookup let greeter skip an absent optional plugin without importing its package."
|
||||
- "D-12 D-13: Typed struct event listeners run by descending priority with stable ties; the three dispatch modes obey their error and panic rules."
|
||||
- "Request actor, organization, collection and locale values pass through `context.Context`, with no package-global request state."
|
||||
artifacts:
|
||||
- path: compass/config.go
|
||||
provides: Config precedence and dot-path access
|
||||
- path: backpack/services.go
|
||||
provides: Typed app-scoped service registry
|
||||
- path: festival/bus.go
|
||||
provides: Typed event dispatch
|
||||
- path: towel/context.go
|
||||
provides: Typed request context accessors
|
||||
key_links:
|
||||
- from: party/registry.go
|
||||
to: compass/config.go
|
||||
via: `HasConfig` discovery before plugin Register
|
||||
- from: examples/hello/plugins/greeter/plugin.go
|
||||
to: backpack/services.go
|
||||
via: Optional typed service lookup during Boot
|
||||
- from: backpack/app.go
|
||||
to: festival/bus.go
|
||||
via: Bus instance owned by each app
|
||||
---
|
||||
|
||||
<objective>
|
||||
As a framework developer, I can change a hello plugin's config, compose it with an optional plugin, and observe a typed event through the built app, so kernel extension behavior is proven in use.
|
||||
|
||||
Purpose: Complete only the config, lifecycle, service and event behavior needed by the first real app slice.
|
||||
Output: Full `compass` layering, deterministic `party` failures, typed `backpack` services, `festival` dispatch and context accessors.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.codex/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.codex/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/REQUIREMENTS.md
|
||||
@.planning/phases/01-framework-kernel-foundation/01-CONTEXT.md
|
||||
@.planning/phases/01-framework-kernel-foundation/01-RESEARCH.md
|
||||
@.planning/phases/01-framework-kernel-foundation/01-VALIDATION.md
|
||||
@.planning/phases/01-framework-kernel-foundation/01-01-SUMMARY.md
|
||||
@../modules/summer-compass/README.md
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 1: Make the hello command observe full layered config</name>
|
||||
<files>go.mod, go.sum, compass/config.go, compass/env.go, compass/persist.go, pact/capabilities.go, party/registry.go, examples/hello/plugins/base/plugin.go, examples/hello/plugins/base/config/config.yaml, examples/hello/config/env/development/app.yaml, examples/hello/hello_test.go</files>
|
||||
<read_first>go.mod, go.sum, compass/config.go, compass/env.go, compass/persist.go, pact/capabilities.go, party/registry.go, examples/hello/plugins/base/plugin.go, examples/hello/plugins/base/config/config.yaml, examples/hello/config/app.yaml, examples/hello/config/env/development/app.yaml, examples/hello/hello_test.go, .planning/phases/01-framework-kernel-foundation/01-CONTEXT.md, ../modules/summer-compass/README.md</read_first>
|
||||
<behavior>With `SUMMER_ENV=development`, the hello command sees a deep-merged env overlay; `SUMMER_GOLEM15__HELLO__POSTS_PER_PAGE` overrides plugin defaults, and Set/Persist/Reload follow D-09.</behavior>
|
||||
<action>Extend `compass` from Plan 01 with deterministic layers (D-06, D-07, D-08, D-09), using pinned koanf providers/file v1.2.1, providers/confmap v1.0.1 and providers/env/v2 v2.0.1: embedded `HasConfig.ConfigFS() fs.FS` files, with `config/config.yaml` merged at the bare plugin ID path (for example `golem15.hello.posts_per_page`) via koanf `MergeAt`; sorted `config/<section>.yaml` base files; sorted `config/env/<env>/<section>.yaml` overlays excluding `overrides.yaml`; `SUMMER_` variables split only on `__` so `POSTS_PER_PAGE` remains one snake_case leaf; `config/env/<env>/overrides.yaml`; then in-memory Set. Read `.env` in the app root into an injected env list only for keys absent from real `os.Environ`; do not mutate process environment. Add `Lookup(path) (any,bool)`, `String`, `Int`, `Bool`, `Has` and typed `LoadSection(path,out)` using `koanf` tags. `SUMMER_ENV` defaults to `production` and explicit constructor env wins. `Persist` atomically writes runtime overrides to the env-specific overrides file with restrictive permissions; `Reload` rebuilds from disk and clears runtime Set. Guard concurrent access with a lock or immutable snapshot. Sanitize env names before creating paths. Keep all behavior visible through the hello command.</action>
|
||||
<verify><automated>go test ./compass ./pact && (cd examples/hello && go test ./...)</automated></verify>
|
||||
<acceptance_criteria>
|
||||
- `SUMMER_DATABASE__HOST` resolves to `database.host`; `SUMMER_GOLEM15__HELLO__POSTS_PER_PAGE` resolves to `golem15.hello.posts_per_page`.
|
||||
- Single underscores remain literal in leaf keys and real environment variables win over `.env` values.
|
||||
- `Set` wins over persisted overrides; `Persist` writes `config/env/<env>/overrides.yaml`; `Reload` clears Set and re-reads files.
|
||||
- Untouched keys in nested YAML maps survive later overlays.
|
||||
</acceptance_criteria>
|
||||
<done>The hello app reads exactly the D-09 priority order through typed and dot-path config access.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 2: Let a hello plugin use optional services without a hard import</name>
|
||||
<files>party/registry.go, pact/capabilities.go, backpack/app.go, backpack/services.go, examples/hello/plugins/greeter/plugin.go, examples/hello/plugins/optional/go.mod, examples/hello/plugins/optional/plugin.go, examples/hello/plugins/optional/config/config.yaml, examples/hello/summer.yaml, examples/hello/go.mod, examples/hello/plugins.gen.go, examples/hello/hello_test.go, go.work</files>
|
||||
<read_first>party/registry.go, pact/capabilities.go, backpack/app.go, backpack/services.go, examples/hello/plugins/greeter/plugin.go, examples/hello/plugins/optional/go.mod, examples/hello/plugins/optional/plugin.go, examples/hello/plugins/optional/config/config.yaml, examples/hello/summer.yaml, examples/hello/go.mod, examples/hello/plugins.gen.go, examples/hello/hello_test.go, go.work, .planning/phases/01-framework-kernel-foundation/01-CONTEXT.md, .planning/research/ARCHITECTURE.md</read_first>
|
||||
<behavior>Greeter boots whether `golem15.optional` is present or absent; when present it uses a service published under a shared interface, and when absent it skips that integration. Invalid Requires graphs fail before any Boot.</behavior>
|
||||
<action>Add a third trivial hello plugin `golem15.optional` as its own module and optional manifest entry (D-03, D-11). Put the small shared service interface in `pact`, not in the optional plugin package. Implement app-owned `backpack.Registry` with typed `Publish[T]` and `Lookup[T] (T,bool)` on a concrete Go 1.27 type (or equivalent typed helpers), duplicate-provider handling, and no process-global state. `backpack.App.HasPlugin(id)` must use the complete registered set, not only already-Booted plugins. During greeter Boot, use `HasPlugin("golem15.optional")` and typed lookup; the greeter package must not import the optional package. Harden `party.Activate` to reject duplicate IDs, missing Requires and dependency cycles with named IDs (D-10), then keep stable ordering of independent plugins while guaranteeing all Register calls precede every Boot. Define and type-assert the Phase 1 `HasConfig`/`HasCommands` capabilities. Document the future KERN-03 capability families (`HasModels`, `HasMigrations`, `HasRoutes`, `HasMiddleware`, `HasJobs`, `HasListeners`, `HasAdminControllers`, `HasNavigation`, `HasPermissions`, `HasSchedule`, `HasMailTemplates`, `HasLang`) without coupling their payloads to absent Phase 3 packages. Do not implement their adapters in this phase.</action>
|
||||
<verify><automated>go test ./party ./backpack ./pact && (cd examples/hello && go test ./...)</automated></verify>
|
||||
<acceptance_criteria>
|
||||
- Reordered manifest input still causes all Register calls to precede every Boot and respects `Requires()`.
|
||||
- Missing dependency and cycle errors include the plugin IDs involved and make app boot exit non-zero.
|
||||
- Greeter runs with and without `golem15.optional`; its source has no import of the optional plugin module.
|
||||
- Typed service lookup returns `(value, false)` for an absent interface and a concrete value when published.
|
||||
</acceptance_criteria>
|
||||
<done>The hello app demonstrates optional plugin composition and deterministic failure handling.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 3: Dispatch typed hello events with request context</name>
|
||||
<files>festival/bus.go, backpack/app.go, towel/context.go, examples/hello/plugins/base/plugin.go, examples/hello/plugins/greeter/plugin.go, examples/hello/hello_test.go</files>
|
||||
<read_first>festival/bus.go, backpack/app.go, towel/context.go, examples/hello/plugins/base/plugin.go, examples/hello/plugins/greeter/plugin.go, examples/hello/hello_test.go, .planning/phases/01-framework-kernel-foundation/01-CONTEXT.md, .planning/research/ARCHITECTURE.md, .planning/phases/01-framework-kernel-foundation/01-RESEARCH.md</read_first>
|
||||
<behavior>The hello command fires a typed struct event; listeners can notify, collect merged values, or stop at the first handled result. Context values remain scoped to that invocation.</behavior>
|
||||
<action>Implement an app-owned `festival.Bus` keyed by concrete Go event type, with synchronous dispatch on the caller goroutine and owner plugin IDs. Expose plain `Listen[T]` at priority 0 plus `ListenPriority[T]`; run higher integer priorities first and ties by registration order (D-12). Provide `Fire[T] error`, `Collect[T] (map[string]any,error)` with later invoked listener winning duplicate keys, and `UntilHandled[T] (bool,error)`. For Fire, invoke every listener and return `errors.Join`; for Collect, return partial merged data and joined errors; for UntilHandled, stop on first handled result or first error. Recover listener panics into an error naming its owner plugin ID (D-13). Do not add async fan-out. Add `towel.WithActor/Actor`, `WithOrganization/Organization`, `WithCollection/Collection`, and `WithLocale/Locale` typed context helpers using unexported key types; no package-global request values (KERN-07). Pass ctx from cobra Run into event dispatch and demonstrate one hello event listener.</action>
|
||||
<verify><automated>go test ./festival ./towel ./backpack && (cd examples/hello && go test ./...)</automated></verify>
|
||||
<acceptance_criteria>
|
||||
- Fire runs all listeners and joins their errors; Collect returns partial payload plus joined errors.
|
||||
- UntilHandled stops on its first error or first handled listener.
|
||||
- Higher priority runs first and equal priority preserves registration order.
|
||||
- A recovered panic error names the owning plugin ID; two app instances have isolated listener sets.
|
||||
- Context values are retrieved only from the passed `context.Context` and no request-state package globals exist.
|
||||
</acceptance_criteria>
|
||||
<done>The hello path demonstrates all three event modes and context-scoped request state.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|---|---|
|
||||
| YAML and `.env` → live config | Local files can change runtime behavior and persisted overrides. |
|
||||
| Plugin listener → app dispatch | A compiled plugin can panic or return an error during another plugin's event. |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
||||
|---|---|---|---|---|
|
||||
| T-01-03 | Tampering | `compass.Persist` path | mitigate | Validate env name; write only beneath configured `config/env/<env>` and use atomic replacement with restrictive file permissions. |
|
||||
| T-01-04 | Denial of service | `festival.Bus` listener | mitigate | Recover panics with owner ID and keep dispatch error behavior deterministic; no implicit goroutine fan-out. |
|
||||
| T-01-05 | Information disclosure | `.env` and config logging | mitigate | Do not print config values or secret env contents in errors or build logs. |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- Run root and nested hello `go vet ./...` and `go test ./...` after each task commit.
|
||||
- Run focused `go test -race ./compass ./party ./backpack ./festival ./towel` before this plan closes.
|
||||
- Exercise the hello command with and without the optional plugin entry.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- All six config priority levels are observable and Reload semantics are tested.
|
||||
- Optional plugin lookup and typed services work without a hard import.
|
||||
- Typed event modes obey D-12/D-13 and do not share state across apps.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/01-framework-kernel-foundation/01-02-SUMMARY.md` after completion.
|
||||
</output>
|
||||
158
.planning/phases/01-framework-kernel-foundation/01-03-PLAN.md
Normal file
158
.planning/phases/01-framework-kernel-foundation/01-03-PLAN.md
Normal file
@@ -0,0 +1,158 @@
|
||||
---
|
||||
phase: 01-framework-kernel-foundation
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 3
|
||||
depends_on: ["01-02"]
|
||||
files_modified:
|
||||
- internal/build/build.go
|
||||
- internal/build/manifest.go
|
||||
- internal/build/scaffold.go
|
||||
- internal/dev/watch.go
|
||||
- cmd/summer/main.go
|
||||
- bonfire/root.go
|
||||
- bonfire/command.go
|
||||
- bonfire/output.go
|
||||
- bonfire/widgets.go
|
||||
- bonfire/prompts.go
|
||||
- bonfire/output_test.go
|
||||
- bonfire/prompts_test.go
|
||||
- internal/build/build_test.go
|
||||
- internal/dev/watch_test.go
|
||||
- examples/hello/plugins/greeter/plugin.go
|
||||
- go.mod
|
||||
- go.sum
|
||||
autonomous: true
|
||||
requirements: [KERN-04, KERN-09, CLI-01]
|
||||
must_haves:
|
||||
truths:
|
||||
- "D-01 D-03 D-04 D-05: `summer make:plugin` scaffolds a compiling plugin and `summer plugin:add` adds it to the ordered manifest/workspace; `summer build` regenerates imports and a separate app binary."
|
||||
- "D-14: Spinner, progress, table, ask/confirm/choice/secret prompts and color policy use standard library plus `x/term`, with deterministic non-TTY output."
|
||||
- "D-15 D-17: Tool and app commands use colon-style names; plugin command metadata, flags, arguments and injected Output are wrapped by the shared cobra kernel."
|
||||
- "D-16: `summer dev` watches sources/config/manifests, debounces, calls the same build function, restarts the app only after successful build, and prints measured rebuild latency."
|
||||
artifacts:
|
||||
- path: internal/build/scaffold.go
|
||||
provides: Minimal plugin scaffold and manifest/workspace registration
|
||||
- path: bonfire/widgets.go
|
||||
provides: Spinner, progress and table rendering
|
||||
- path: bonfire/prompts.go
|
||||
provides: Prompt handling and non-TTY fallback
|
||||
- path: internal/dev/watch.go
|
||||
provides: Built-in watch rebuild loop
|
||||
key_links:
|
||||
- from: cmd/summer/main.go
|
||||
to: internal/build/scaffold.go
|
||||
via: `make:plugin` and `plugin:add` tool commands
|
||||
- from: internal/dev/watch.go
|
||||
to: internal/build/build.go
|
||||
via: One shared build function
|
||||
- from: bonfire/root.go
|
||||
to: bonfire/output.go
|
||||
via: Output injection into plugin commands
|
||||
---
|
||||
|
||||
<objective>
|
||||
As a framework developer, I can add a plugin, rebuild, see useful CLI output, and keep an app running while editing source, so compiled plugins have a practical development loop.
|
||||
|
||||
Purpose: Complete the Phase 1 tool experience around the already bootable hello app.
|
||||
Output: `make:plugin`, `plugin:add`, rich CLI output and built-in `summer dev` watch loop.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.codex/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.codex/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/REQUIREMENTS.md
|
||||
@.planning/phases/01-framework-kernel-foundation/01-CONTEXT.md
|
||||
@.planning/phases/01-framework-kernel-foundation/01-RESEARCH.md
|
||||
@.planning/phases/01-framework-kernel-foundation/01-VALIDATION.md
|
||||
@.planning/phases/01-framework-kernel-foundation/01-02-SUMMARY.md
|
||||
@../modules/summer-bonfire/PLAN.md
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Add and rebuild a compiling plugin from the tool</name>
|
||||
<files>internal/build/build.go, internal/build/manifest.go, internal/build/scaffold.go, internal/build/build_test.go, cmd/summer/main.go</files>
|
||||
<read_first>internal/build/build.go, internal/build/manifest.go, internal/build/scaffold.go, internal/build/build_test.go, cmd/summer/main.go, examples/hello/summer.yaml, examples/hello/go.mod, examples/hello/plugins.gen.go, examples/hello/plugins/base/plugin.go, go.work, .planning/phases/01-framework-kernel-foundation/01-CONTEXT.md, .planning/phases/01-framework-kernel-foundation/01-RESEARCH.md</read_first>
|
||||
<action>Complete D-01/D-03/D-04/D-05 using `summer.yaml` as the sole ordered source of plugin IDs and module paths. `summer make:plugin <vendor.plugin>` scaffolds `plugins/<plugin>/go.mod` with module path `<app-module>/plugins/<plugin>` and `toolchain go1.27.0`, `plugin.go` implementing ID/Requires/Register/Boot and `init(){ party.Register(...) }`, plus an empty `config/` placeholder; no model/migration/controller/command stubs. `summer plugin:add <local-plugin-dir>` reads the module path from that directory's `go.mod`, validates the ID and module path, adds the plugin once to `summer.yaml`, updates the nearest app `go.work` with `go work use`, and updates app `go.mod` as needed for a portable dependency graph. Preserve manifest order and reject conflicting IDs. `summer build` must regenerate both app files from the updated manifest and print elapsed build time; no map iteration controls output. Keep tool source independent of app/plugin imports. Exercise the commands on a temporary copy of `examples/hello` so the permanent testbed stays small.</action>
|
||||
<verify><automated>go test ./internal/build ./cmd/summer && (cd examples/hello && go test ./...)</automated></verify>
|
||||
<acceptance_criteria>
|
||||
- `make:plugin golem15.demo` produces a compiling plugin module with only `go.mod`, `plugin.go` and `config/`.
|
||||
- `plugin:add` adds one manifest entry and workspace module; repeating it is idempotent.
|
||||
- `summer build` produces byte-stable `main.go` and `plugins.gen.go` from the updated manifest.
|
||||
- The framework tool has no import of an app-specific plugin package.
|
||||
</acceptance_criteria>
|
||||
<done>A new local plugin can be scaffolded, added and compiled into the hello app.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Give tool and plugin commands deterministic rich output</name>
|
||||
<files>bonfire/root.go, bonfire/command.go, bonfire/output.go, bonfire/widgets.go, bonfire/prompts.go, bonfire/output_test.go, bonfire/prompts_test.go, cmd/summer/main.go, examples/hello/plugins/greeter/plugin.go, go.mod, go.sum</files>
|
||||
<read_first>bonfire/root.go, bonfire/command.go, bonfire/output.go, bonfire/widgets.go, bonfire/prompts.go, bonfire/output_test.go, bonfire/prompts_test.go, cmd/summer/main.go, examples/hello/plugins/greeter/plugin.go, go.mod, go.sum, ../modules/summer-bonfire/PLAN.md, .planning/phases/01-framework-kernel-foundation/01-CONTEXT.md, .planning/phases/01-framework-kernel-foundation/01-RESEARCH.md</read_first>
|
||||
<action>Implement D-14/D-15/D-17 with stdlib formatting and `golang.org/x/term` v0.46.0 only for terminal capability/raw/password primitives. `bonfire.Output` is constructed once from injected stdin/stdout/stderr and terminal/color policy; the cobra adapter passes `ctx`, parsed `Input`, and that Output to every `bonfire.Command`. Command metadata defines arguments and flags without duplicating root wiring; reject plugin command names without `<namespace>:<verb>`, while kernel tool commands are `make:plugin`, `plugin:add`, `build`, `dev`. Implement braille spinner and gradient progress for TTY, box-drawing table, ask/confirm/choice/secret prompts. For non-TTY: spinner prints one `[...] message`; progress prints `[N/M] pct%` at 10% steps; table is tab-separated; ask/choice/secret read stdin lines; confirm uses its default. `NO_COLOR` or `TERM=dumb` disables ANSI; `FORCE_COLOR` enables it only when neither disable condition is present. `secret` uses `term.ReadPassword` on TTY and a plain stdin line otherwise. Make the hello plugin command exercise table and one prompt without hanging when stdin is closed.</action>
|
||||
<verify><automated>go test ./bonfire ./cmd/summer && (cd examples/hello && go test ./...)</automated></verify>
|
||||
<acceptance_criteria>
|
||||
- `summer make:plugin --help` and the app's `greeter:hello --help` are generated through the shared `bonfire` cobra adapter.
|
||||
- Non-TTY output has no ANSI when `NO_COLOR=1` or `TERM=dumb` and uses the exact spinner/progress/table fallback shapes above.
|
||||
- Injected streams let tests capture Output and feed prompt answers without a real TTY.
|
||||
- Plugin command names without `:` fail registration with a named error.
|
||||
</acceptance_criteria>
|
||||
<done>Both binaries expose namespaced commands and usable output in interactive and CI environments.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Rebuild and restart the hello binary during source edits</name>
|
||||
<files>internal/dev/watch.go, internal/dev/watch_test.go, internal/build/build.go, cmd/summer/main.go, go.mod, go.sum</files>
|
||||
<read_first>internal/build/build.go, internal/dev/watch.go, internal/dev/watch_test.go, cmd/summer/main.go, go.mod, go.sum, examples/hello/summer.yaml, .planning/phases/01-framework-kernel-foundation/01-CONTEXT.md, .planning/phases/01-framework-kernel-foundation/01-RESEARCH.md</read_first>
|
||||
<action>Implement `summer dev` with `github.com/fsnotify/fsnotify` v1.10.1 (D-16). Watch app workspace directories recursively by walking them and adding new directories on Create; include `.go`, `.yaml`, `.yml`, `.env`, `go.mod`, `go.work` and plugin manifests. Ignore `.git`, `bin`, temp build outputs and generated app files to prevent loops. Debounce bursts (for example 200ms) and serialize builds. Call the exact `internal/build` function used by `summer build`; after success stop and reap the old child, start the new app binary with inherited streams, and print `rebuild: <duration>` measured from build start to completion. On build failure keep the old process running and print the build error; on context cancellation stop/reap the child and close the watcher. Do not execute manifest-derived shell commands. Add a smoke test with a temporary workspace to assert one restart after a source edit, no restart after ignored generated output and a latency line.</action>
|
||||
<verify><automated>go test ./internal/dev ./internal/build ./cmd/summer && (cd examples/hello && go test ./...)</automated></verify>
|
||||
<acceptance_criteria>
|
||||
- Editing a watched hello plugin `.go` file causes one debounced rebuild and successful restart.
|
||||
- Build failure leaves the last successful child running; cancellation reaps it.
|
||||
- A generated `plugins.gen.go` write does not cause an infinite rebuild loop.
|
||||
- Every rebuild cycle prints measured elapsed time, including the cycle after a single-plugin edit.
|
||||
</acceptance_criteria>
|
||||
<done>The local development loop rebuilds and restarts without an external watcher install.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|---|---|
|
||||
| CLI plugin input → filesystem | Plugin ID and directory values select paths and module names. |
|
||||
| Filesystem event → build/restart | File changes trigger local tool execution and process replacement. |
|
||||
| Prompt → terminal | Secret values may be echoed or captured if mode detection is wrong. |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
||||
|---|---|---|---|---|
|
||||
| T-01-06 | Tampering | `make:plugin` and `plugin:add` paths | mitigate | Validate lowercase vendor.plugin ID and keep generated paths under the app root; reject traversal and duplicate/conflicting entries. |
|
||||
| T-01-07 | Denial of service | `summer dev` watcher | mitigate | Debounce, ignore output dirs, serialize builds, and reap child processes on cancellation. |
|
||||
| T-01-08 | Information disclosure | `bonfire` secret prompt | mitigate | Use `term.ReadPassword` for TTY and document plain stdin fallback; never log answers. |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- After each task commit, run `go vet ./... && go test ./...` in root and hello modules.
|
||||
- Smoke-test `make:plugin`, `plugin:add`, `build` and `dev` against temporary copies of `examples/hello`.
|
||||
- Capture non-TTY output with pipes; manually inspect one TTY command during phase verification.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- A new plugin is scaffolded, registered and built using only the framework tool.
|
||||
- CLI output degrades predictably without a TTY.
|
||||
- A plugin source change triggers a measured rebuild and app restart.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/01-framework-kernel-foundation/01-03-SUMMARY.md` after completion.
|
||||
</output>
|
||||
131
.planning/phases/01-framework-kernel-foundation/01-04-PLAN.md
Normal file
131
.planning/phases/01-framework-kernel-foundation/01-04-PLAN.md
Normal file
@@ -0,0 +1,131 @@
|
||||
---
|
||||
phase: 01-framework-kernel-foundation
|
||||
plan: 04
|
||||
type: execute
|
||||
wave: 4
|
||||
depends_on: ["01-03"]
|
||||
files_modified:
|
||||
- compass/config_test.go
|
||||
- party/registry_test.go
|
||||
- backpack/services_test.go
|
||||
- festival/bus_test.go
|
||||
- towel/context_test.go
|
||||
- bonfire/output_test.go
|
||||
- bonfire/prompts_test.go
|
||||
- internal/build/build_test.go
|
||||
- internal/dev/watch_test.go
|
||||
- examples/hello/hello_test.go
|
||||
- scripts/check-phase1.sh
|
||||
- .planning/phases/01-framework-kernel-foundation/01-VALIDATION.md
|
||||
autonomous: true
|
||||
requirements: [KERN-01, KERN-02, KERN-03, KERN-04, KERN-05, KERN-06, KERN-07, KERN-08, KERN-09, CLI-01]
|
||||
must_haves:
|
||||
truths:
|
||||
- "Every KERN-01 through KERN-09 and CLI-01 contract has a behavioral test, including failure and non-TTY paths."
|
||||
- "The final check runs `go vet`, `go test`, and `go test -race` in the framework, hello app and each hello plugin module."
|
||||
- "A temporary app can run `summer build` and its generated binary; source edit under `summer dev` records measured rebuild time and restarts the child."
|
||||
artifacts:
|
||||
- path: scripts/check-phase1.sh
|
||||
provides: Repeatable phase-wide verification command
|
||||
- path: compass/config_test.go
|
||||
provides: Config precedence regression tests
|
||||
- path: festival/bus_test.go
|
||||
provides: Event dispatch regression tests
|
||||
- path: examples/hello/hello_test.go
|
||||
provides: Built-app integration test
|
||||
key_links:
|
||||
- from: scripts/check-phase1.sh
|
||||
to: examples/hello/go.mod
|
||||
via: Checks nested app and plugin modules independently
|
||||
- from: examples/hello/hello_test.go
|
||||
to: cmd/summer/main.go
|
||||
via: Builds and executes a generated app binary
|
||||
---
|
||||
|
||||
<objective>
|
||||
As a framework maintainer, I can run one repeatable check that proves the Phase 1 kernel and development loop before Phase 3 depends on them.
|
||||
|
||||
Purpose: Add the phase's dedicated unit and integration test plan, with broad behavior and failure coverage.
|
||||
Output: Focused Go tests, a nested-module check script, a generated-binary smoke and completed Nyquist validation map.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.codex/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.codex/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/REQUIREMENTS.md
|
||||
@.planning/phases/01-framework-kernel-foundation/01-CONTEXT.md
|
||||
@.planning/phases/01-framework-kernel-foundation/01-RESEARCH.md
|
||||
@.planning/phases/01-framework-kernel-foundation/01-VALIDATION.md
|
||||
@.planning/phases/01-framework-kernel-foundation/01-03-SUMMARY.md
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Cover config, lifecycle, services, events and context behavior</name>
|
||||
<files>compass/config_test.go, party/registry_test.go, backpack/services_test.go, festival/bus_test.go, towel/context_test.go</files>
|
||||
<read_first>compass/config.go, compass/env.go, compass/persist.go, party/registry.go, backpack/app.go, backpack/services.go, festival/bus.go, towel/context.go, compass/config_test.go, party/registry_test.go, backpack/services_test.go, festival/bus_test.go, towel/context_test.go, .planning/phases/01-framework-kernel-foundation/01-CONTEXT.md</read_first>
|
||||
<action>Expand earlier smoke tests into table-driven behavioral tests for all config precedence levels, malformed YAML, snake_case env keys, `.env` non-override, dotted plugin namespace, typed section decode, Set/Persist/Reload and concurrent reads. Test plugin duplicate/missing/cycle errors, reordered manifest inputs, all-Register-before-any-Boot, HasConfig/HasCommands assertions, absent/present HasPlugin and typed service lookup with two separate App instances. Test three typed event dispatch modes, priority and stable ties, partial collect payload, joined errors, stop-on-error, owner-labelled panic recovery and concurrent independent app buses. Test actor/organization/collection/locale context accessors and nested context isolation. Fix any production defect exposed by these tests in the same task, keeping API behavior from D-01–D-13 intact. Report per-package coverage for these packages; cover contract branches rather than chasing an arbitrary percentage.</action>
|
||||
<verify><automated>go test -race ./compass ./party ./backpack ./festival ./towel ./pact</automated></verify>
|
||||
<acceptance_criteria>
|
||||
- Each KERN-01/02/03/05/06/07/08 behavior above has at least one source-to-observable assertion.
|
||||
- `go test -race ./compass ./party ./backpack ./festival ./towel ./pact` exits 0.
|
||||
- No test asserts only implementation structure when a behavior can be observed.
|
||||
</acceptance_criteria>
|
||||
<done>The kernel's core behavioral and failure contracts are regression tested.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Cover tool, output, watch loop and every workspace module</name>
|
||||
<files>bonfire/output_test.go, bonfire/prompts_test.go, internal/build/build_test.go, internal/dev/watch_test.go, examples/hello/hello_test.go, scripts/check-phase1.sh, .planning/phases/01-framework-kernel-foundation/01-VALIDATION.md</files>
|
||||
<read_first>bonfire/root.go, bonfire/output.go, bonfire/widgets.go, bonfire/prompts.go, internal/build/build.go, internal/build/manifest.go, internal/build/scaffold.go, internal/dev/watch.go, cmd/summer/main.go, examples/hello/summer.yaml, bonfire/output_test.go, bonfire/prompts_test.go, internal/build/build_test.go, internal/dev/watch_test.go, examples/hello/hello_test.go, scripts/check-phase1.sh, .planning/phases/01-framework-kernel-foundation/01-VALIDATION.md</read_first>
|
||||
<action>Test namespaced command discovery and flag/argument parsing, injected output capture, all non-TTY widget/prompt fallbacks, `NO_COLOR`/`FORCE_COLOR`/`TERM=dumb` policy and closed stdin. In a temporary copied hello workspace, assert `make:plugin` minimal files, `plugin:add` idempotence, stable generated bytes, malicious ID/path rejection, `summer build` success, built hello command output and non-zero failure on invalid Requires. Use deterministic build/process hooks for debounce/cancellation tests, plus a real fsnotify temp-workspace edit for one successful rebuild/restart; capture and assert a `rebuild: <duration>` line without assuming a threshold. Add `scripts/check-phase1.sh` that runs `go vet ./...`, `go test ./...` and `go test -race ./...` from root, `examples/hello`, and each nested plugin module; include built-binary integration. Update VALIDATION.md's per-task map/status and set `nyquist_compliant: true` only when every planned automated check exists and passes. Fix production defects the tests expose, while keeping this plan focused on verification.</action>
|
||||
<verify><automated>bash scripts/check-phase1.sh</automated></verify>
|
||||
<acceptance_criteria>
|
||||
- `bash scripts/check-phase1.sh` exits 0 and names root, hello app, base, greeter and optional plugin modules.
|
||||
- Test suite observes `summer build` and a separately executed hello binary, not only package compilation.
|
||||
- Watch test observes a source-change rebuild, restart and measured latency line; generated-file edits do not loop.
|
||||
- Non-TTY spinner/progress/table/prompt and color policy assertions run without a terminal.
|
||||
- VALIDATION.md records passing automated checks and `nyquist_compliant: true` only after the full script passes.
|
||||
</acceptance_criteria>
|
||||
<done>The phase has a repeatable, race-enabled verification command and completed validation evidence.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|---|---|
|
||||
| Test fixture input → tool/process | Malformed manifests and source edits should fail safely during local tooling. |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
||||
|---|---|---|---|---|
|
||||
| T-01-09 | Tampering | Manifest and scaffold inputs | mitigate | Add negative tests for invalid IDs, duplicate modules and path traversal. |
|
||||
| T-01-10 | Denial of service | Watch loop | mitigate | Test debounce, ignored outputs, failed build and child cleanup. |
|
||||
| T-01-11 | Information disclosure | CLI secret and config | mitigate | Assert secret prompt answer and config values do not leak into captured error/output streams. |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- Run `bash scripts/check-phase1.sh` and inspect its per-module output.
|
||||
- Confirm `go test -race` passes in all five modules and `go vet` is green.
|
||||
- Record measured single-plugin rebuild time in 01-04-SUMMARY.md.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- All ten phase requirement IDs have automated behavioral evidence.
|
||||
- The final testing plan runs after implementation and passes across all workspace modules.
|
||||
- VALIDATION.md can be signed off without missing tests or broken Wave 0 references.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/01-framework-kernel-foundation/01-04-SUMMARY.md` after completion.
|
||||
</output>
|
||||
@@ -121,11 +121,11 @@ The fast feedback command should take seconds once dependencies are cached. CI c
|
||||
</validation_architecture>
|
||||
|
||||
<open_questions>
|
||||
## Open Questions
|
||||
## Open Questions (RESOLVED)
|
||||
|
||||
1. **Exact optional capability method signatures for Phase 3+** — `HasModels`, `HasRoutes` and peers are named in KERN-03, but their concrete adapters do not exist yet. Prefer contracts that compile without importing future packages and document that signatures may be refined when the first consumer arrives.
|
||||
2. **Plugin config representation with dotted IDs** — test koanf lookup behavior for a YAML key `golem15.fonoteka` versus nested `golem15: {fonoteka: ...}` before locking the on-disk form. The public logical path and env mapping remain fixed by D-06/D-08.
|
||||
3. **Dev watch process ownership across platforms** — implement and test Linux first in this environment; keep command execution behind a small abstraction if later platform handling needs adjustment.
|
||||
1. **Exact optional capability method signatures for Phase 3+ — RESOLVED:** Phase 1 implements the type-asserted `HasConfig` and `HasCommands` adapters 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 `[]any` placeholders are introduced just to fill an interface list.
|
||||
2. **Plugin config representation with dotted IDs — RESOLVED:** Each plugin's embedded `config/config.yaml` contains keys relative to its ID; `compass` merges it at the logical path `golem15.fonoteka` using koanf `MergeAt`. Additional embedded section files, if used later, merge at `golem15.fonoteka.<filename>`. App-level `config/<section>.yaml` retains filename-as-section behavior. This avoids relying on a dotted YAML root key while preserving the D-06/D-08 public paths.
|
||||
3. **Dev watch process ownership across platforms — RESOLVED:** Use `exec.CommandContext` and 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>
|
||||
|
||||
<sources>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# Walking Skeleton — SummerCMS (Go)
|
||||
|
||||
**Phase:** 1
|
||||
**Generated:** 2026-09-16
|
||||
|
||||
## Capability Proven End-to-End
|
||||
|
||||
A framework developer can run `summer build` in `examples/hello`, boot a separately compiled app from YAML config and generated plugin imports, then execute a plugin command through the app binary.
|
||||
|
||||
## Architectural Decisions
|
||||
|
||||
| Decision | Choice | Rationale |
|
||||
|---|---|---|
|
||||
| Framework | Go 1.27.0, module `git.golem15.com/golem15/summercms` | Locked by Phase 1 context. |
|
||||
| Plugin linkage | App `summer.yaml` → generated `plugins.gen.go` blank imports | Compiled plugins with an explicit ordered manifest. |
|
||||
| App composition | `party` lifecycle, `backpack.App`, `compass`, `festival`, `bonfire` | The smallest Phase 1 kernel path with a real consumer. |
|
||||
| Data layer | Phase 3: Postgres + GORM/gormigrate | Phase 1 context excludes ORM and DB; the first real data slice is `GET /_fonoteka/api/v1/genres`. |
|
||||
| HTTP/auth/UI | Phase 3 HTTP/auth; existing Nuxt UI remains the app frontend | Phase 1 is a framework CLI phase and excludes router, auth and UI. |
|
||||
| Dev target | Local `summer build` and `summer dev` in `examples/hello` | Makes plugin development observable before deployment is introduced. |
|
||||
| Directory layout | Framework packages at root, tool in `cmd/summer`, app/modules under `examples/hello` | Proves two-binary and two-repository architecture without importing the real app. |
|
||||
|
||||
## Stack Touched in Phase 1
|
||||
|
||||
- [ ] Framework module and root `go.work` with separate hello app/plugin modules
|
||||
- [ ] Generated app entry point and plugin import list
|
||||
- [ ] YAML config → Register-all → Boot-all → plugin command
|
||||
- [ ] Built-in watch rebuild and restart loop
|
||||
- [ ] Local run command: `cd examples/hello && summer build && ./bin/hello greeter:hello`
|
||||
|
||||
## Phase Boundary
|
||||
|
||||
The generic Walking Skeleton recipe calls for routing, a DB read/write and a UI interaction. Phase 1's locked CONTEXT.md explicitly excludes `surf`, `lagoon`, `bouncer` and UI until Phase 3. This skeleton proves the complete **kernel developer path** and records the later app slice as its handoff. No substitute fake DB/UI is added to satisfy a generic template.
|
||||
|
||||
## Subsequent Slice Plan
|
||||
|
||||
- Phase 2 builds the API parity harness independently.
|
||||
- Phase 3 adds the first real Postgres-backed Płytarium endpoint and exercises the framework from the separate app repository.
|
||||
@@ -16,14 +16,14 @@ created: 2026-09-16
|
||||
| Framework | `go test` (Go 1.27.0) |
|
||||
| Config file | No test harness yet; first implementation slice adds Go tests |
|
||||
| Quick run | `go vet ./... && go test ./...` from repo root, then from `examples/hello` once created |
|
||||
| Full suite | Both modules: `go vet ./...`, `go test ./...`, `go test -race ./...`; generated binary integration |
|
||||
| Full suite | Framework, hello app and three hello plugin modules: `go vet ./...`, `go test ./...`, `go test -race ./...`; generated binary integration |
|
||||
| Estimated runtime | Measure after dependencies are cached; no assumed threshold |
|
||||
|
||||
## Sampling Rate
|
||||
|
||||
- After each implementation task commit, run root `go vet ./... && go test ./...`; run the same from `examples/hello` after it exists.
|
||||
- After each plan wave, run both module checks plus the relevant built-binary smoke.
|
||||
- Before verify-work, run race tests in both modules and an end-to-end `summer build`/hello invocation.
|
||||
- Before verify-work, run race tests in all five modules and an end-to-end `summer build`/hello invocation.
|
||||
- Record actual feedback latency and single-plugin rebuild latency; no fixed performance promise has been set.
|
||||
|
||||
## Per-Requirement Verification Map
|
||||
@@ -41,6 +41,21 @@ created: 2026-09-16
|
||||
| KERN-09 | integration | Temp app source edit triggers one debounced rebuild and restart, latency line captured | Pending |
|
||||
| CLI-01 | unit + integration | Plugin command discovery, injected output, non-TTY spinner/progress/table/prompt behavior | Pending |
|
||||
|
||||
## Per-Task Verification Map
|
||||
|
||||
| Task ID | Plan | Wave | Requirements | Automated check | Status |
|
||||
|---|---|---|---|---|---|
|
||||
| 01-01-01 | 01 | 1 | KERN-01/02/03/04, CLI-01 | Root and hello `go vet ./...` + `go test ./...` | Pending |
|
||||
| 01-01-02 | 01 | 1 | KERN-04, CLI-01 | Same checks plus `summer build` and built hello command | Pending |
|
||||
| 01-02-01 | 02 | 2 | KERN-01/03 | `go test ./compass ./pact`; hello tests | Pending |
|
||||
| 01-02-02 | 02 | 2 | KERN-02/03/05/08 | `go test ./party ./backpack ./pact`; hello tests | Pending |
|
||||
| 01-02-03 | 02 | 2 | KERN-06/07 | `go test ./festival ./towel ./backpack`; hello tests | Pending |
|
||||
| 01-03-01 | 03 | 3 | KERN-04 | `go test ./internal/build ./cmd/summer`; hello tests | Pending |
|
||||
| 01-03-02 | 03 | 3 | CLI-01 | `go test ./bonfire ./cmd/summer`; hello tests | Pending |
|
||||
| 01-03-03 | 03 | 3 | KERN-09 | `go test ./internal/dev ./internal/build ./cmd/summer`; hello tests | Pending |
|
||||
| 01-04-01 | 04 | 4 | KERN-01/02/03/05/06/07/08 | `go test -race ./compass ./party ./backpack ./festival ./towel ./pact` | Pending |
|
||||
| 01-04-02 | 04 | 4 | KERN-04/09, CLI-01 | `bash scripts/check-phase1.sh` | Pending |
|
||||
|
||||
## Wave 0 Requirements
|
||||
|
||||
- [ ] First implementation slice creates `examples/hello`, its `go.mod`, and a root `go.work` that names every example module.
|
||||
@@ -58,8 +73,8 @@ created: 2026-09-16
|
||||
|
||||
- [ ] Every plan task has an automated verification or an explicit Wave 0 dependency.
|
||||
- [ ] No three consecutive implementation tasks lack automated feedback.
|
||||
- [ ] Root and nested example modules both run `go vet`, tests and race tests.
|
||||
- [ ] Root, hello app and three plugin modules run `go vet`, tests and race tests.
|
||||
- [ ] No watch-mode flags in CI commands.
|
||||
- [ ] Set `nyquist_compliant: true` after plan-task mapping is complete.
|
||||
- [ ] Set `nyquist_compliant: true` after all mapped checks exist and pass.
|
||||
|
||||
**Approval:** pending plan verification
|
||||
|
||||
Reference in New Issue
Block a user