docs(01-03): complete plugin scaffold CLI and watch-loop plan

- Record make:plugin, bonfire output and summer dev results
This commit is contained in:
Jakub Zych
2026-09-16 14:01:34 +02:00
parent 270b7f1e87
commit 9b2b035b68

View File

@@ -0,0 +1,164 @@
---
phase: 01-framework-kernel-foundation
plan: 03
subsystem: kernel
tags: [go, cobra, fsnotify, cli, scaffolding, watch]
requires:
- phase: 01-framework-kernel-foundation
provides: Manifest-driven summer build, shared bonfire.NewRoot, hello app workspace
provides:
- make:plugin and plugin:add scaffolding against summer.yaml
- Deterministic bonfire Output widgets and prompts with non-TTY fallback
- Built-in summer dev watch/rebuild/restart loop
affects: [01-04, 04]
tech-stack:
added:
- golang.org/x/term v0.46.0
- github.com/fsnotify/fsnotify v1.10.1
patterns:
- summer.yaml is the sole ordered plugin list; scaffold then plugin:add then build
- One injected bonfire.Output decides TTY/color once per process
- summer dev calls the same build.App used by summer build
key-files:
created:
- internal/build/manifest.go
- internal/build/scaffold.go
- bonfire/output.go
- bonfire/widgets.go
- bonfire/prompts.go
- internal/dev/watch.go
modified:
- internal/build/build.go
- cmd/summer/main.go
- bonfire/root.go
- bonfire/command.go
- examples/hello/plugins/greeter/plugin.go
- go.mod
- go.sum
key-decisions:
- "plugin:add is idempotent on the same id+module and rejects conflicting ids"
- "Child go commands override inherited GOWORK so summer build in a nested app does not use a parent workspace"
- "Confirm is silent on non-TTY and uses its default so tests never hang"
- "Watch ignores app-root main.go and plugins.gen.go to prevent generate loops"
patterns-established:
- "Pattern: make:plugin writes plugins/<name>/{go.mod,plugin.go,config/}; plugin:add wires manifest, go.work and go.mod replace"
- "Pattern: plugin command names are namespace:verb; kernel names build and dev are the only colon-less exceptions"
- "Pattern: summer dev prints rebuild: <duration> after each successful build.App and reaps the child on cancel"
requirements-completed: [KERN-04, KERN-09, CLI-01]
duration: 26min
completed: 2026-09-16
---
# Phase 1 Plan 3: Plugin Scaffold, CLI Output and Watch Loop Summary
**make:plugin / plugin:add wire a compiling module into summer.yaml, bonfire degrades spinner/progress/table/prompts without a TTY, and summer dev rebuilds through build.App with measured restart latency**
## Performance
- **Duration:** 26 min
- **Started:** 2026-09-16T11:33:58Z
- **Completed:** 2026-09-16T12:00:25Z
- **Tasks:** 3
- **Files modified:** 21
## Accomplishments
- `summer make:plugin` scaffolds `plugins/<name>` with `go.mod` (`toolchain go1.27.0`), `plugin.go` and empty `config/`; `plugin:add` appends one ordered manifest entry, `go work use`, and a portable app `go.mod` require/replace
- Shared cobra adapter injects one `bonfire.Output` built from stdin/stdout/stderr; non-TTY spinner is `[...] message`, progress is `[N/M] pct%` at 10% steps, tables are TSV, and `NO_COLOR` / `TERM=dumb` disable ANSI
- `summer dev` watches sources, debounces, calls `build.App`, prints `rebuild: <duration>`, keeps the last child on build failure, and reaps it on cancel without looping on generated `main.go` / `plugins.gen.go`
## Task Commits
Each task was committed atomically:
1. **Task 1: Add and rebuild a compiling plugin from the tool** - `9b1a25d` (feat)
2. **Task 2: Give tool and plugin commands deterministic rich output** - `a6004e4` (feat)
3. **Task 3: Rebuild and restart the hello binary during source edits** - `270b7f1` (feat)
**Plan metadata:** pending (docs: complete plan)
## Files Created/Modified
- `internal/build/manifest.go` - Load/Save summer.yaml, lowercase vendor.plugin IDs
- `internal/build/scaffold.go` - MakePlugin / AddPlugin with path containment checks
- `internal/build/build.go` - generate-and-compile plus GOWORK isolation for child `go` commands
- `cmd/summer/main.go` - `make:plugin`, `plugin:add`, `build`, `dev` tool commands
- `bonfire/output.go` / `widgets.go` / `prompts.go` - injected Output, spinner, progress, table, prompts
- `bonfire/root.go` / `command.go` - cobra adapter, named `ErrCommandName` for plugin names without `:`
- `internal/dev/watch.go` - fsnotify watch loop with debounce, serialize, restart, reap
- `examples/hello/plugins/greeter/plugin.go` - table plus confirm on `greeter:hello` without hanging on closed stdin
## Decisions Made
- Keep generated plugin paths under `plugins/<name>` derived from the ID's plugin segment
- Treat `build` and `dev` as the only kernel command names allowed without a colon
- Detect widgets from stdout TTY and prompts from stdin+stdout TTY so piped tests use defaults
- `secret` uses `term.ReadPassword` on a TTY file and a plain stdin line otherwise; answers are never written back
- Watch production path always calls `build.App`; tests inject Build/Start so debounce and ignore behavior stay fast
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 3 - Blocking] Child go commands inherited test GOFLAGS/GOWORK**
- **Found during:** Task 1 (plugin compile on a temp hello copy)
- **Issue:** `go test` sets `-mod=readonly` and a parent workspace; `go build` of a temp app then targeted the wrong module
- **Fix:** Strip inherited `GOWORK`/`GOFLAGS` and set `GOWORK` to the nearest app `go.work` (or `off`)
- **Files modified:** `internal/build/scaffold.go`, `internal/build/build.go`
- **Verification:** `go test ./internal/build`
- **Committed in:** `9b1a25d` (Task 1)
**2. [Rule 3 - Blocking] NewRoot now returns a named command-name error**
- **Found during:** Task 2
- **Issue:** Plugin names without `namespace:verb` must fail registration; callers and generated `main.go` still treated NewRoot as a single return
- **Fix:** `NewRoot`/`NewRootIO` return `(*cobra.Command, error)`; generateMain and hello callers updated
- **Files modified:** `bonfire/root.go`, `internal/build/build.go`, `examples/hello/main.go`, `examples/hello/hello_test.go`, `cmd/summer/main.go`
- **Verification:** `go test ./bonfire ./cmd/summer` and hello tests
- **Committed in:** `a6004e4` (Task 2)
**3. [Rule 3 - Blocking] Greeter test Output stub no longer satisfied the interface**
- **Found during:** Task 2
- **Issue:** Expanding Output broke `examples/hello/plugins/greeter/plugin_test.go`'s three-method stub
- **Fix:** Run the command against `bonfire.NewOutput` with injected streams
- **Files modified:** `examples/hello/plugins/greeter/plugin_test.go`
- **Verification:** `go test` in the greeter module
- **Committed in:** `a6004e4` (Task 2)
---
**Total deviations:** 3 auto-fixed (3 blocking)
**Impact on plan:** Required for temp-app builds, named registration errors, and a compiling greeter test. No scope creep. Choice prompts use a numbered list rather than TTY arrow keys; non-TTY behavior matches the plan.
## Issues Encountered
- `go mod tidy` in a workspace still drops `toolchain go1.27.0` unless it is rewritten; Task 1 restores it after plugin tidy (same D-05 pattern as Plans 01 and 02).
## Authentication Gates
None.
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
Ready for `01-04-PLAN.md` (phase unit-test coverage). Scaffolding, rich CLI output and `summer dev` exist on the hello path. Do not add HTTP/DB/auth here.
## Self-Check: PASSED
- Created scaffold, output, watch, and SUMMARY files exist on disk
- Commits `9b1a25d`, `a6004e4` and `270b7f1` exist
- Root and hello `go vet ./...` and `go test ./...` passed
- STATE.md and ROADMAP.md were not updated in this worktree
---
*Phase: 01-framework-kernel-foundation*
*Completed: 2026-09-16*