make:*, plugin:add and new inserted toolchain go1.27.0 into go.mod and
go.work whenever that exact line was missing. A file that already named
another toolchain (go mod tidy and go work use write the running one)
ended up with two toolchain lines, and Go refused to load it with
"repeated toolchain statement". ensureToolchain now keeps an existing
line, replaces it only when it names an older toolchain, and inserts
one only when there is none.
- make:admin-controller edits the singular of its name (--model overrides):
ModelName, modelClass and the YAML under models/<model>/
- NewRecord returns &models.<Model>{} when the model exists; otherwise the
nil body stays and the command says to run make:model
- config_list.yaml offers create and delete with showCheckboxes
- registry.gen.go embeds controllers/*/*.yaml and models/*/*.yaml once they
exist and exposes generatedAdminFS(); scaffolded plugins implement
pact.AdminAssets through it, and a plugin without AdminFS gets a note
- blog example moves its YAML to models/post; docs and help text follow
- model, migration, command, job and admin-controller stubs start with
//summer:make <kind>; registry.gen.go keeps its generated-code header
- the registry scan accepts the directive and the older header
- command functions may take one *backpack.App; generatedCommands takes a
variadic app, so plugin.go files calling generatedCommands() still compile
- scaffolded plugins keep the app from Boot and return generatedCommands(p.app)
- make:command notes the nil app for an older plugin.go
- docs, help text and the blog example follow
- make:plugin writes plugins/<vendor>/<name> with module
<app>/plugins/<vendor>/<name>; the package name stays <name>
- make:* and the models-leaf check look in summer.yaml, then
plugins/<vendor>/<name>, then the older plugins/<name>
- summer new puts its starter plugin at plugins/golem15/<name>
- refuse a vendor directory that is a legacy plugin module and a second
copy of a plugin that already sits in plugins/<name>
- help text and docs, with an upgrading note: moving is optional
- nextMigrationFile starts at one second past the newest updates/ timestamp
when that is not earlier than now, so registry order is creation order
- frozen-clock unit test and a registry-order check in the scaffold test
- docs: drop the same-second caveat and the manual order check
- build runs go build under a spinner on a terminal and ends with
'Built bin/<name> in <time>'; make:* print 'Created <path>' per file
- make:*, plugin:add, new, parity:*, docs:build, docs:sync and every
delegate get long help and examples; delegates note that global options
pass through to the application binary
- make:* failures (no summer.yaml, invalid plugin ID or name, existing
artifact, unknown plugin) explain themselves and suggest a command
- docs:build and docs:sync return bonfire.Reported after the problem list
- TestCommandsDocumented covers the tool commands too
- docs: Upgrading to 0.4, delegates and global options, make: results and
hints, the checklist for a finished command
- Global options -v/-vv/-vvv, -q, --ansi/--no-ansi, -n and --env on every
command; PeekGlobals reads them before config loads, OnGlobals hooks
re-apply them after parsing (diary.ApplyGlobals in the generated main)
- Bare binary, --help and list print the Artisan list with the version
header from BuildVersion and namespace blocks
- Execute runs the root through fang (no version, man page or completions)
and returns 0, 1 or 2; Fail renders errors before the root exists
- NewRoot rejects duplicate command names (ErrDuplicateCommand) and flags
that reuse a global option (ErrReservedFlag)
- Generated main and summer use PeekGlobals, compass.Open with --env,
diary.FromGlobals and bonfire.Execute; docs checker knows the globals
- stderr and null drivers; per-component levels from log.components
- SUMMER_LOG__ overrides parse as strings and overlay the defaults key by key
- Validation names the key: driver, format, level, days, stack children and cycles, daily path outside the root
- summer new writes config/log.yaml (stack of console and daily, river warn, http info)
- docs/services/logging.md: channels, config/log.yaml, component loggers, daily files, redaction
- Log keys in the configuration docs; ExampleFor backs the docs snippet
- New diary module: For, Bootstrap, Setup, SetVerbosity, Reset, Close, ParseConfig
- Proxy component handler safe in package vars; styled console handler in bonfire's palette
- Daily writer: one file per day, 0640, lazy open, retention, disables itself on open failure
- Every channel leaf wrapped by sunscreen.Wrap; stack via slog.NewMultiHandler
- bonfire: ColorMode, Palette, DefaultPalette, DetectProfile (lipgloss v2, colorprofile)
- Generated main runs diary.Bootstrap first and diary.Setup after compass.Load
- Ignore /storage/ and /examples/hello/storage/
- manifest_tags_test.go: build.tags load, trim, dedupe and validation
errors naming build.tags; SaveManifest keeps tags and plugin order;
goBuildArgs puts one -tags before -o
- s3_test.go: s3:// refused without summercms_s3 naming the tag, opens
with it (both build modes, any scheme case)
- diskext_test.go: RESEARCH probe rows and table consistency
- url_test.go: public_url base joining, File.URL and thumbnails,
malformed values refused naming the key
- store_test.go: Postgres regression table through Store
- gate: attach names in both tag modes, Store regression in docker
- Manifest.Tags from build.tags (^[a-z0-9_]+$, duplicates dropped), round-tripped by SaveManifest
- summer build passes -tags=<list> through goBuildArgs and prints the build tags line
- attach: s3blob imported only by s3_on.go (//go:build summercms_s3); an untagged binary
refuses an s3:// bucket_url naming the tag, build.tags and -tags
- go mod tidy adds the AWS SDK v2 modules as indirect requirements
- storage, modules, scaffolding, configuration and scaling docs plus the lagoon README
- NewOptions.AdminSecret: summer new writes SUMMER_ADMIN__JWT__SECRET
to .env (still 0600), generated from 32 crypto/rand bytes when empty
and validated as at least 32 .env-safe characters
- .env.example gets an empty SUMMER_ADMIN__JWT__SECRET= placeholder
with a comment; the secret never reaches the printed next steps
- The next steps and the generated CLAUDE.md explain the admin at
/backend: make:admin-controller, build, migrate and
summer admin:create --email you@example.com --superuser
- Goldens use a fixed, non-secret admin secret fixture
- Scaffolding, installation and users-and-permissions docs updated
- .env (0600) with a fresh lagoon.GenerateAppKey key and the compose DSN;
.env.example without a key
- compose.yaml runs postgres:16 on 127.0.0.1:54320 with a healthcheck
- .gitignore, CLAUDE.md with AGENTS.md as a relative symlink
- git init on main and one commit, with a fallback identity only when git
has none; git failures are warnings; --no-git skips it
- print the next steps after creating the app
Claude-Session: https://claude.ai/code/session_01M8Qu9LgCKYy7iYEho52tW8
- resolve the framework from --framework, then a sibling summercms or
summercms.go checkout, then the binary's own source tree, each accepted
only when its go.mod declares the framework module
- refuse a missing parent and a target inside the framework checkout
- remove the half-written app when any step fails
- add --no-plugin for an app with no starter plugin
- build.NewApp writes sm-<name>-app: go.mod with a relative framework
replace, go.work, summer.yaml, config/ and a starter plugin
- make:plugin gains an opt-in starter route (GET /{$}) in the same
routes.go template; plain make:plugin output is unchanged
- plugin:add writes go.work use paths relative to the go.work directory
- bonfire accepts new as a bare tool command name
- Wrap redacts sensitive keys at any depth and scrubs Bearer, sk- and x-api-key shapes
- InstallDefault is the first statement of the generated run; hello main regenerated
- surf test pins that recovered panics echo no credential
- sunscreen README, root modules row and the logging docs page
- pact.HasSchedule with ScheduledCommand and Daily/DailyAt/Every cadences (no River import)
- bonfire.Call, Catalog and ErrUnknownCommand for in-process command runs
- conga Daily/Every wall-clock schedules in app.timezone, periodic jobs on every worker,
scheduled queue (MaxAttempts 1, unique by args within the cadence period)
- scheduled worker runs only entries matching the compiled table; unregistered
commands are skipped with a Warn log
- generated app main publishes bonfire.NewCatalog(commands); hello main regenerated
- Manager gains the apparatus JobManager surface: StartJob, UpdateJobState,
UpdateMetadata, FailJob, CancelJob (is_canceled + STOPPED + River JobCancel),
StopJob (STOPPED only), CheckIfCanceled and GetMetadata, all raw column
writes so updated_at is untouched
- serve starts the in-process worker unless queue.work_in_serve is false and
stops it on shutdown; an app without jobs gets an idle worker
- queue:work runs a foreground worker with repeatable --queue filters;
queue:clear deletes available, scheduled and retryable jobs of one queue
- the generated main appends conga.RuntimeCommands; summer delegates
queue:work and queue:clear; make:job scaffolds a conga.Job
- phrasebook ships the backend::lang admin strings (pl, en) with CLDR
plural maps, loads them as namespace backend, applies
pact.HasLangOverrides trees (lang/<locale>/<namespace>/<group>.yaml)
after every namespace, and fails activation when a backend key cannot
convert to plural forms
- Translator.Forms, Bundle, Resolved and Has serve keys as CLDR form maps
- Public GET /lang returns every backend::lang key for the request
locale over the fallback locale, Cache-Control no-cache
- config_list, config_form and config_relation accept a strict messages
block; omitted keys take framework defaults, schemas serve every message
as CLDR forms, and activation fails on a missing phrase key
- toolbar.buttons is an ordered [create, delete] list; the Winter string
form, duplicates, unknown actions and delete without showCheckboxes fail
at boot, and create is dropped when the controller has no form
- Form schema serves the raw Winter redirects; scaffold emits the list
syntax; form and relation schema routes are typed in the admin OpenAPI
- config_form.yaml and config_list.yaml point at models/<name>/fields.yaml and columns.yaml
- Duplicate model or controller assets fail before any new file is written
- make:admin-controller must emit config_form and config_list beside model fields and columns
- A pre-existing model asset must fail before any controller file is written
- GroupRaw plus sticky raw inheritance and registration-time house-envelope refusal via pact.HasHouseMiddleware
- Recover on raw routes writes a bare 500; non-raw keeps the house JSON body
- Router.Routes() and surf.RouteListCommand; generated main registers route:list
- Compile and vet every generated artifact in a copied hello workspace
- Reject malformed names, traversal, and duplicates through public Make*
- Cover CLI argument forms, --no-migration, and one-arg make inside a plugin
Co-authored-by: Cursor <cursoragent@cursor.com>
Scaffold pact.Job and pact.AdminController stubs with Winter YAML
assets, and fail summer build when models imports a sibling package.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add make:model, make:migration and make:command with plugin-id
inference, duplicate rejection, and a deterministic registry refresh
that leaves handwritten plugin.go untouched.
Co-authored-by: Cursor <cursoragent@cursor.com>
Render plugin.go, routes.go, leaf packages and go.mod from embedded
templates, emit empty registry accessors, and define job, admin, lang
and mail capability contracts so a new plugin compiles before artifacts.
Co-authored-by: Cursor <cursoragent@cursor.com>
Named middleware resolves at boot, HS256 tokens are pinned with required
exp/sub, and both binaries expose a signal-aware serve command.
Co-authored-by: Cursor <cursoragent@cursor.com>
Open one pgx stdlib *sql.DB, hand it to GORM, and run per-plugin
gormigrate sets with isolated history tables after an ICU pl-PL check.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Inject bonfire.Output from stdin/stdout/stderr with stdlib widgets and x/term
- Degrade spinner, progress, table and prompts without a TTY or color
- Reject plugin command names that are not namespace:verb
- Scaffold a compiling vendor.plugin module with go.mod, plugin.go and config/
- Register the module once in summer.yaml, go.work and the app go.mod
- Keep summer build on the same generate-and-compile path and print elapsed time
- App-scoped typed Publish/Lookup with duplicate-provider errors
- Set plugin IDs before Register so HasPlugin sees the full set
- Greeter uses pact.OptionalMessage without importing optional
- Add summer build with validated summer.yaml codegen
- Generate stable main.go and plugins.gen.go then go build
- Smoke-test the built hello binary greeter:hello command