Files
summercms/.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-05-SUMMARY.md
2026-09-24 19:48:29 +02:00

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 05 admin
cabana
crud
bulk-delete
gorm
mass-assignment
lifecycle
phase provides
09-backend-admin-authentication-and-schema-pipeline compiled form schema, backend permission gate, and D-10 envelopes
Schema-projected create and update through Fill then Validate
Permissioned show, create, update, and delete with scoped lookup
Transactional bulk delete with duplicate, empty, retry, and concurrency rules
09-backend-admin-authentication-and-schema-pipeline
admin-api
phase-10-spa
tokens tasks commits
17783 3 6
added patterns
Writable fields are bound to gorm columns at activation and projected by exact key
Record mutations run Fill, BeforeValidate, Validate, then controller and model hooks in one transaction
Bulk delete locks the scoped set FOR UPDATE and commits every selected row or none
created modified
cabana/crud.go
cabana/crud_test.go
cabana/crud_lifecycle_test.go
cabana/bulk_test.go
cabana/http.go
cabana/registry.go
cabana/contracts.go
pact/capabilities.go
Writable admin fields are bound to gorm columns at activation; id, timestamps, scope, and system flags are never fillable
Show and update use one not-found body for missing and out-of-scope rows; delete of an absent row is deleted 0 and does not run hooks
A bulk selection that matches no scoped row is a no-op; a mixed present and absent selection is a 409 and rolls back
Controller hook failures return an opaque lifecycle error and do not echo the hook text
Pattern: ProjectWritableFields copies only activation bindings, so request key casing and nesting cannot reach Fill
Pattern: bulk ids are parsed, deduped, and sorted before a single locked transaction
ADMIN-04
id description requirement verification human_judgment
D1 Create and update project only schema-writable fields, call Fill then Validate, and return D-10 422 field errors. ADMIN-04
kind ref status
integration cabana/crud_test.go#TestCRUDFillValidate pass
false
id description requirement verification human_judgment
D2 Unknown, cased, nested, and protected keys never change id, timestamps, scope, or system flags. ADMIN-04
kind ref status
unit cabana/crud_test.go#TestCRUDWritableProjection pass
kind ref status
integration cabana/crud_test.go#TestCRUDRejectsProtectedFields pass
false
id description requirement verification human_judgment
D3 A model missing Fillable or Rules, or a Validate provider error, fails closed with the controller id and persists nothing. ADMIN-04
kind ref status
integration cabana/crud_test.go#TestCRUDCapabilityFailure pass
false
id description requirement verification human_judgment
D4 Show, create, update, and delete are mounted behind the backend permission check and use D-10 envelopes. ADMIN-04
kind ref status
integration cabana/crud_lifecycle_test.go#TestCRUDRecordRoutes pass
kind ref status
integration cabana/crud_lifecycle_test.go#TestCRUDPermissions pass
false
id description requirement verification human_judgment
D5 Missing and out-of-scope records share one not-found or deleted-zero body, and in-scope rows stay unchanged. ADMIN-04
kind ref status
integration cabana/crud_lifecycle_test.go#TestCRUDScope pass
false
id description requirement verification human_judgment
D6 Create, update, and delete run Before and After hooks once, in order, inside the transaction. ADMIN-04
kind ref status
integration cabana/crud_lifecycle_test.go#TestCRUDHooks pass
false
id description requirement verification human_judgment
D7 A failing create, update, or delete hook rolls the transaction back and the HTTP body stays opaque. ADMIN-04
kind ref status
integration cabana/crud_lifecycle_test.go#TestCRUDRollback pass
false
id description requirement verification human_judgment
D8 Bulk delete rejects an empty selection, collapses duplicates, and deletes in ascending primary-key order. ADMIN-04
kind ref status
integration cabana/bulk_test.go#TestBulkDeleteEmpty pass
kind ref status
integration cabana/bulk_test.go#TestBulkDeleteDuplicates pass
kind ref status
integration cabana/bulk_test.go#TestBulkDeleteOrder pass
false
id description requirement verification human_judgment
D9 Repeating a completed bulk delete, or naming only out-of-scope rows, returns deleted 0 and does not run hooks. ADMIN-04
kind ref status
integration cabana/bulk_test.go#TestBulkDeleteIdempotent pass
false
id description requirement verification human_judgment
D10 A mixed selection, hook error, or cancellation rolls the whole batch back, and two concurrent deletes remove each row once. ADMIN-04
kind ref status
integration cabana/bulk_test.go#TestBulkDeleteRollback pass
kind ref status
integration cabana/bulk_test.go#TestBulkDeleteConcurrent pass
false
25min 2026-09-24 complete 040f3ef81c 50754808f6

Phase 9 Plan 05: Schema-projected CRUD and atomic bulk delete Summary

Admin create and update fill only activation-bound fields, and bulk delete locks the scoped set so every selected row commits or none do.

Performance

  • Duration: 25 min
  • Started: 2026-09-24T17:20:57Z
  • Completed: 2026-09-24T17:45:40Z
  • Tasks: 3
  • Files modified: 8

Accomplishments

  • ProjectWritableFields keeps only schema fields bound to gorm columns at activation. id, timestamps, scope_id, ownership ids, and system flags never reach lagoon.Fill, even when the JSON key is cased or nested.
  • Create and update run Fill, BeforeValidate, then lagoon.Validate (YAML required merged onto Rules()) before persistence. Validation errors are D-10 validation_failed with field messages. A missing Fillable/Rules method or a Validate provider error does not insert.
  • GET/POST/PUT/DELETE /_admin/api/v1/{vendor}/{plugin}/{controller} record routes sit behind the backend group and protect, so a forbidden caller is 403 before the id or body is read. Show and update of a missing or out-of-scope id share one not_found body. Delete of an absent id returns deleted: 0 and does not run hooks.
  • Create, update, and delete call the matching FormBefore* / FormAfter* hook once around the GORM callbacks, inside one transaction. A hook error rolls back and the response is Server error without the hook text.
  • POST .../bulk-delete rejects an empty ids list with 422, dedupes, sorts by primary key, and locks the scoped rows FOR UPDATE. A wholly absent selection returns deleted: 0. A mixed present/absent selection is 409 and rolls back. Concurrent identical requests delete each row once.

TDD Gate Compliance

Each task has a test(09-05) commit that failed on the named assertion, then a feat(09-05) commit. gsd_run check tdd-red-evidence returned RED_EVIDENCE_OK for TestCRUDFillValidate, TestCRUDRecordRoutes, and TestBulkDeleteEmpty before the matching implementation. No refactor commit was needed.

Task RED GREEN REFACTOR
1 Fill/Validate 1e14da7 578bdc8 —
2 Record lifecycle 94814d9 e3e1c25 —
3 Bulk delete 247c323 5075480 —

Task Commits

Each task was committed atomically. commits: 6 is git rev-list --count from 040f3ef81c199c82beec1ee8d4626aa4f85fe19a to 50754808f61071e23746f3f36dde8a292a18360b.

  1. Task 1: Project writable fields and enforce Fill/Validate (RED) - 1e14da7 (test)
  2. Task 1: Project writable fields and enforce Fill/Validate (GREEN) - 578bdc8 (feat)
  3. Task 2: Wire scoped record CRUD with mandatory lifecycle hooks (RED) - 94814d9 (test)
  4. Task 2: Wire scoped record CRUD with mandatory lifecycle hooks (GREEN) - e3e1c25 (feat)
  5. Task 3: Make bulk deletion deterministic, retry-safe, and atomic (RED) - 247c323 (test)
  6. Task 3: Make bulk deletion deterministic, retry-safe, and atomic (GREEN) - 5075480 (feat)

Plan metadata: included in the docs commit for this summary

Files Created/Modified

  • cabana/crud.go - CRUDService, writable projection, record lifecycle, and locked bulk delete
  • cabana/http.go - show, create, update, delete, and bulk-delete routes after the permission check
  • cabana/registry.go - binds writable fields while compiling a controller
  • cabana/contracts.go - WritableField on the compiled controller
  • cabana/crud_test.go - Fill/Validate, projection, protected fields, and capability tests
  • cabana/crud_lifecycle_test.go - routes, permissions, scope, hooks, and rollback
  • cabana/bulk_test.go - empty, duplicate, order, retry, rollback, and concurrency
  • pact/capabilities.go - FormAfterCreate, FormAfterUpdate, FormBeforeDelete, FormAfterDelete

Decisions Made

  • Activation binds a scalar form field to the gorm column of the same name. Protected columns are omitted from that list rather than copied and then dropped.
  • YAML required: true is appended to model Rules() and never replaces a stricter rule. Validate reads the model after BeforeValidate, so a hook can still clear a value and fail required.
  • Show and update answer missing and out-of-scope ids with the same 404. Delete answers both with deleted: 0 so a retry of a completed delete does not rerun hooks and does not reveal scope.
  • Bulk delete uses ListExtendQuery. If the locked row count is zero, the result is deleted: 0. If it is greater than zero but short of the normalized ids, the response is conflict and the transaction rolls back.
  • Hook and database errors become cabana: controller <id> failed. The HTTP body stays Server error.

Deviations from Plan

Auto-fixed Issues

1. [Rule 2 - Missing Critical] Added the After and delete controller hooks

  • Found during: Task 2 (scoped record lifecycle)
  • Issue: D-13 named FormBeforeCreate and FormBeforeUpdate only. The plan also requires Before/After hooks for create, update, and delete, once each.
  • Fix: Added FormAfterCreate, FormAfterUpdate, FormBeforeDelete, and FormAfterDelete next to the existing pact hooks and call each implemented hook inside the transaction.
  • Files modified: pact/capabilities.go, cabana/crud.go
  • Verification: TestCRUDHooks and TestCRUDRollback
  • Committed in: 94814d9 (interfaces) and e3e1c25 (calls)

2. [Rule 2 - Missing Critical] Stored the writable binding on the compiled controller

  • Found during: Task 1 (Fill/Validate)
  • Issue: The plan's file list did not include contracts.go, but the binding has to survive activation and be readable without reflecting over request keys.
  • Fix: Added Writable []WritableField and fill it from BindWritableFields during compileRegistry.
  • Files modified: cabana/contracts.go, cabana/registry.go
  • Verification: TestCRUDWritableProjection
  • Committed in: 1e14da7 and 578bdc8

Total deviations: 2 auto-fixed (2 missing critical) Impact on plan: Both were required to enforce the mass-assignment and lifecycle contracts. No new route family or dependency.

Issues Encountered

None.

User Setup Required

None - no external service configuration required.

Next Phase Readiness

Ready for 09-06. Record and bulk routes are in place for every compiled controller that exposes NewRecord, Fillable, and Rules. Relation link/unlink and settings upsert are still later plans. ADMIN-04 stays pending in REQUIREMENTS.md because 09-06, 09-07, 09-08, 09-09, 09-10, and 09-12 also declare it.

go test ./cabana -run '^(TestCRUD|TestBulkDelete)' -count=1 passed.

Self-Check: PASSED


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