18 KiB
phase, plan, subsystem, tags, requires, provides, affects, actuals, plan_head_before, plan_head_after, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, coverage, duration, completed, status
| phase | plan | subsystem | tags | requires | provides | affects | actuals | plan_head_before | plan_head_after | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | coverage | duration | completed | status | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 12-p-ytarium-api-collections-and-albums | 01 | api |
|
|
|
|
|
cfe568a214 |
1bdbdf041b |
|
|
|
|
|
|
39min | 2026-10-02 | complete |
Phase 12 Plan 01: Framework gaps, user groups and Phase 12 scope Summary
Laravel 9 request validator with WinterCMS pl/en catalogs, Winter upload URLs and webp, multipart tide fixtures with shape-masked upload URLs, a re-gateable search found count with field weights, and Winter user groups in sm-user-plugin.
Performance
- Duration: 39 min
- Started: 2026-10-02T09:09:24Z
- Completed: 2026-10-02T09:48:46Z
- Tasks: 4
- Files modified: 57 (39 summercms.go, 8 fonoteka.go, 10 sm-user-plugin)
Accomplishments
lagoon.ValidateRequestports Laravel 9 request validation: wildcard expansion in Laravel's attribute order, implicit-rule stop,bail,sometimes/nullable/blank skipping, size messages split by type, string length in characters and exact decimal bounds.{"posts":[]}againstrequired|array|min:1gives the singlePole posts jest wymagane.in Polish. The Go expectations were checked case by case against the real Winter validator run from the PHP vendor tree.- The
lagoon::validationpl/en catalogs are generated verbatim from Winter'svalidation.phpfiles. Keys missing in Polish (after_or_equal,before_or_equal) fall back to English. - The
lagoon-validate-min-messagetodo is folded:lagoon.Validatenow reports the bound that failed, and every user-plugin 422 fixture still replays. attach.PublicURLand(*attach.File).URLproduce Winter'sFile::getPath()URLs. The webp decoder is registered, and the thumbnailer reads the image size from the header before it decodes.- tide requests can carry multipart
parts, encoded with the fixedtide.MultipartBoundary. File bytes stay beside the fixture, pinned by sha256. Upload URLs underurl/thumb_urlare compared by shape (default prefix/storage/app/uploads/public), and Carbon dates in the published album subtree are masked. beachcomber.PageSearcher,beachcomber.SearchResultandbeachcomber.SearchPageexpose the engine'sfoundcount.Query.QueryByWeightsis sent asquery_by_weights. A Typesense page above 250 is refused before any request is sent.- sm-user-plugin gains
user_groups(with the Guest and Registered seed rows) and theusers_groupspivot, plusmodels.UserGroup, aUser.Groupsrelation that is never serialized, andclasses.UserGroupCodes/HasGroupCode. No controller changed. - ROADMAP Phases 12-14 and REQUIREMENTS API-01/02/03/07 and INTG-01 now state the locked Phase 12 boundary.
Task Commits
- Task 1: Laravel request validator (tracer):
f9b7f2e(feat, summercms.go) - Task 2: uploads, webp, tide multipart and masks:
e06e0cc(feat, summercms.go);c65ab3c(build, sm-user-plugin);6d6c827(build, fonoteka.go) - Task 3: search found/weights and user groups:
3ae49bb(feat, summercms.go);8697007(feat, sm-user-plugin);02e1aa9(feat, fonoteka.go, pointer bump) - Task 4: roadmap and requirements wording:
1bdbdf0(docs, summercms.go)
Per-repo plan ledgers (commits measured with git rev-list --count):
- summercms.go:
cfe568a..1bdbdf0, 4 commits - fonoteka.go:
18576db..02e1aa9, 2 commits - sm-user-plugin:
44f3c4c..8697007, 2 commits
Files Created/Modified
modules/lagoon/validate_request.go: ValidateRequest, RequestRule, Rule, CustomRule, In, UploadedFile, ErrorKeys, the expansion and message logicmodules/lagoon/validate_rules.go: ParseRules and the rule implementations (FILTER_VALIDATE_EMAIL, the Laravel url regex rewritten for RE2, dates, exists, mimes, size)modules/lagoon/validate.go: numericRangeMessage (the min/between fix); removed the now-unused moneyInRangemodules/phrasebook/lang/{pl,en}/validation.yaml: Winter catalog portsmodules/lagoon/attach/thumb.go: PublicURL, File.URL, webp import, DecodeConfig before Decodemodules/tide/multipart.go(+ flow, fixture, record, replay, variables, diff, normalize, centrifugo_golden): Parts, MultipartBoundary, upload and album-date maskingmodules/beachcomber/searchable.go,engines.go,typesense/engine.go: PageSearcher, SearchResult, SearchPage, QueryByWeights, MaxPerPage../fonoteka.go/plugins/golem15/user/...: UserGroup model, Groups relation, migration 202610020001_create_user_groups, group-code queries, README../fonoteka.go/parity/schema_diff_test.go,parity/migrate_test.go,plugins/golem15/fonoteka/classes/hidden_marshal_test.go: allow-list and count updates- READMEs and docs:
modules/lagoon/README.md,modules/phrasebook/README.md,modules/tide/README.md,modules/beachcomber/README.md,docs/database/casts-and-validation.md,docs/database/attachments.md,docs/services/storage.md,docs/services/parity-testing.md,docs/services/search.md,docs/services/localization.md
Decisions Made
See key-decisions in the frontmatter. The two that change the plan's literal text are written up under Deviations.
Deviations from Plan
Auto-fixed Issues
1. [Rule 1 - Bug] Wildcard with an absent parent adds no attribute (plan said required still fires)
- Found during: Task 1
- Issue: The plan said an absent parent should expand to the literal attribute so that
requiredstill fires. Laravel'sValidationRuleParser::explodeWildcardRulesdrops a wildcard rule that has nothing to expand, and the real Winter validator returns[]for{}againstposts.*.title: required. - Fix: Implemented Laravel's behaviour and pinned it with
TestValidateRequestWildcardWithoutParentAddsNothing. - Files modified: modules/lagoon/validate_request.go
- Commit:
f9b7f2e
2. [Rule 3 - Blocking] ErrorKeys(errs, rules) takes the rule table
- Found during: Task 1
- Issue: The planned
ErrorKeys(errs map[string][]string)cannot recover an order from a Go map. - Fix: Added the rule table as a second argument. The function rebuilds Laravel's message-bag order from the rules (cross-checked against PHP output).
- Commit:
f9b7f2e
3. [Rule 3 - Blocking] Acceptance grep grep -c 'Pole :attribute jest wymagane.' prints 7, not 1
- Found during: Task 1
- Issue: The faithful port carries every Winter key (filled, required_if, required_with and others). In grep, the unescaped
.matches the space inwymagane gdy, so the pattern hits 7 lines. - Fix: None needed. The exact line
Pole :attribute jest wymagane."appears forrequiredandfilled, as in Winter (grep -cFprints 2). The port stays faithful.
4. [Rule 2 - Missing critical] Thumbnailer checks dimensions before decoding
- Found during: Task 2 (T-12-16)
- Issue:
File.Thumbdecoded the whole image before its pixel check. A small webp or png that declares a huge canvas would allocate the full image first. - Fix: It now reads the bounded bytes, runs
image.DecodeConfig, refuses anything over 4096x4096, and only then decodes. - Commit:
e06e0cc
5. [Rule 3 - Blocking] Migration and model counts in existing tests
- Found during: Task 3
- Issue: Adding a sixth user migration and a fourth user model broke hard-coded counts in
updates/organisations_test.goandupdates/user_session_test.go(sm-user-plugin), and inparity/migrate_test.go(three assertions) andplugins/golem15/fonoteka/classes/hidden_marshal_test.go(fonoteka.go). - Fix: Updated the expected histories, rollback counts and model count. No production code changed.
- Commits: 8697007, 02e1aa9
6. [Rule 3 - Blocking] tide RecordConfig.BaseDir, ReplayConfig.UploadPrefix and tide.DefaultUploadPrefix
- Found during: Task 2
- Issue: Recording needs a directory to read part files from, and the upload prefix had to be configurable.
- Fix: Added both fields and the default constant. All are additive and documented in the tide README.
- Commit:
e06e0cc
7. [Orchestrator instruction] sm-user-plugin not pushed
- The plan says to push sm-user-plugin
masterbefore the pointer bump. The orchestrator prompt says not to push anywhere, so commitsc65ab3cand8697007exist locally only. The fonoteka.go pointer references them.
Total deviations: 6 auto-fixed (1 bug, 1 missing critical, 4 blocking) plus 1 instruction-driven. Impact on plan: All deliverables shipped. The deviations follow the PHP contract or keep existing tests honest; there is no scope creep.
Issues Encountered
- One full
go test ./...run in summercms.go failed inmodules/lighthouse: its testcontainers Postgres did not become ready in time under parallel package load. lighthouse was not touched by this plan. It passed alone, and a second full run was green. - All commits were made on
masterin every repo (the project usesbranching_strategy: none, and the orchestrator dispatched a sequential executor on the main tree). The generic protected-branch guard would refusemaster. The orchestrator's instruction and the project convention were followed instead. .planning/ROADMAP.mdalready held an uncommitted Phase 12.1 insertion that the executor did not make. The Task 4 commit staged only this plan's hunks.
Known Stubs
None.
User Setup Required
None. No external service configuration is required. The app config change that adopts the Winter upload prefix (storage.uploads.bucket_url / public_path_prefix in fonoteka.go config/storage.yaml) belongs to 12-02/12-04.
Next Phase Readiness
- 12-02 can build the collection endpoints on
ValidateRequest,HasGroupCode(..., "admin"),attach.File.URLand tide multipart. - 12-04 still has to set the Winter upload layout in
config/storage.yamland check that avatar URLs stay correct. It usesbeachcomber.SearchPagefor the D-19 recount. - The PHP-oracle approach (vendor autoload plus
Winter\Storm\Validation\Factory) is available to 12-05 for table-driven validator coverage.
Self-Check: PASSED
All 15 created files listed above exist on disk. All 8 task commits exist: f9b7f2e, e06e0cc, 3ae49bb and 1bdbdf0 in summercms.go; 6d6c827 and 02e1aa9 in fonoteka.go; c65ab3c and 8697007 in sm-user-plugin. Plan-level verification passed: summercms.go go vet ./... && go test ./... (green on rerun), TestDocsTree, and fonoteka.go vet plus tests for the root module, the fonoteka plugin and the user plugin.
Phase: 12-p-ytarium-api-collections-and-albums Completed: 2026-10-02