- scripts/check-phase19.7.sh: fail-closed multi-repo gate (self-test, go,
marker; plugins, consumers, docker, admin and docs are stubs until the
later tasks fill them)
- modules/pier/helpers_test.go: the plan 02 fixtures moved out of the smokes
plus fakeGuard, fakeVerifier, fakeLinkResolver, fakePersonalResolver,
routePlugin, newApp, twoInstances, sdkClient and fireSubjectChanged
- TestToolsList (pier), TestDispatchMarker (bouncer, cabana with a Postgres
part through the screens harness, which now keeps its app)
- 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)
- SUMMARY with every deviation from the checkpoint report
- deferred items: sm-demo-app push and deploy wait for the user, release
builds on summercms v0.4.4 lack the cabana subject-event fix, Redis for a
second instance, the real-client check, the retracted mimetype
Plan 03 summary: verified account link, Administrators-screen link actions, plugin frontend permissions, user MCP guard and user_me in sm-user-plugin v0.1.10 (requires SummerCMS v0.4.4). deferred-items: cabana fires pact.MCPSubjectChanged as a value inside the transaction while pier listens for a pointer; fix before v0.5.0.
- hotkey core: combos, AltGr and layouts, labels, aria-keyshortcuts, editable
targets, modal root, refcounted install, disabled and repeat
- smoke: record, create, Create and new (new session key), Esc, modal scope,
settings Save and close, My account, list search and the help dialog
- ShortcutHelpDialog rows, keys and close button
- '?' 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
- store_redis.go: records with PX TTLs, per-binding sets, pub/sub events,
opened lazily from redis.url under cooler's prefix (nothing dials at mount)
- events.go: pact.MCPSubjectChanged republished through the store; every
instance recomputes the subject's sessions and stateless servers
(list_changed) or ends them on revocation; the link cache is dropped
- stateless.go: the SDK's stateless handler over per-identity servers for
2026-07-28 clients, routed by the protocol header or server/discover
- prm.go: RFC 9728 metadata at both well-known paths; long GET streams
re-verify their bearer every minute
- TestPierSessionsSmoke, TestPierFanOutSmoke, TestPierStatelessSmoke on
miniredis; README features, configuration and operational notes
- modules/pier: Host contract, mcp.* config, catalog compiled from
pact.HasMCP (prefixing, route binding, guard resolution, schema
reflection, annotations), per-request identity through the published
verifier and resolvers, built-in backend guard provider, in-process
dispatch with the bouncer marker, per-identity SDK server with the
per-request re-filter, stateful session router with rehydration over a
memory store, audit lines
- modules/surf/pier_on.go: the summercms_mcp hook adapting the router to
pier.Host; the untagged hello binary carries no SDK
- go.mod: go-sdk v1.8.0 and jsonschema-go v0.4.3 (decision note committed
before this change)
- TestPierTracerSmoke: SDK client lists and calls acme_echo end to end
through surf's compiled guard chain; README and root table row
- decision note for go-sdk v1.8.0 and jsonschema-go v0.4.3 with the
transitive graph, size, govulncheck result and the build-tag containment
- STACK.md rows for both modules naming the note (D-24)
- AccountContributions.vue on My account: one card per contributed section
with status and outline buttons; confirm or danger actions go through the
shared ConfirmDialog; a returned URL opens in a new tab (noopener) and stays
as a visible link; 409 reloads with the stale toast
- ConfirmRequest.prompt and useConfirm value; ConfirmDialog renders one
labelled text input (maxlength, autocomplete off, aria-describedby comment,
autofocus), confirm disabled until a value is typed, Enter confirms
- RecordActions posts {payload} for a prompted action and {} otherwise; a 422
on payload shows the server's detail as a danger toast
- types.ts exports RecordActionPrompt, AccountContribution,
AccountContributionAction, AccountActionResult
- vitest smokes AccountContributions.test.ts and RecordActionsPrompt.test.ts
- 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