- helpOnly also counts no command word, list, help, cobra's shell
completion requests and --help=<bool>, read the way pflag does
- TestHelpOnlyAgreesWithCobra pins the classification against bonfire's
real dispatch, with the passthrough --help case as the exception
- console and logging docs describe which invocations boot quietly
- Options.Args holds the command-line arguments; FromGlobals fills it
from os.Args[1:], so the generated main needs no change
- Bootstrap and Setup install the setup with Quiet when Args only ask
for help (-h, --help) and no -v is given; files keep their levels
- TestBootQuietSmoke runs the generated main's sequence with a plugin
that logs from Register and Boot
- users form partial on the Administrators tab of the role update form,
served by rolesAdmin.PartialData through scopeAdministrators (D-03)
- deleted and deactivated holders marked, superusers only for superuser
editors, first 100 rows with a count note, preview links for editors
holding backend.manage_users
- en/pl phrases, README and docs, TestRolesSmoke asserts the partial
- boardwalk.BuildID hashes the dist tree plus the binary's build info once
per handler; X-Summer-Build on every response, {prefix}/build.json and the
summer-admin-build meta carry it
- the SPA reads the booted build, re-checks on focus, visibility and every
5 minutes, and shows UpdateBanner on a different build
- the route's private inline list and its helper are gone; the
disposition writer asks attach.IsInlineContentType, the same
definition attach.Store uses for the disposition it stores
- headers are unchanged (TestUploadsResponseHeaders, TestUploadsMount)
- surf README and the storage page name the shared function
- attach.IsInlineContentType: the shared inline raster list (JPEG, PNG,
GIF, WebP, AVIF), parameters and case ignored, false for empty or
unparsable values
- writeBlob stores Content-Disposition: attachment for every other type,
so a public bucket or CDN makes the browser download HTML, SVG, PDF or
plain text; content_type and thumbnails are unchanged
- driver tests on memblob and fileblob, Postgres-backed Store assertion,
lagoon README and the storage and attachments pages
- runAction writes "cabana: admin action run" with controller, action,
admin_id and kind (toolbar or field; field name and record id for a
widget), never values, payload, answer or record contents
- the failure and data-rejected lines carry the same keys (admin_id, kind)
- actionAdminID helper shared with the bulk and record lines (unchanged)
- TestAdminActionRunLog pins the line on the acme.demo fixture (Postgres)
- README and the permissions docs describe the run log
- flushStatementAfterCommit marks the statement; AfterCommit logs a warning
when work is buffered on a flushed statement that no longer holds GORM's
transaction. Log-only: the work still never runs, the write succeeds
- subtests pin the late callback warning, a callback between commit and
flush, and a reused chained statement staying silent
- README OnDatabase example registers After(gorm:create).Before(commit),
matching docs/database/transactions.md
- RegisterJoinTable: pivot-ordered reads join the pivot table explicitly;
a Preload cannot be ordered by a pivot column
- TestPNGScriptChunkNaming (no Docker) and TestStoreScriptChunkPNG (Postgres)
pin the todo exploit: a decodable PNG with a script tEXt chunk uploaded in
image mode as x.html is stored and served as .png, bytes unchanged
- storage deploy note separates static servers (nosniff, nginx extension
allow-list, 404 otherwise) from public buckets, which send the stored
sniffed Content-Type; attachments.md points to it
- 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
- READMEs and service pages name the new pact and wristband identifiers
- an empty MCP scope list resolves to the five fixed names; OIDC discovery is unchanged
Hide removed an owner field from the schema, show and save, but a hidden
relation-manager field's relation routes (panel, link, unlink, child CRUD,
deferred commit) and a hidden partial field's partial stayed reachable.
dropHiddenFields now drops the relation and the partial path too; a partial
the list or preview header or another field still uses keeps rendering.
Admin saves copied the raw context locale into RecordSaved.Locale, which
could be a whole Accept-Language header, while reorder reported a
normalised code. Saves, child writes and reorder now report the primary tag
of the request locale. The pact doc says what Locale is and that per-locale
work keys on RecordChange.Locale, since an admin save writes the row in the
default content locale.
hasMany link and unlink rewrote a child's foreign key (and ClearOnChange
columns) without a RecordSaved, the deferred commit attached session-created
children silently, and unlinking a session-created child deleted it without
the RecordDeleted that should follow its RecordSaved{Created}. These paths
now fire the child's events after the commit; the CRUD service carries the
registry so the deferred commit names the child's controller.
WidgetField assigned its element after append(), so an action the element
dispatched from connectedCallback (inside append) found no target and was
dropped without a result or an error state; widgets that load on connect
never started. The element is now the action target before it is connected.
Rebuilt the embedded admin dist.
The plugin field-type upload route resolved a form extension's field without
checking the extension's Permissions and Visible, so an administrator who
cannot see the extension could still run its Upload. The route now answers
404 for them, like the extension's widget actions and partials.
A required: true field in a form extension became a rule on the owner's
model, where its value never exists, so every save of the host form failed,
also for administrators who cannot see the extension. mergedRules now skips
extension fields; liftExtensionValues checks required on the submitted value
for visible extensions only (create: must be sent and not blank; update:
only when sent).
- ContentType, ContentTypeForController, Records and MatchPath on the
registry; the pure MatchPattern for whole-segment placeholder matching
- two types declaring the same Content.Model or Controller stop the boot
- TestContent* cover lookups, optional Records, boot errors, MatchPattern
and MatchPath precedence (first exact, else first inexact)
- docs: Content descriptors in menu-item-types.md, signpost README
- Delete and each bulk-delete row fire RecordDeleted after commit
- tree reorder fires RecordSaved per moved record (parent/sort in model types)
- relation-manager child create/update/delete fire events naming the
controller that edits the child's model
- TestRecordEvents covers create, update, translated slug, no-op, rollback
(validation, FormBeforeUpdate, extension Save), failing listener, reorder,
child writes, delete and bulk delete; extension Save sees the changes
- docs: Record events in extending.md, admin-controllers notes, cabana README
- admin-controllers: placement, values, Load/Save, permissions,
widgets, partials and boot errors, with the ExtrasPlugin snippet
- Example_formExtension compiles an owner form with the extension
- coming-from-wintercms maps extendFormFields, addFields, addTabFields
and removeField to the form extension
- cabana README feature bullet and Activate row
- pact.AdminActionExtension.Form with AdminFormExtension: a fields
document (fields.yaml format plus before:/after:/zone: and a tabs:
list) given as an AdminFS path or YAML bytes, Load and Save hooks,
AdminFormLoad/Save/SaveResult, AdminFormErrors and zone constants
- cabana compileFormExtensions places extension fields at owner
anchors at boot, regrouping an extended form by tab
- save lifts extension values before any owner path and runs Save in
the record's transaction after the owner row; show merges Load
- TestFormExtensionTracer: c after a on a, b gives a, c, b through the
admin API, stored in the contributor's table
pact.MCPGrant gains BackendUserID, the backend account the consent page
showed. pier gives an OAuth identity a backend subject (on resolution and
after a link event) only while the user's link names that account; a link
to anyone else, or no recorded account, gives none, so a link changed after
the consent never re-targets the grant.
An action whose Applies failed was dropped with no signal, so a section
whose Status succeeded showed a normal card with a button silently
missing. The section is now also sent with error: true, and My account
shows its error line. The error field's description, the cabana README
and the My account docs say so; the OpenAPI document and the SPA schema
types are regenerated.
reserveScript reads the record keys of the binding set's members, which it
cannot declare in KEYS, and its declared keys hash to different slots, so
Redis Cluster refuses it and a key-pattern ACL must allow the whole
<prefix>mcp: range. The script's comment, the pier README's
mcp.sessions.store row (which also no longer says the store opens on the
first request) and docs/services/mcp.md say so.
promptHandler logs a failing Render's error with the prompt name and
answers the fixed JSON-RPC internal error "pier: the prompt could not be
rendered", as dispatch does for tool calls (A-WR-08), so database or
provider text never reaches the agent or its transcript.
TestPromptRenderError renders a prompt that fails with a database error
naming its host and asserts, on both paths, that the client sees only the
fixed text and the cause is in the log.
Fan-out matching snapshots the sessions (and stateless entries) under the
router-wide lock, releases it, and reads each identity through a small
identity box that is never held across I/O, so an event no longer waits on
a session that is re-filtering while it holds sr.mu or sp.mu.
Widening and the fan-out recomputes run their guard checks with
guardContext: not cancelled with the triggering request, but bounded by
mcp.call_timeout, so a provider that never answers cannot hold a session
lock forever. README and docs/services/mcp.md describe the wider meaning of
mcp.call_timeout.
TestLockScopeUnderWidening blocks a widening guard check, fires a
permission event for that session and asserts that another session's call
still finishes, and that a never-answering check ends at call_timeout.
- plain-key shortcuts test composedPath()[0], and a custom element inside a
form counts as editable (closed shadow roots)
- Ctrl/Cmd+S and Ctrl/Cmd+Enter blur the focused field before clicking, so a
pending taglist draft or native change is committed, then refocus it
- safeUrl keeps javascript:/data: URLs out of href and window.open
- a failed contributions GET shows a toast and keeps the sections; a section
with error true shows an error line
- My account confirm and stale texts get their own phrase keys
- rebuilt modules/boardwalk/dist
- an account action URL is kept only when it is http(s) with a host or a
same-origin path; anything else is dropped and logged without the URL
- a failing Status marks its section error: true and a failing Applies
drops only that action, instead of a 500 for the whole list
- status text goes through localizeDetail (B-IN-03); the non-string
payload refusal is the phrase key action_prompt_invalid (B-IN-02)
- the record action annotations and pact's prompt doc say what happens
(B-IN-05 text); admin.json and schema.d.ts regenerated
- pact.MCPSubjectChanged and pier.Event gain TokenID and ClientID; a
narrowed revoked event closes only matching sessions and stateless servers
- cabana fires the admin token revoke with the token row id
- towel.WithClientIP/ClientIP; pier puts the address (surf applies
http.trusted_proxies) on the resolve context and ResolveBackendToken logs it
- pact.FrontendTokenScope and pact.HasFrontendTokenScopes declare member
(usr_) token scopes; the admin token catalog never reads them
- pact.ValidTokenScopeName carries cabana's scope-name pattern; cabana's
g15_ catalog switches to it with unchanged behaviour and messages
- MintAudience stamps auth_time equal to iat; refreshAudience copies the
presented token's value and never renews it (absent stays absent)
- bouncer.ClaimAuthTime and bouncer.VerifyAuthTime for plugins, with the
same signature, audience and expiry checks as VerifyClaims
- TestAuthTimeSmoke traces mint, refresh and read
- pact.MCPPrompt.Writes (additive; the zero value keeps today's listing)
- pier's allowsPrompt hides a prompt declared with Writes from an identity
without write, as it hides a tool that is not read-only (C-IN-01
framework part, P-28)
- a path field must be required in the reflected schema (boot error), and
a missing value, an empty, . or .. segment, or a / inside one segment is
a tool error before dispatch (A-WR-01, A-WR-02)
- a failed guard dispatch logs its cause and answers a fixed tool text;
only an invalid token and an unknown or deactivated account keep their
own fixed texts (A-WR-08)
- path and query scalars decode with UseNumber, so integers above 2^53
keep their digits (A-IN-04)
- the dispatched request carries the outer Host, X-Forwarded-Proto and
X-Forwarded-Host (A-IN-05)
- an embedded struct on a GET or DELETE tool fails boot (A-IN-07)
- a dispatch whose guard list or principal kind contradicts the tool's
guard is refused (A-IN-08)
- Identity.fingerprint covers scopes, both subjects, the ceiling and the
token kind; a stateful lookup or stateless hit whose fingerprint changed
re-filters the server with the event diff under its own mutex (A-WR-04)
- Mount warms a Redis store in the background with a 1 s to 30 s backoff
and every request kicks a pending open without waiting, so stateless-only
instances subscribe to fan-out (A-WR-05)
- a stateless subscriptions/listen POST re-verifies its bearer every
recheck interval; an LRU-evicted stateless server closes its sessions
(A-WR-06)
- resolvePersonalToken refuses a subject with TokenID 0, and the
pact.MCPPersonalTokenResolver contract says one resolver per application,
false means unauthorized and TokenID keys the binding (A-WR-09, A-IN-03)
- cabana wraps the bouncer backend guard middleware: a marker that lists the
backend guard with Backend false answers 401 before the guard honours it,
as ResolveBackend already does, so a frontend user id is never read as an
administrator's by the admin API or a backend-guarded plugin route
- found by plan 19.7-05's TestDispatchMarker (admin API in-process case)
- '?' help dialog (ShortcutHelpDialog) listing the current screen's shortcuts
- Esc = Cancel on record forms; '/' focuses the list search
- settings forms: Save and close (Ctrl/Cmd+Enter, then /settings) and Ctrl/Cmd+S
- My account: Ctrl/Cmd+S
- modal forms: Ctrl/Cmd+S and Ctrl/Cmd+Enter submit the innermost dialog only
- docs: Keyboard shortcuts section in the admin SPA page
- app/hotkeys.ts: dependency-free data-hotkey core (Ctrl/Cmd+S, Ctrl/Cmd+Enter,
Ctrl/Cmd+Shift+S, Esc, / and ?), innermost-modal root, capture-phase modifier
listener, platform labels and the help-dialog state
- Button hotkey prop (data-hotkey, aria-keyshortcuts); shortcuts installed by AppShell
- FormView: save keys and tooltips, Create and new on create forms via remountView
- phrasebook: form.create_and_new and the shortcuts group (en, pl)
- personal tokens only behind mcp.allow_personal_tokens (401 without a
lookup otherwise); an admin token yields a backend-only identity with
its scope limit per dispatched method, a user token a frontend-only one
- read/write ceiling and prompts proven by the tracer's subtests; the
audit line asserted to carry the tool and never the arguments or token
- the mounted *pier.Pier is published on the application (test seams in
export_test.go)
- docs/services/mcp.md with the tested acme.blog example, site.yaml note,
cross-links from authentication.md, registration.md and the surf README
- pact.HasAccountContributions compiled at Activate; GET /account/contributions
and POST /account/contributions/{name}/actions/{action} behind interactiveOnly
(AccountContributionView, AccountContributionActionView, AccountActionResultView)
- RecordAction.prompt (RecordActionPrompt) for a pact.AdminRecordAction with a
Prompt; the record action route accepts payload only for such an action
(trimmed JSON string, non-empty, capped at max_length) and hands it to Run as
Value through CRUDService.RecordActionWithValue; a prompt without a label
fails boot
- fireSubjectChanged: pact.MCPSubjectChanged for revoked tokens, Administrators
saves that changed role, permissions or superuser, Roles saves that changed
grants, deactivation and deletion
- phrases backend::lang.form.action_prompt_required/_too_long (en, pl)
- smoke tests TestAccountContributionsSmoke and TestRecordActionPromptSmoke;
TestRecordActionPromptRun on Postgres; route inventories, CSRF walk and
OpenAPI conformance cases extended for the two routes
- regenerated admin/openapi/admin.json and admin/src/api/schema.d.ts; cabana
README, docs/backend/{admin-controllers,users-and-permissions}.md
- surf.Router.MountStream and recoverStream: a raw, unbuffered route kind
whose http.Flusher reaches the client; panic after the first byte only
aborts the body
- surf.MCPBuildTag and the mountPier hook: mcp.enabled is inert without the
summercms_mcp tag until it is true, then boot fails naming the tag
(pier_off.go is the untagged side)
- wristband parseAuthorizeScopes takes the server's allowed set built from
Options.ScopesSupported; the default set is unchanged
- cooler.KeyPrefix exported for the MCP module's Redis keys
- smoke tests TestStreamingRouteSmoke, TestPierMountSmoke, TestMCPScopesSmoke
- surf, wristband and cooler READMEs, docs/services/{routing,oauth-server}.md
- bouncer.Dispatch, WithDispatch, Dispatched: context-only pre-resolved
principal marker honoured by Registry.Middleware for listed guard names
- cabana.ResolveBackend honours a backend-kind marker; additive
ResolveBackendToken, ScopeLimitFor and BackendPrincipal resolvers
- surf.Router.Handler and compileHandlers: one compiled chain per route,
shared by BuildRouter validation and compile
- smoke tests TestDispatchSmoke, TestDispatchedSmoke, TestDispatchMarkerSmoke
- READMEs and docs/services/{authentication,routing}.md updated