- Command.Passthrough turns off cobra flag parsing and uses arbitrary
args, so every argument after the command name, including --help, -h
and --, reaches Run through Input.Args unchanged
- Command.Long sets cobra's Long text for the help command; without it
the description is shown as before
- both fields are additive: commands without Passthrough parse flags and
reject unknown ones exactly as before
- README and docs/console/writing-commands.md describe both fields
- new.sh reads the basename of SHELL (via ${SHELL:-} under set -u) and
prints fish_add_path "<dir>" for fish, which keeps it for new sessions,
export PATH="<dir>:$PATH" naming ~/.zshrc or ~/.bashrc for zsh and
bash, and today's generic export line with "your shell profile" for
any other, empty or unset shell
- dq_escape escapes the install directory for the target shell's double
quotes: backslash, double quote and dollar always, plus backtick and a
single-quoted ! for POSIX shells; a per-character loop, so bash 3.2
and 5.2 behave the same
- the behaviour harness defaults SHELL to empty so cases do not depend on
the developer's login shell, adds an unset-SHELL mode and covers fish,
zsh, bash, empty, unset, unknown and two metacharacter directories
- TestBootstrapEndToEnd pins SHELL=/bin/bash
- sunrise README and docs/setup/installation.md describe the per-shell
hint; the manual PATH section gains fish_add_path (go env GOPATH)/bin
- TestScriptBehavior: 25 cases on a stub PATH (real git, stub go/docker/summer)
against a bare fake framework remote: usage and name errors, existing app,
Go missing/old/devel, git required, Docker warning, newest release tag,
SUMMERCMS_REF, fast-forward re-runs, refusals with HEAD unchanged,
summer called from the parent with stdin closed, cd line and PATH hint
- TestScriptShellcheck: shellcheck -s bash new.sh, skipped only without shellcheck
- modules/sunrise/new.sh: curl | bash installer wrapped in main(), checks
Go 1.27+/git/Docker, clones or fast-forwards the framework at the newest
release tag (SUMMERCMS_REPO/DIR/REF overrides), installs summer and runs
summer new next to the checkout, prints the cd line and a PATH hint
- sunrise.Script() returns a fresh copy of the embedded script
- smoke, shape, truncation and gated end-to-end tests; README and modules row
- key:generate now calls GenerateAppKey; its output is unchanged
- tools can write a fresh app.key without the check-mark decoration
- document lagoon.GenerateAppKey in the lagoon README
Claude-Session: https://claude.ai/code/session_01M8Qu9LgCKYy7iYEho52tW8
- 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
A settings entry with a controller opens that controller's route from
its card (settingsPath); singleton cards still open /settings/<code>.
On a linked controller with no navigation entry of its own the rail
marks Settings current, the breadcrumbs read Settings > entry label
(plus the record title on record and create routes) and the page title
uses the entry label. The settings docs describe the admin behaviour;
the embedded admin shell is rebuilt.
pact.SettingsItem gains an additive Controller field, the equivalent of a
WinterCMS registerSettings 'url' => Backend::url(...) entry. A link entry
declares no Model, Form or NewModel and needs no AdminFS; start-up fails
when it combines Controller with a singleton form or a model, or names an
unregistered controller. Settings codes stay one namespace.
GET /settings lists a link entry with its controller only when the
principal passes the item's permissions and may open the controller.
Registry.Setting never returns a link entry, so the singleton settings
endpoints answer 404 for its code. SettingsEntry carries controller,
empty for singletons; the OpenAPI document, generated SPA types and
settings fixture follow, and the pact and cabana READMEs and the
settings docs describe the link.
The admin sets the html font size to 14px, so the rem utilities w-11 and
size-5 shrank the track to 38.5px and the knob to 17.5px against the
fixed 26px height and 18px travel. Use w-[44px] and size-[20px] so the
knob sits in a symmetric 3px inset at both ends. Rebuild the embedded
admin shell.
- MLField.vue is the one locale-switching wrapper; MLTextField,
MLMarkdownField and the new MLTextareaField are shells around it
- isMLFieldType replaces the inline ML type-name checks in formState,
FormView and RelationChildModal
- register mltextarea as a group-labelled control; rebuild dist
- mltextarea compiles (with size), binds as a writable scalar and goes
through the mltext lift, translation write and hydrate paths
- preset on or from an mltextarea field stays refused
- tests: compile, Postgres round trip over a nullable host column with
multi-line text, invalid locale maps, preset refusals
- schema_types.go and the form-schema swag description name text or mltext
- regenerated admin/openapi/admin.json and admin/src/api/schema.d.ts
- README and docs/backend/forms.md describe the per-locale rules
- formState: changedLocales and presetUpdates hold the preset-follow rules
for text and mltext pairs
- FormView applies presetUpdates on create, tracks per-locale hand edits of
ML fields and follows the active ML locale
- rebuilt modules/boardwalk/dist
- compilePresetKey admits a text or mltext target
- checkPresets admits a text or mltext source
- TestPresetML pins every text/mltext pairing and the refusals
- MarkdownField posts the source to POST /markdown/preview when Preview
opens and again 300 ms after a change while open; stale answers dropped
- the pane binds only data.html of a 2xx answer; a refusal is a text notice
- mlmarkdown previews the active locale and follows a locale switch
- .summer-markdown style kit restores headings, lists, code and tables
- vitest coverage, forms.md and rebuilt modules/boardwalk/dist
- POST {prefix}/api/v1/markdown/preview renders {markdown} through
cabana.RenderMarkdown in the backend-guarded group behind requireAjax
- refused output is a 422 validation_failed on markdown with a fixed message
- swag annotation, regenerated admin.json and schema.d.ts
- route inventories, CSRF walk (26) and OpenAPI conformance learn the route
- README and docs/backend/forms.md document the route
- table-driven hostScalarString cases for nil and live pointers to string, ints, uint, float, bool, []byte, named string and double pointers
- DB-free hydrateMLRecord cases for nil, empty and filled *string hosts
- hostScalarString walks pointers via reflect; nil at any depth is ""
- string kinds return raw text, byte slices decode, other kinds format the dereferenced value
- Postgres round-trip regression for an mltext field on a *string column
Copy overwrote the active locale with the other locale's often-empty string, so the editor went blank. Keep a single synchronized locale picker.
Co-authored-by: Cursor <cursoragent@cursor.com>
UpdateChild and ShowChild now lift, apply, and hydrate nested locales the same way as host CRUD, so a child form keeps English on the column and Polish through the writer.
Co-authored-by: Cursor <cursoragent@cursor.com>
RelationService looks up TranslationWriter like CRUDService so CreateChild can lift locale maps before fillChild, write Polish after the child PK, and return a hydrated title map.
Co-authored-by: Cursor <cursoragent@cursor.com>
MLMarkdownField and relation-child forms share the enabled-locale
inject and adopt merge; OpenAPI, types, docs, and boardwalk dist match.
Co-authored-by: Cursor <cursoragent@cursor.com>
Form schema meta lists enabled locales, Show/save expand mltext maps
without D-11 fallback, and the SPA seeds and adopt-merges every locale.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Document markdown, mltext, and mlmarkdown plus TranslationWriter save semantics
- Extend the SPA registry tests and rebuild committed boardwalk dist
Co-authored-by: Cursor <cursoragent@cursor.com>
Lift locale maps before ProjectWritableFields so a Journal-shaped save can persist the default host scalar and non-default locales through TranslationWriter without dropping nested JSON.
Co-authored-by: Cursor <cursoragent@cursor.com>
Look up a backpack-published resolver so a compiled translate plugin can
strip an enabled URL prefix and write a validated locale onto context.
Co-authored-by: Cursor <cursoragent@cursor.com>
- acme-demo-lookup fixture is a reorder widget: a click reverses its items,
sends the new id order as detail.payload and renders the returned items from
the data attribute and the summer-result event with textContent, no HTTP
- partials-and-widgets gains a Widget element contract subsection listing the
attributes the SPA sets and the summer-action / summer-result events
- cabana README names both events in the form widgets bullet
- WidgetField posts the summer-action event's detail.payload as payload only
when the detail carries one; a payload-less post body is unchanged
- after a successful action the response data is set on the element as the
data attribute (removed when the answer has none) and announced with a
summer-result event carrying {data, fill, message}; failures dispatch nothing
- WIDGET_RESULT_EVENT exported from formContext; unit tests for both directions
- modules/boardwalk/dist rebuilt
- pact.AdminActionInput.Payload (json.RawMessage) carries the widget's own
JSON value untouched; pact.AdminActionResult.Data is passed through as data
- cabana decodes payload with a 64 KiB cap (422 on body), refuses it on the
toolbar and record routes, and embeds Data once encoded with a 256 KiB cap
(opaque 500 when larger or unencodable); fill stays filtered
- root .swaggo overrides json.RawMessage so swag keeps record_id and values;
admin.json and schema.d.ts regenerated (payload?: unknown, data?: unknown)
- TestWidgetPayloadAndData covers pass-through, cap, refusal and data 500
- cabana and pact READMEs, partials-and-widgets and admin-spa docs updated
Ported plugins send Winter icon-* class names on nav and settings; the SPA
never loads Font Awesome, so unknown names rendered as empty squares. Map
BM Studies, Quizzes, icon-pencil, and a closed Winter backend alias table
onto named @lucide/vue 1.17.0 exports, keep Square for unknown names, and
rebuild the embedded boardwalk dist.
- bulk action: empty, duplicate, unordered, absent, partial, out-of-scope, rollback, concurrent runs, permissions, CSRF, body cap
- record action: scope, Applies, strict body, offered order, rollback, Applies error
- ForbiddenError from every Form hook, the bulk delete and the relation link and child hooks
- permission editor modes, locked codes and provider errors; relation locks on create, update and belongsTo
- TestPhase121BootErrors: every boot error of plans 01 and 02 with plugin, controller and file
- pact: the action, row state and filter contracts on a sample controller
- TestPhase121Threats: one subtest per mitigated threat T-12.1-01 to T-12.1-15
- roster fixture: sentinel names and knobs for failing hooks and providers
- scripts/check-phase12.1.sh: fail-closed go test detector, --self-test and --security
- FieldRelationContract.WritableForeignKey makes a belongsTo field over a
protected foreign key writable; the protected key list is unchanged
- cabana.RelationLockProvider names related ids an administrator may not add
or remove: options and labels carry locked, and a create or update that
changes the locked subset is 403 before any row is written
- columns.yaml invisible keeps a column searchable and out of the rows
- a controller implementing pact.FilterOptions serves a scope filter's
choices before the model
- SPA: locked chips and options in RelationField, DataTable skips invisible
columns
- README, docs, OpenAPI document, TS types and dist updated
- type: permissioneditor with mode radio (1, -1) or checkbox (1); the
controller serves the options per request through
cabana.PermissionEditorProvider and reads and stores the values
- a save answers 422 for a non-object, an unknown code or a value outside the
mode's set and 403 for a changed locked code; stored codes that are not
offered are kept
- record responses carry the stored permissions as an object
- SPA: PermissionEditorField with sections by tab, locked rows and a read-only
mode for the preview
- README, docs, OpenAPI document, TS types and dist updated
- pact.FormVirtualFields lists form fields that are not model columns: never
bound, filled or projected; their values reach the Form hooks through
cabana.VirtualFieldsFromContext when the field's context allows the operation
- type: password is a masked field that must be listed as virtual
- pact.FormRules supplies the rule set per operation and replaces the model's
Rules() for admin saves; a rule on a virtual field sees the submitted value
- preset on a text field follows another text field on the create form
- SPA: PasswordField, preset handling in FormView, empty password left out of
an update
- README, docs, OpenAPI document, TS types and dist updated
- config_form.yaml preview block (optional headerPartial), reported in the form schema as preview
- fields with context: preview show only on the preview screen and are never written
- form messages preview and edit; recordActions without a preview block stops boot
- SPA route {id}/preview, PreviewView and PreviewField, record actions in the footer
- mapWinterUrl maps preview/:id; the update form returns to the preview
- summer-callout partial style classes for status hints
- README, docs, OpenAPI document, TS types and the embedded build updated
- hooks and bulk, record, toolbar and widget actions may return it
- 403 forbidden with the localized message and field details; the write's
transaction is rolled back; other errors stay the opaque 500
- form shows a refused save as a persistent banner and keeps the values;
a refused delete is a toast
- smoke tests, OpenAPI notes, dist, README, docs
- pact.ListRowStates with the fixed RowState set deleted, negative, disabled
- list response meta.row_states keyed by row id; unknown values dropped
- list messages rowStateDeleted, rowStateNegative, rowStateDisabled
- update writes through the scope the load used, so a soft-deleted record
a controller includes stays soft-deleted
- DataTable row state badges and text styles
- roster fixture, smoke tests, OpenAPI, TS types, dist, READMEs, docs
- pact.HasAdminRecordActions with AdminRecordAction (Applies, Run)
- config_form.yaml recordActions, compiled fail-loud
- show response meta.actions lists the permitted actions that apply
- POST .../{controller}/{id}/actions/{action}: record loaded and locked
through the form scope; 404 out of scope, 409 when it does not apply
- RecordActions.vue with confirm and request flow (mounted by plan 02)
- roster fixture, smoke tests, OpenAPI, TS types, READMEs, docs
- pact.HasAdminBulkActions with AdminBulkAction, its input and result
- config_list.yaml bulkActions, compiled fail-loud, needs showCheckboxes
- POST .../{controller}/bulk/{action}: ids resolved and locked through the
list scope in one transaction; partial selection is 409
- list schema offers declared actions per principal, with confirm text
- admin SPA bulk actions menu with confirm, busy state and failure toasts
- acme.roster fixture, tracer test, OpenAPI, TS types, dist, READMEs, docs
- LoadUpstream names the file for a missing, unknown-field, wrong-version,
method-less, relative-URL or out-of-range-status sidecar
- WriteUpstream writes nothing for an invalid sidecar or an uncreatable
directory
- compareParts reports count, name, filename, content type, sha256 and
value mismatches and a non-multipart body
- every OPTIONS on a CORS path: 204 with Cache-Control no-cache, private
- a preflight echoes the requested method (upper-cased) and headers when * allows any, with Vary and PHP's default Content-Type, as recorded from PHP
- README and the routing docs describe the answer