Files
summercms/.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-04-SUMMARY.md

13 KiB

phase, plan, subsystem, tags, requires, provides, affects, actuals, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, coverage, duration, completed, status, plan_head_before, plan_head_after
phase plan subsystem tags requires provides affects actuals tech-stack key-files key-decisions patterns-established requirements-completed coverage duration completed status plan_head_before plan_head_after
09-backend-admin-authentication-and-schema-pipeline 04 admin
cabana
list-schema
gorm
pagination
filters
phrasebook
phase provides
09-backend-admin-authentication-and-schema-pipeline cabana list compiler, form localizer, and backend guard
Ordered Winter list schema with columns, actions, default sort, and page sizes
Switch, date-range, and registered model-scope filters without raw SQL
Allowlisted list query execution with D-11 envelopes and primary-key tie-break
09-backend-admin-authentication-and-schema-pipeline
admin-api
phase-10-spa
tokens tasks commits
22349 3 7
added patterns
List IR caches phrase keys; Localize copies labels per request
Query identifiers resolve only through compiled columns, filters, and FilterScopes
lagoon.Paginate supplies last-page math; the admin envelope keeps D-11's page key
created modified
cabana/list_schema.go
cabana/filter_schema.go
cabana/query.go
cabana/testdata/list/all_columns.yaml
cabana/testdata/list/all_filters.yaml
cabana/schema.go
cabana/schema_types.go
cabana/http.go
cabana/contracts.go
pact/capabilities.go
Omitted sortable defaults to true, except relation columns, which stay unsortable unless columns.yaml sets sortable
perPageOptions keep YAML order and must include recordsPerPage
list_toolbar, recordUrl, and showCheckboxes compile to create, update, and delete
A conditions key is a boot error; a model scope must be listed by FilterScopes()
Admin list meta uses D-11 page, while lagoon.Paginate still computes last_page
Pattern: list compilation walks declaration order and rejects unknown keys before routes are served
Pattern: search, sort, and filter values are bound; identifiers come only from the compiled schema
ADMIN-02
id description requirement verification human_judgment
D1 Every locked list column, action, default sort, search term, page size, and showSetup switch compiles to stable Winter JSON. ADMIN-02
kind ref status
unit cabana/list_schema_test.go#TestListSchemaCompile pass
false
id description requirement verification human_judgment
D2 Empty and single list declarations stay arrays, and column plus perPageOptions order is stable across compiles. ADMIN-02
kind ref status
unit cabana/list_schema_test.go#TestListSchemaEmpty pass
kind ref status
unit cabana/list_schema_test.go#TestListSchemaSingle pass
kind ref status
unit cabana/list_schema_test.go#TestListSchemaOrdering pass
false
id description requirement verification human_judgment
D3 Duplicate columns, unknown keys, bad defaults, path escape, a mismatched modelClass, and unsupported actions fail with plugin, controller, and file context. ADMIN-02
kind ref status
unit cabana/list_schema_test.go#TestListSchemaRejects pass
false
id description requirement verification human_judgment
D4 Switch, date-range, and model-scope filters compile in source order with typed switch values and no condition string. ADMIN-02
kind ref status
unit cabana/list_schema_test.go#TestListSchemaFilter pass
false
id description requirement verification human_judgment
D5 Model scopes resolve only through FilterScopes, and raw conditions, unknown columns, and arbitrary method names fail activation. ADMIN-02
kind ref status
unit cabana/list_schema_test.go#TestListSchemaScope pass
kind ref status
unit cabana/list_schema_test.go#TestListSchemaRejectsRawCondition pass
false
id description requirement verification human_judgment
D6 Filter and option labels localize per request while column, scope, and option values stay unchanged on the cached schema. ADMIN-02
kind ref status
unit cabana/list_schema_test.go#TestListSchemaFilter/labels_localize pass
false
id description requirement verification human_judgment
D7 Search, sort, relation search, empty, single, and adjacent pages return the D-11 envelope with a primary-key tie-break. ADMIN-02
kind ref status
integration cabana/query_test.go#TestListQueryContract pass
kind ref status
integration cabana/query_test.go#TestListQueryEmpty pass
kind ref status
integration cabana/query_test.go#TestListQuerySingle pass
kind ref status
integration cabana/query_test.go#TestListQueryAdjacent pass
false
id description requirement verification human_judgment
D8 Switch, date-range, and model-scope filters narrow rows through bound values and the registered scope name. ADMIN-02
kind ref status
integration cabana/query_test.go#TestListQueryFilters pass
false
id description requirement verification human_judgment
D9 Unknown or case-changed identifiers, oversized pages, and injected search text fail closed, and permission denial still runs before the query. ADMIN-02
kind ref status
integration cabana/query_test.go#TestListQueryRejectsInjection pass
false
36min 2026-09-24 complete db3d7222e9 3efdcc1c59

Phase 9 Plan 04: Deterministic admin list queries Summary

Winter list YAML now compiles to ordered columns and typed filters, and the admin index runs that schema through bound, tie-broken queries.

Performance

  • Duration: 36 min
  • Started: 2026-09-24T16:29:24Z
  • Completed: 2026-09-24T17:05:17Z
  • Tasks: 3
  • Files modified: 13

Accomplishments

  • config_list.yaml and columns.yaml compile to ordered columns, default sort, searchTerm: "search", page-size choices, toolbar create, row update, and bulk delete. Empty collections marshal as [].
  • config_filter.yaml accepts only switch, daterange, and a model scope named by FilterScopes(). A conditions key fails activation. Option values keep their JSON types.
  • GET /_admin/api/v1/{vendor}/{plugin}/{controller} searches, sorts, filters, and pages through those selectors. Equal sort values break ties on the primary key. Unknown identifiers return validation_failed after the permission check.
  • The 09-01 genre list still compiles, including a controller with no config_form.yaml.

Task Commits

Each task was committed atomically. commits: 7 is git rev-list --count from the plan ledger in summercms.go. The tracer expectation lives in fonoteka.go.

  1. Task 1: Ordered list columns and actions (RED) - fd591ed (test)
  2. Task 1: Ordered list columns and actions (GREEN) - aab4398 (feat)
  3. Task 2: Typed filters (RED) - cb832eb (test)
  4. Task 2: Typed filters (GREEN) - d3a9307 (feat)
  5. Task 3: Deterministic list queries (RED) - 7f451c3 (test)
  6. Task 3: Deterministic list queries (GREEN) - 6a57f63 (feat)
  7. Relation sort default (fix) - 3efdcc1 (fix)

App tracer: d8fb9ac in fonoteka.go (test)

Plan metadata: included in the docs commit for this summary

Files Created/Modified

  • cabana/list_schema.go - strict list compiler, model column check, and request localizer
  • cabana/filter_schema.go - switch, daterange, and scope compilation
  • cabana/query.go - allowlisted search, sort, filter, scope, and pagination
  • cabana/schema_types.go - list, filter, and action JSON types
  • cabana/schema.go - shared YAML helpers; list compilation moved out
  • cabana/http.go - schema response and index handler use the compiled list
  • cabana/contracts.go - D-10 error details for validation
  • pact/capabilities.go - FilterScopes() catalog on FilterScope
  • cabana/testdata/list/all_columns.yaml - column fixture
  • cabana/testdata/list/all_filters.yaml - filter fixture
  • fonoteka.go/plugins/golem15/fonoteka/admin_tracer_test.go - unknown sort expects 422

Decisions Made

  • Omitted sortable means true, matching Winter, except a relation column, which is not a local column and stays unsortable unless YAML sets sortable. An explicit true orders the joined select column, then the primary key.
  • perPageOptions keep source order. recordsPerPage must be one of them. The query rejects any other per_page.
  • toolbar.buttons: list_toolbar is the create action. recordUrl is the update row action. showCheckboxes is bulk delete. Other button names fail activation.
  • The search query parameter is always search. YAML cannot rename it.
  • conditions is rejected with the column-or-scope rule. Scope names must appear in FilterScope.FilterScopes() exactly.
  • D-11 meta is page, per_page, total, last_page. lagoon.Paginate still computes last_page; its current_page is not the admin key.
  • ADMIN-02 stays shared with later plans in this phase, so REQUIREMENTS.md is not marked complete by this plan alone.

Deviations from Plan

Auto-fixed Issues

1. [Rule 2 - Missing Critical] Served the full list schema and validation details

  • Found during: Task 1 and Task 3
  • Issue: The schema handler copied a subset of ListSchema, so new slices would marshal as null, and WriteError could not attach field messages.
  • Fix: The schema handler returns the localized schema. WriteErrorDetails writes D-10 validation_failed details.
  • Files modified: cabana/http.go, cabana/contracts.go
  • Verification: TestListSchemaEmpty and TestListQueryRejectsInjection passed
  • Committed in: aab4398 and 6a57f63

2. [Rule 2 - Missing Critical] Added FilterScopes() to the model capability

  • Found during: Task 2
  • Issue: FilterScope could execute a name but could not declare the finite set, so an arbitrary method could not be rejected without reflection.
  • Fix: FilterScopes() []string is now part of pact.FilterScope. Compilation accepts only those names.
  • Files modified: pact/capabilities.go, cabana/filter_schema.go
  • Verification: TestListSchemaScope and TestListSchemaRejectsRawCondition passed
  • Committed in: d3a9307

3. [Rule 1 - Bug] Stopped treating relation columns as sortable by default

  • Found during: Task 3
  • Issue: Omitted sortable became true for relation columns, while a sort on that key is not a local column.
  • Fix: Relation columns default to not sortable. Explicit sortable: true orders the joined select column and then the primary key.
  • Files modified: cabana/list_schema.go, cabana/query.go
  • Verification: go test ./cabana -run '^(TestListSchema|TestListQuery)' -count=1 passed
  • Committed in: 3efdcc1

4. [Rule 1 - Bug] Updated the genre tracer for unknown sorts

  • Found during: Task 3
  • Issue: TestAdminTracerGenreList expected sort=users.email to return 200. The new contract rejects that identifier.
  • Fix: The persisted genre is still read without a caller sort. The unknown sort expects validation_failed and the body does not echo the identifier.
  • Files modified: fonoteka.go/plugins/golem15/fonoteka/admin_tracer_test.go
  • Verification: go test ./plugins/golem15/fonoteka -run '^TestAdminTracerGenreList$' -count=1 passed
  • Committed in: d8fb9ac (fonoteka.go)

Total deviations: 4 auto-fixed (2 missing critical, 2 bug) Impact on plan: The extra capability and response fields are the locked list contract. No new dependency and no change to the frontend API.

TDD Gate Compliance

Gate Commit Result
RED Task 1 fd591ed TestListSchemaCompile failed because perPageOptions was unknown. TestListSchemaEmpty compared the old four-key JSON.
GREEN Task 1 aab4398 TestListSchema(Compile|Empty|Single|Ordering|Rejects) passed
RED Task 2 cb832eb TestListSchemaFilter failed on unknown field filter
GREEN Task 2 d3a9307 TestListSchema(Filter|Scope|RejectsRawCondition) passed
RED Task 3 7f451c3 TestListQueryContract returned every row for search=Other
GREEN Task 3 6a57f63 TestListQuery(Contract|Empty|Single|Adjacent|Filters|RejectsInjection) passed

Issues Encountered

None.

User Setup Required

None - no external service configuration required.

Next Phase Readiness

Ready for 09-05. List compilation and the index query are reusable by the later controller plans. ADMIN-02 remains pending until every plan that declares it has a summary.

Self-Check: PASSED

  • FOUND: cabana/list_schema.go
  • FOUND: cabana/filter_schema.go
  • FOUND: cabana/query.go
  • FOUND: cabana/testdata/list/all_columns.yaml
  • FOUND: cabana/testdata/list/all_filters.yaml
  • FOUND: fd591ed
  • FOUND: aab4398
  • FOUND: cb832eb
  • FOUND: d3a9307
  • FOUND: 7f451c3
  • FOUND: 6a57f63
  • FOUND: 3efdcc1
  • FOUND: d8fb9ac

RED evidence for TestListSchemaCompile, TestListSchemaFilter, and TestListQueryContract was checked with gsd_run check tdd-red-evidence and returned RED_EVIDENCE_OK before each implementation commit.


Phase: 09-backend-admin-authentication-and-schema-pipeline Completed: 2026-09-24