Files
summercms/.planning/phases/01-framework-kernel-foundation/01-VERIFICATION.md
2026-09-16 14:46:25 +02:00

19 KiB
Raw Blame History

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
test expected why_human
Run `./bin/hello greeter:hello` and one tool command (`summer make:plugin --help` or a spinner-using command) in a real TTY. Braille spinner / box-drawing table / colored status glyphs appear; prompts are interactive. NO_COLOR=1 or TERM=dumb removes ANSI. 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 expected why_human
In `examples/hello`, run `go run ../../cmd/summer dev`, edit `plugins/greeter/plugin.go`, and watch the terminal. A `rebuild: <duration>` line prints, the child restarts, and generated `main.go` / `plugins.gen.go` writes do not loop. 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/<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

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)