Files
summercms/.planning/quick/261001-ddh-replace-lagoon-hardcoded-icu-pl-pl-datab/261001-ddh-SUMMARY.md

8.2 KiB

phase, plan, status, subsystem, tags, requires, provides, affects, tech-stack, key-files, decisions, metrics, actuals, plan_head_before, plan_head_after
phase plan status subsystem tags requires provides affects tech-stack key-files decisions metrics actuals plan_head_before plan_head_after
quick-261001-ddh 01 complete lagoon (data layer), application genre list
lagoon
collation
postgres
icu
docs
parity
lagoon.Collate / lagoon.OrderOption per-query collation for lagoon.OrderBy
lagoon.Open / lagoon.Use without any database locale check
every framework test harness (plain PostgreSQL databases)
application genre list (fonoteka.go)
added patterns
variadic functional options on an existing exported func (source-compatible API growth)
byte-loop identifier validation plus one quoting helper for raw SQL fragments
created modified
.planning/notes/lagoon-per-query-collation.md
modules/lagoon/order.go
modules/lagoon/connection.go
modules/lagoon/order_test.go
modules/lagoon/example_test.go
modules/lagoon/README.md
docs/database/queries-and-pagination.md
../fonoteka.go/plugins/golem15/fonoteka/controllers/genre_controller.go
../fonoteka.go/parity/genre_integration_test.go
Per-query COLLATE via lagoon.Collate supersedes Phase 3 D-06 (database-level ICU pl-PL); CheckLocale removed as pre-v1 API, no replacement config key
duration completed
~8 min 2026-10-01
tokens tasks commits
15200 3 2
565ce982d9 026ce74234

Quick 261001-ddh: Per-query collation replaces lagoon's ICU pl-PL database requirement

lagoon.OrderBy(db, col, dir, allowed, lagoon.Collate("pl-x-icu")) now emits a validated, double-quoted COLLATE clause. lagoon no longer checks the database locale, and the framework's tests and docs use plain PostgreSQL. The application's genre list sorts with pl-x-icu, so it returns the recorded Polish order on any database. A libc C database test proves it.

Commits

Task Repo Commit Message
1 summercms.go 037dc53 feat(lagoon): per-query collation for OrderBy, drop the database locale check
2 fonoteka.go 2da9482 fix(genres): sort genre names with a per-query Polish collation
3 summercms.go 026ce74 docs(quick-261001-ddh): record per-query collation decision superseding D-06

commits: 2 counts summercms.go only (measured with git rev-list --count 565ce98..HEAD). The fonoteka.go commit 2da9482 sits on top of 1c88199 in the sibling repo. actuals.tokens is chars/4 over both repos' diffs (53422 + 7458 bytes).

What changed

Task 1, framework (037dc53):

  • modules/lagoon/order.go:
    • Adds OrderOption and Collate(name). OrderBy now has the signature OrderBy(db, column, dir, allowed, opts ...OrderOption), so existing callers compile unchanged.
    • Checks run in order: column allow-list, then direction, then collation. The collation check accepts 1-63 bytes, starting with an ASCII letter, digit or _ and continuing with letters, digits, _, -, . or @.
    • The name is emitted through a single quoteCollation helper. When the last Collate option wins, the clause is <col> COLLATE "<name>" ASC|DESC.
  • modules/lagoon/connection.go: removes the CheckLocale/checkLocale functions, their constants and both call sites. The Open/Use doc comments are rewritten.
  • New tests:
    • The clause tests now use neutral table names.
    • Tables of accepted and rejected collation names. The rejected list includes injection-shaped names, NUL, non-ASCII, a 64-byte name, and names with a leading - or .. Each rejection comes back from OrderBy as (nil, err) and leaves the GORM handle untouched.
    • The last Collate option wins, and the check order is pinned.
    • ExampleCollate.
    • Integration test TestCollatePolishOrderOnLibcDatabase: Open and Use succeed on a libc C database. The plain order is [Lis Mysz Zebra Łoś] and the pl-x-icu order is [Lis Łoś Mysz Zebra]. An unknown collation reaches Postgres as an identifier (SQLSTATE 42704).
  • Deleted tests: TestCheckLocaleRejectsNilSQL, TestWrongICULocaleFailsOpen, TestCheckLocaleMessage.
  • Removed the ICU initdb args and ICU CREATE DATABASE from the lagoon, lagoon/attach, cabana, beachcomber, lighthouse, bouncer and conga harnesses and from docs/examples/blog (icuDatabase was renamed to testDatabase). The testcontainers imports that became unused were dropped in the bouncer and cabana tests.
  • Updated the lagoon README, root README, installation, models, queries-and-pagination (new "Sorting with a collation" section with an ExampleCollate snippet filled by docs:sync) and plugin testing docs. The comment in scripts/check-phase8.sh is corrected; its initdb args are kept.

Task 2, application (2da9482):

  • polishCollation = "pl-x-icu" is passed at the PolishOrder call site. The response shape is unchanged.
  • The source check now also requires lagoon.Collate(.
  • TestGenrePolishOrderOnLibcDatabase replaces TestGenreQueryFailsOnWrongLocale. Before the controller change it failed (RED), with Łódź Beat sorted last. After the change it passes: Łódź Beat comes after Latin, while the plain ORDER BY on the same database still puts it last.
  • TestMigrateRejectsNonPolishLocale is deleted.
  • README: ICU pl-PL is now a recommendation. The section explains the explicit collation on the genre list and that the framework no longer checks the locale.

Task 3, decision note (026ce74): .planning/notes/lagoon-per-query-collation.md supersedes D-06, and D-06 in 03-CONTEXT.md points to it.

Verification

  • summercms.go:
    • go vet ./... is clean.
    • Full go test -count=1 ./... with Docker passed (35 packages ok, exit 0).
    • The targeted lagoon tests pass, including the libc integration test.
    • go test ./cmd/summer -run TestDocsTree passes, and go run ./cmd/summer docs:build --check reports "no problems found".
    • grep -rnE 'CheckLocale|ICU_LOCALE|icu-locale|ICU .?pl-PL' modules docs examples README.md and the fonoteka grep on order_test.go and example_test.go both print nothing.
  • fonoteka.go:
    • go vet ./... is clean in the root module and in both plugin modules.
    • Full go test -count=1 ./... passes in the root module and in plugins/golem15/user and plugins/golem15/fonoteka.
    • go test ./parity -run Genre passes.
    • gofmt -l on the touched packages is empty.
  • None of the three commits has a co-author trailer, and all files were staged by explicit path.

Deviations from Plan

1. [Scope boundary] gofmt drift in files this task did not touch. gofmt -l modules docs/examples (part of the plan's automated verify) prints 6 files that were already in that state at 565ce98: modules/compass/config_test.go, modules/compass/persist_test.go, modules/party/registry_test.go, modules/tide/flow_test.go, modules/tide/headers_test.go, modules/wristband/server.go. The drift is method-alignment whitespace from the newer gofmt. None of the files this task changed are listed. I did not fix them because they are out of scope; they need a separate gofmt -w commit.

2. [Verification scope] fonoteka.go workspace. In fonoteka.go, go vet ./... and go test ./... from the repo root cover only the root module (5 packages) under go.work. I also ran both inside plugins/golem15/user and plugins/golem15/fonoteka, and all passed.

3. [Rule 3 - Blocking] Unused imports. After the WithEnv option was removed, the testcontainers import was unused in modules/bouncer/phase07_coverage_test.go and modules/cabana/auth_test.go, so I removed it. The plan anticipated this.

Commits were made directly on master, as the plan says (branching_strategy: none).

Known Stubs

None.

Threat Flags

None. The only new raw-SQL surface is the collation name. It is covered by T-ddh-01: byte validation, one quoting helper, and unit tests with injection-shaped names.

Self-Check: PASSED

  • FOUND: modules/lagoon/order.go (contains func Collate()
  • FOUND: docs/database/queries-and-pagination.md (contains src=modules/lagoon/example_test.go#ExampleCollate)
  • FOUND: ../fonoteka.go/plugins/golem15/fonoteka/controllers/genre_controller.go (contains lagoon.Collate()
  • FOUND: .planning/notes/lagoon-per-query-collation.md (contains Supersedes)
  • FOUND: commits 037dc53, 026ce74 (summercms.go), 2da9482 (fonoteka.go)