- optional beachcomber.PageSearcher returns a page of candidate ids plus
the engine's found count; beachcomber.SearchPage falls back to
SearchIDs for engines without it, so Engine is unchanged
- Query.QueryByWeights is sent to Typesense as query_by_weights; a
mismatched weight list or a page above typesense.MaxPerPage (250) is
refused before any request
- attach.PublicURL and (*File).URL build Winter File::getPath() URLs; the
thumbnailer decodes webp via golang.org/x/image v0.46.0 and checks the
image size from the header before decoding
- tide requests carry multipart parts (files beside the fixture pinned by
sha256) encoded with the fixed MultipartBoundary, so PHP and Go receive
byte-identical bodies
- tide masks the random partition, disk name and file id of url/thumb_url
upload URLs while still diffing prefix, size, mode and extension, and
NormalizePublications masks Carbon dates in the published album
- lagoon.ValidateRequest ports Laravel 9 request validation: wildcard
expansion, implicit-rule stop, bail, sometimes/nullable/blank skipping,
size messages split by type and character-counted string lengths
- ParseRules, In, CustomRule, UploadedFile and ErrorKeys for rule tables
- pl/en lagoon::validation catalogs ported verbatim from WinterCMS
- lagoon.Validate answers a numeric range failure with the bound that
failed (min, max or numeric between) instead of always max
- new note .planning/notes/core-plugins-own-repos.md: shared core plugins live in sm-<name>-plugin repos mounted as submodules
- 01-CONTEXT deferral points to the note; PROJECT constraint and Key Decisions row
- ROADMAP Phase 12 repos and the 12-01 entry, and Phase 12 plans 12-01, 12-02, 12-05 name sm-user-plugin and the submodule commit workflow
Add a backend admin migration that creates a unique index on
lower(backend_users.email). Rows copied from WinterCMS may hold emails
that differ only in case, so the migration refuses to run and names the
clashing logins instead of choosing an account to drop.
Under set -e a missing TAP summary aborted run_site without a refuse
message; the explicit refuse checks now report it. The site's test
script pins the TAP reporter so the summary lines exist on Node 23+.