19 KiB
phase, verified, status, score, overrides_applied, human_verification
| phase | verified | status | score | overrides_applied | human_verification | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-framework-kernel-foundation | 2026-09-16T12:37:13Z | passed | 5/5 must-haves verified | 0 |
|
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/<binary>; 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: <duration> |
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/<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 <id> |
✓ 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: <duration> 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)