--- phase: 01-framework-kernel-foundation verified: 2026-09-16T12:37:13Z status: passed score: 5/5 must-haves verified overrides_applied: 0 human_verification: - test: "Run `./bin/hello greeter:hello` and one tool command (`summer make:plugin --help` or a spinner-using command) in a real TTY." expected: "Braille spinner / box-drawing table / colored status glyphs appear; prompts are interactive. NO_COLOR=1 or TERM=dumb removes ANSI." why_human: "TTY glyph, color, and prompt appearance vary by emulator and cannot be asserted from piped tests. VALIDATION.md lists this as the remaining manual CLI-01 check." - test: "In `examples/hello`, run `go run ../../cmd/summer dev`, edit `plugins/greeter/plugin.go`, and watch the terminal." expected: "A `rebuild: ` line prints, the child restarts, and generated `main.go` / `plugins.gen.go` writes do not loop." why_human: "Automated fsnotify tests prove rebuild/restart; perceived watch-loop feel still needs a human session (VALIDATION.md KERN-09 manual check)." --- # Phase 1: Framework kernel foundation Verification Report **Phase Goal:** The `summer` binary boots from layered YAML config, plugins self-register through one required interface plus optional capability interfaces, a typed event bus and service registry are available, and a CLI command framework with a dev watch loop exists — built only as far as the first vertical slice will need it, per the interleaved-not-sequential kernel approach. **Verified:** 2026-09-16T12:37:13Z **Status:** passed **Re-verification:** No — initial verification **MVP note:** ROADMAP marks this phase `mode: mvp`, but the phase goal is not a User Story (`gsd-sdk query user-story.validate` → `valid: false`). Direct instruction was to verify and write this report, so verification used the five ROADMAP success criteria as the contract and treated the developer as the user for flow coverage. Individual plan objectives are already in User Story form. ## User Flow Coverage User story (derived from plan objectives, because the ROADMAP goal is not a User Story): *As a framework developer, I want to build a tiny compiled-plugin app, change layered config, compose an optional plugin, dispatch typed events, scaffold a plugin, and watch-rebuild, so the first kernel is proven by a real binary.* | Step | Expected | Evidence | Status | |------|----------|----------|--------| | Build | `summer build` in `examples/hello` regenerates `plugins.gen.go` / `main.go` and writes `bin/hello` | `internal/build/build.go` `App()` → `generate()` + `go build -o bin/`; live run printed `built hello in 100ms` | ✓ | | Boot + command | `./bin/hello greeter:hello` prints config, optional extra, and event results | Live: `name=hello-app posts_per_page=10 debug=false extra=hello-from-optional events=ok collected=greeter handled=true` | ✓ | | Overlay | `SUMMER_ENV=development` changes `app.name` / `app.debug` | Live: `name=hello-dev … debug=true` from `config/env/development/app.yaml` | ✓ | | Env overlay | `SUMMER_GOLEM15__HELLO__POSTS_PER_PAGE=25` wins over plugin default | Live: `posts_per_page=25` | ✓ | | Optional skip | Greeter runs without `golem15.optional` and does not import it | `TestGreeterRunsWithoutOptionalPlugin`; `TestGreeterSourceDoesNotImportOptional` | ✓ | | Scaffold | `summer make:plugin` / `plugin:add` / `build` | `cmd/summer/main.go` wires `build.MakePlugin` / `AddPlugin`; `TestMakeAndAddPluginOnHelloCopy` | ✓ | | Watch | Source edit rebuilds and restarts with `rebuild: ` | `TestWatchHelloWorkspaceRebuildLatency` calls real `build.App` after a greeter edit | ✓ | | Outcome | Kernel boots, plugins compose, CLI and watch loop exist | All five ROADMAP success criteria verified below | ✓ | ## Goal Achievement ### Observable Truths | # | Truth | Status | Evidence | | --- | --- | --- | --- | | 1 | `summer build` regenerates the blank-import plugin list and produces a binary that boots from layered YAML config (base files, env overlay directory, per-plugin namespace, environment variables) with dot-path access | ✓ VERIFIED | `generatePluginsGen` writes `_ "module"` imports in `summer.yaml` order plus `PluginIDs`. Generated `examples/hello/main.go` loads `compass.Load("config")` then `party.Activate`. Live binary: base `hello-app`/`posts_per_page=10`, development overlay `hello-dev`/`debug=true`, env `SUMMER_GOLEM15__HELLO__POSTS_PER_PAGE=25`. Compass layers: plugin FS `MergeAt` at plugin ID, sorted `config/*.yaml`, `config/env//*.yaml`, `SUMMER_` split on `__`, `overrides.yaml`, runtime `Set`. `TestPrecedenceIsolatesEachLayer` covers all six. | | 2 | A throwaway plugin registered via the Plugin interface has its Register phase run for all plugins before any Boot phase runs, in `Requires()`-topological order, verified by a test with reordered input | ✓ VERIFIED | `party.Plugin` has `ID/Requires/Register/Boot`. `activate` topo-sorts then Register-all then Boot-all. `TestActivateRunsAllRegisterBeforeAnyBoot` and `TestActivateReorderedManifestStillTopoSorts` feed greeter-before-hello / extra-hello-greeter and assert `hello:register, greeter:register, extra:register` then boots. Missing/cycle errors name IDs and skip Boot. | | 3 | A plugin can type-assert an optional capability interface (e.g. `HasModels`) and skip integration with another plugin that isn't registered, with no hard import | ✓ VERIFIED | Phase 1 implements the pattern with `pact.HasConfig` / `pact.HasCommands` (type-asserted in `party.Activate` and generated `main.go`) plus `HasPlugin` + `Lookup[pact.OptionalMessage]`. Future names including `HasModels` are documented in `pact/capabilities.go` without payloads (Plan 02: do not couple to absent Phase 3 packages). Greeter Boot: `if app.HasPlugin("golem15.optional") { Lookup[pact.OptionalMessage] }`. Greeter source has no `plugins/optional` import. `TestGreeterRunsWithoutOptionalPlugin` prints `extra=` empty. | | 4 | The typed event bus supports fire-and-forget, fire-and-collect, and fire-until-handled dispatch, exercised by unit tests; per-request state travels only through `context.Context`, never a package-level global | ✓ VERIFIED | `festival.Bus` `Fire` / `Collect` / `UntilHandled` with priority + stable ties, panic recovery naming owner ID, app-isolated buses. Tests: `TestFireRunsAllListenersAndJoinsErrors`, `TestCollectMergesPayloadsLaterWinsAndKeepsPartialOnError`, `TestUntilHandledStopsOnFirstHandled`, `TestPriorityDescendingStableTies`, `TestPanicRecoveredNamesOwnerPlugin`. `towel` uses unexported key types; `TestNoPackageGlobalRequestState` scans the package. Hello command fires all three modes (`events=ok collected=greeter handled=true`). | | 5 | The `summer` CLI discovers a registered command and renders rich output (spinner, progress, table, prompts) with non-TTY degradation, and a dev watch loop rebuilds and restarts the binary on source change | ✓ VERIFIED | Tool commands `build`, `make:plugin`, `plugin:add`, `dev` via shared `bonfire.NewRoot`. App discovers `greeter:hello` through `pact.HasCommands`. Non-TTY: spinner `[...] message`, progress `[N/M] pct%` at 10% steps, TSV table, confirm uses default. Live `greeter:hello` printed TSV table. `TestWatchHelloWorkspaceRebuildLatency` copies hello, calls real `build.App`, asserts `rebuild:` after greeter edit, generated files do not loop. `Watch()` defaults `opts.Build = build.App`. | **Score:** 5/5 truths verified Plan-specific must-haves that restate the SCs (Go 1.27 toolchain, shared `bonfire` root, `make:plugin`/`plugin:add`, `scripts/check-phase1.sh`) were checked as supporting evidence, not extra score rows. `go.work` / every example `go.mod` pin `toolchain go1.27.0`. `bash scripts/check-phase1.sh` exited 0. ### Required Artifacts `gsd-sdk query verify.artifacts` on all four plans: 16/16 passed (exists, non-stub). | Artifact | Expected | Status | Details | | -------- | ----------- | ------ | ------- | | `internal/build/build.go` | Manifest-driven codegen + `go build` | ✓ VERIFIED | 163 lines; `App()` generates then `exec.CommandContext("go", "build", …)` with argv, not a shell string | | `internal/build/manifest.go` | Ordered `summer.yaml` parse/save | ✓ VERIFIED | Validates lowercase `vendor.plugin`, duplicate IDs/modules, module-path charset | | `internal/build/scaffold.go` | `make:plugin` / `plugin:add` | ✓ VERIFIED | 476 lines; scaffolds `go.mod`+`plugin.go`+`config/`, `go work use`, require/replace | | `party/registry.go` | Plugin interface + Activate | ✓ VERIFIED | 209 lines; topo sort, HasConfig merge before Register | | `backpack/app.go` | App container, HasPlugin | ✓ VERIFIED | Owns Config/Services/Events; no `party` import (`TestDoesNotImportParty`) | | `backpack/services.go` | Typed Publish/Lookup | ✓ VERIFIED | `reflect.TypeFor[T]()` registry, duplicate rejected, app-scoped | | `pact/capabilities.go` | Optional interfaces | ✓ VERIFIED | HasCommands, HasConfig, OptionalMessage; future families documented | | `compass/config.go` | Layered config + dot-path | ✓ VERIFIED | 358 lines plus `env.go`/`persist.go`; Lookup/String/Int/Bool/Has/LoadSection | | `festival/bus.go` | Typed dispatch | ✓ VERIFIED | Listen/Fire/Collect/UntilHandled, panic recover | | `towel/context.go` | Request context accessors | ✓ VERIFIED | Actor/Organization/Collection/Locale on unexported keys | | `bonfire/root.go` | Shared cobra constructor | ✓ VERIFIED | `NewRoot`/`NewRootIO` inject Output; used by tool and generated app | | `bonfire/widgets.go` | Spinner/progress/table | ✓ VERIFIED | TTY braille/box; non-TTY fallbacks match plan shapes | | `bonfire/prompts.go` | Ask/confirm/choice/secret | ✓ VERIFIED | Implemented; sequential piped lines are buggy (see Anti-Patterns) | | `internal/dev/watch.go` | Watch rebuild loop | ✓ VERIFIED | fsnotify, 200ms debounce, ignore bin/tmp/generated, `rebuild:` line | | `cmd/summer/main.go` | Tool entry | ✓ VERIFIED | `bonfire.NewRoot("summer", toolCommands(), …)`; no hello plugin imports | | `examples/hello/summer.yaml` | Ordered plugin manifest | ✓ VERIFIED | hello, greeter, optional in order | | `examples/hello/plugins.gen.go` | Blank imports + PluginIDs | ✓ VERIFIED | Three `_` imports matching manifest | | `scripts/check-phase1.sh` | Phase-wide check | ✓ VERIFIED | vet/test/race in root, hello, base, greeter, optional + built binary | | `compass/config_test.go` | Precedence tests | ✓ VERIFIED | Six-layer table, malformed YAML, snake_case env, Persist/Reload | | `festival/bus_test.go` | Dispatch tests | ✓ VERIFIED | All three modes, panics, isolation, concurrency | | `examples/hello/hello_test.go` | Built-app integration | ✓ VERIFIED | Layered config, optional skip, stable codegen, unknown command | ### Key Link Verification `gsd-sdk query verify.key-links` reported 0/11 verified. That checker greps target *filenames* inside source; Go imports package paths, so every link is a false negative. Manual wiring: | From | To | Via | Status | Details | | ---- | --- | --- | ------ | ------- | | `cmd/summer/main.go` | `bonfire/root.go` | Shared root constructor | WIRED | `bonfire.NewRoot("summer", toolCommands(), os.Stdout)` | | `examples/hello/main.go` | `party/registry.go` | `party.Activate(app, PluginIDs)` | WIRED | Generated `run()` line 30 | | `examples/hello/plugins.gen.go` | greeter `plugin.go` | Blank import `init()` | WIRED | `_ "…/plugins/greeter"`; `init() { party.Register(&Plugin{}) }` | | `party/registry.go` | `compass/config.go` | HasConfig before Register | WIRED | `p.(pact.HasConfig)` then `app.Config.MergePlugin` before Register loop | | greeter `plugin.go` | `backpack/services.go` | Typed lookup in Boot | WIRED | `app.Lookup[pact.OptionalMessage]()`; Publish lives on `App` | | `backpack/app.go` | `festival/bus.go` | App-owned bus | WIRED | `Events: festival.New()` | | `cmd/summer/main.go` | `internal/build/scaffold.go` | `make:plugin` / `plugin:add` | WIRED | `build.MakePlugin` / `build.AddPlugin` | | `internal/dev/watch.go` | `internal/build/build.go` | Shared `build.App` | WIRED | `opts.Build = build.App` when unset | | `bonfire/root.go` | `bonfire/output.go` | Output injection | WIRED | `NewOutput` passed into `wrap` → `c.Run(..., out)` | | `scripts/check-phase1.sh` | hello module | Nested module checks | WIRED | `run_module "hello app" "examples/hello"` plus plugin dirs | | `examples/hello/hello_test.go` | `cmd/summer` | `go run ../../cmd/summer build` | WIRED | `runSummerBuild` then `./bin/hello greeter:hello` | ### Data-Flow Trace (Level 4) | Artifact | Data Variable | Source | Produces Real Data | Status | | -------- | ------------- | ------ | ------------------ | ------ | | hello `greeter:hello` | `name`, `posts_per_page`, `debug` | `compass.Config` from `config/app.yaml` + plugin embed + env overlay + `SUMMER_` | Yes — live binary printed `hello-app` / `10` / overlay `hello-dev` / env `25` | ✓ FLOWING | | hello `greeter:hello` | `extra` | `HasPlugin("golem15.optional")` + `Lookup[pact.OptionalMessage]` published in optional `Register` | Yes — `hello-from-optional` when present, empty when omitted | ✓ FLOWING | | hello `greeter:hello` | `events` / `collected` / `handled` | `app.Events.Fire/Collect/UntilHandled` on `*HelloEvent` | Yes — `ok` / `greeter` / `true` | ✓ FLOWING | | `plugins.gen.go` | `PluginIDs` | `summer.yaml` plugins list | Yes — `golem15.hello`, `golem15.greeter`, `golem15.optional` | ✓ FLOWING | | generated `main.go` | `commands` | `plugin.(pact.HasCommands).Commands()` after Activate | Yes — `greeter:hello` is executable on `bin/hello` | ✓ FLOWING | ### Behavioral Spot-Checks | Behavior | Command | Result | Status | | -------- | ------- | ------ | ------ | | Tool discovers `make:plugin` | `go run ./cmd/summer make:plugin --help` | Usage `summer make:plugin ` | ✓ PASS | | Tool discovers `build` / `dev` | `go run ./cmd/summer build --help` / `dev --help` | Help text for generate-and-build / watch | ✓ PASS | | Built hello command | `examples/hello/bin/hello greeter:hello` | Config + optional extra + events line | ✓ PASS | | Env overlay | `SUMMER_ENV=development ./bin/hello greeter:hello` | `name=hello-dev debug=true` | ✓ PASS | | Env var override | `SUMMER_GOLEM15__HELLO__POSTS_PER_PAGE=25 ./bin/hello greeter:hello` | `posts_per_page=25` | ✓ PASS | | Unknown command | `./bin/hello does:not-exist` | non-zero, `unknown command` | ✓ PASS | | Tool isolation | `go list -deps ./cmd/summer` | no hello/greeter/optional packages | ✓ PASS | | Sequential piped Ask | two `Ask` on `"alice\nbob\n"` | first=`alice`, second=default `d2` | ℹ️ reproduced (see Anti-Patterns) | | Phase check | `bash scripts/check-phase1.sh` | exit 0, `phase1 check passed` | ✓ PASS | ### Probe Execution No `scripts/*/tests/probe-*.sh` files exist. Phase-declared verification is `scripts/check-phase1.sh` (not a probe). Ran it from repo root: **PASS** (exit 0). Named modules: root, hello app, base, greeter, optional. Built-binary integration printed `built hello in 100ms` and the greeter greeting. ### Requirements Coverage All PLAN `requirements:` IDs for this phase: KERN-01 … KERN-09, CLI-01. REQUIREMENTS.md maps the same ten IDs to Phase 1. No orphaned Phase 1 IDs. CLI-02+ are later phases. | Requirement | Source Plan | Description | Status | Evidence | | ----------- | ---------- | ----------- | ------ | -------- | | KERN-01 | 01, 02, 04 | Layered YAML config, dot-path, typed section | ✓ SATISFIED | compass Open/MergePlugin/Set/Persist/Reload; hello prints overlay + env | | KERN-02 | 01, 02, 04 | Plugin interface; all Register before any Boot; Requires topo | ✓ SATISFIED | `party.Plugin` + `TestActivateReorderedManifestStillTopoSorts` | | KERN-03 | 01, 02, 04 | Optional capability interfaces by type assertion | ✓ SATISFIED | HasConfig/HasCommands asserted; remaining families documented for later phases | | KERN-04 | 01, 03, 04 | go.work modules, init register, `summer build`, `plugin:add` | ✓ SATISFIED | workspace 5 modules; codegen; MakePlugin/AddPlugin tests | | KERN-05 | 02, 04 | Skip optional plugin without hard import | ✓ SATISFIED | HasPlugin + greeter Lookup; no optional import | | KERN-06 | 02, 04 | Fire / Collect / UntilHandled | ✓ SATISFIED | festival tests + hello command | | KERN-07 | 02, 04 | Request state on context.Context only | ✓ SATISFIED | towel accessors; no package-global request vars | | KERN-08 | 02, 04 | backpack typed service registry | ✓ SATISFIED | Publish/Lookup; optional plugin publishes OptionalMessage | | KERN-09 | 03, 04 | Dev watch rebuild/restart | ✓ SATISFIED | `internal/dev/watch.go`; real hello copy rebuild test | | CLI-01 | 01, 03, 04 | Command discovery + rich output + non-TTY | ✓ SATISFIED | bonfire widgets/prompts tests; hello TSV table; TTY appearance still human | ### Anti-Patterns Found No `TBD` / `FIXME` / `XXX` debt markers in phase Go/sh/yaml. No stub `return null` handlers on the hello command path. | File | Line | Pattern | Severity | Impact | | ---- | ---- | ------- | -------- | ------ | | `bonfire/prompts.go` | 116-133 | New `bufio.Reader` per `readLine`; piped remainder discarded | ⚠️ Warning | Sequential Ask/Choice/Secret on a pipe: first line works, later prompts get EOF/default. Independently reproduced (`alice` then default `d2`, not `bob`). Single-prompt tests miss it. Hello uses Confirm, which short-circuits non-TTY, so the slice still works. | | `internal/dev/watch.go` | 86-92 | Start new child before `current.stop()` | ⚠️ Warning | Two processes overlap. Harmless for Phase 1 hello (CLI exits immediately) but will break a later `serve` bind. | | `cmd/summer/main.go` | 13-23 | No `signal.NotifyContext`; `startBin` ignores ctx and passes no args | ⚠️ Warning | Ctrl+C cleanup is accidental; `bin/hello` with no args prints cobra help and exits. Watch still rebuilds. Long-running serve is Phase 3. | | `bonfire/command.go` | 83-90 | Plugin commands may use kernel names `build`/`dev` | ℹ️ Info | Colon rule is skipped for those two names on every root, not only the tool. | | `bonfire/prompts.go` | 125 | `TrimSpace` on Secret answers | ℹ️ Info | Leading/trailing password spaces stripped. | These do not fail a ROADMAP success criterion for this slice. They are quality issues for later CLI/serve reuse. ### Human Verification Required ### 1. Interactive terminal rendering **Test:** Run `./bin/hello greeter:hello` (and optionally a spinner-using command) in a real TTY, then again with `NO_COLOR=1` or `TERM=dumb`. **Expected:** Box-drawing table and colored glyphs on TTY; TSV / no ANSI when color is disabled; Confirm prompt appears when stdin is a TTY. **Why human:** Terminal appearance is emulator-specific. Automated suite covers non-TTY shapes only. ### 2. Rebuild feel under `summer dev` **Test:** From `examples/hello`, `go run ../../cmd/summer dev`, edit `plugins/greeter/plugin.go`, observe the log. **Expected:** `rebuild: ` then a child restart; editing generated `main.go` does not loop. **Why human:** Tests prove the mechanism; perceived latency/feel is listed as manual in VALIDATION.md. ### Gaps Summary No blocking gaps. All five ROADMAP success criteria hold in the codebase and were exercised by `bash scripts/check-phase1.sh` plus live `bin/hello` invocations. Remaining work is human inspection of TTY output and watch-loop feel, plus optional follow-ups (sequential piped prompts, stop-then-start child, signal context) that later long-running serve/CLI phases will feel. --- _Verified: 2026-09-16T12:37:13Z_ _Verifier: Claude (gsd-verifier)_