Files
summercms/.planning/phases/10.1-runtime-admin-extension-point/10.1-03-SUMMARY.md
2026-09-29 02:28:40 +02:00

13 KiB

phase, plan, subsystem, tags, requires, provides, affects, actuals, plan_head_before, plan_head_after, fonoteka_head_before, fonoteka_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 fonoteka_head_before fonoteka_head_after tech-stack key-files key-decisions patterns-established requirements-completed coverage duration completed status
10.1-runtime-admin-extension-point 03 admin
go
fonoteka
cabana
custom-elements
html-template
lagoon
admin
discogs-stub
phase provides
10.1-runtime-admin-extension-point 10.1-01 pact AdminClientAssets/HasAdminActions/AdminPartialData, widget/toolbar/partial routes, plugin asset route; 10.1-02 SPA widget host, header partial host, toolbar actions, partial style kit
Albums statistics strip (headerPartial stats, controllers/albums/_stats.htm) scoped by scopeAlbums
albumsAdminController.PartialData, AdminJS, AdminCSS, AdminActions with albumsStatsView/albumsStatItem view models
Albums form year field and discogs widget field (golem15-fonoteka-discogs-lookup, action discogsLookup, fill [year, format])
discogsLookup and discogsSync stub actions gated by golem15.fonoteka.access_albums
plain-JS custom element assets/js/discogs-lookup.js and assets/css/albums.css embedded in the plugin AdminFS
en and pl copy for item.year, stats.*, discogs.lookup_*, discogs.sync_button, discogs.stub_*
TestPhase101AlbumsSmoke (stats, widget, assets, toolbar)
lagoon.Fill converts json.Number into integer, unsigned and float fields
10.1-04 unit tests and gate
14 Discogs client
tokens tasks commits
15900 3 4
720ee5796b c3efbc3428 be3fbf4d23315e10abe37ef405400e7907ae33fa 9868a0a26957d4657e7084755655dd3a6f6c8f11
added patterns
A header partial's view model is a curated struct of labels and integers; each count query is built fresh from db.WithContext(ctx).Model(...) and passed through the controller's scope
Application widget elements are plain JS custom elements with one light-DOM button; text comes only from the SPA's label and busy-label attributes and the element only dispatches summer-action
Discogs actions reuse the existing Winter permission instead of adding permission codes
created modified
../fonoteka.go/plugins/golem15/fonoteka/controllers/albums/_stats.htm
../fonoteka.go/plugins/golem15/fonoteka/assets/js/discogs-lookup.js
../fonoteka.go/plugins/golem15/fonoteka/assets/css/albums.css
../fonoteka.go/plugins/golem15/fonoteka/admin_phase101_smoke_test.go
../fonoteka.go/plugins/golem15/fonoteka/controllers/albums_admin_controller.go
../fonoteka.go/plugins/golem15/fonoteka/controllers/albums/config_list.yaml
../fonoteka.go/plugins/golem15/fonoteka/models/album/fields.yaml
../fonoteka.go/plugins/golem15/fonoteka/admin.go
../fonoteka.go/plugins/golem15/fonoteka/lang/en/lang.yaml
../fonoteka.go/plugins/golem15/fonoteka/lang/pl/lang.yaml
../fonoteka.go/plugins/golem15/fonoteka/admin_albums_test.go
../fonoteka.go/plugins/golem15/fonoteka/admin_phase10_controllers_test.go
../fonoteka.go/plugins/golem15/fonoteka/admin_phase10_copy_test.go
modules/lagoon/fill.go
modules/lagoon/fill_test.go
modules/lagoon/README.md
lagoon.Fill converts json.Number into numeric fields; a fraction or an overflow into an integer field is an error, not a silent truncation
The stats template trims whitespace between items with {{- -}}, so the node tree carries no whitespace-only text nodes between stats
The discogs widget sends only its fill keys; the stub ignores them and the record, so the create form (record-id empty) gets the same answer
Application extension surfaces get a smoke test through the assembled router at the admin prefix with a real cookie admin and a collection binding
ADMIN-07
id description requirement verification human_judgment
D1 Albums list header partial: a statistics strip with the total, per-format counts in getFormatOptions order and No shelf, counted only inside the admin's collection ADMIN-07
kind ref status
integration ../fonoteka.go/plugins/golem15/fonoteka/admin_phase101_smoke_test.go#TestPhase101AlbumsSmoke/stats pass
false
id description requirement verification human_judgment
D2 Albums form year field and Load from Discogs widget whose stub action fills year 1977 and format LP without saving; a save persists both ADMIN-07
kind ref status
integration ../fonoteka.go/plugins/golem15/fonoteka/admin_phase101_smoke_test.go#TestPhase101AlbumsSmoke/widget pass
kind ref status
unit modules/lagoon/fill_test.go#TestFillJSONNumber pass
false
id description requirement verification human_judgment
D3 Controller JS and CSS served from the embedded AdminFS with JS/CSS content types, nosniff and an ETag; undeclared plugin files are 404 ADMIN-07
kind ref status
integration ../fonoteka.go/plugins/golem15/fonoteka/admin_phase101_smoke_test.go#TestPhase101AlbumsSmoke/assets pass
false
id description requirement verification human_judgment
D4 Sync with Discogs toolbar action: localized label, stub message with an empty fill, CSRF header required, Genres-only admin gets 403 on toolbar, widget and partial ADMIN-07
kind ref status
integration ../fonoteka.go/plugins/golem15/fonoteka/admin_phase101_smoke_test.go#TestPhase101AlbumsSmoke/toolbar pass
kind ref status
integration ../fonoteka.go/plugins/golem15/fonoteka/admin_phase10_copy_test.go#TestPhase10ControllerCopy pass
false
id description verification human_judgment rationale
D5 In a real browser: plugin module script loads under CSP, the element upgrades, the widget turns busy and fills the form, the strip and buttons wrap at 768px with pl labels, and the Albums stylesheet is off on other controllers
true CSP enforcement, module loading, custom-element upgrade and the 768px layout backstops need a real browser (plan Task 3 human-check, UI-SPEC backstop rows); collected at /gsd-verify-work
11min 2026-09-29 complete

Phase 10.1 Plan 03: Albums extension proof Summary

Płytarium Albums now uses all three extension surfaces. A server-rendered statistics strip above the list is counted through scopeAlbums. A plain-JS "Load from Discogs" form widget has a stub action that fills Release year 1977 and Format LP into the unsaved form. A "Sync with Discogs" toolbar stub shows a test-mode toast. Both Discogs actions require golem15.fonoteka.access_albums, and there is pl and en copy for everything.

Performance

  • Duration: 11 min
  • Started: 2026-09-29T00:17:03Z
  • Completed: 2026-09-29T00:27:41Z
  • Tasks: 3
  • Files modified: 13 in fonoteka.go, 3 in summercms.go

Accomplishments

  • albumsAdminController.PartialData("stats") returns an albumsStatsView built from three separate queries, each scoped by scopeAlbums: the total, a grouped per-format count and a no-shelf count. _stats.htm renders it with the .summer-stats kit classes only. With 0 albums it shows "All albums 0". The smoke test proves that five albums in a second collection are never counted.
  • fields.yaml gains year (number, span right, next to shelf) and a last, full-width discogs widget field. AdminJS/AdminCSS declare the element and stylesheet, which are embedded in AdminFS and served under /plytadmin/assets/golem15/fonoteka/.
  • discogs-lookup.js defines one light-DOM button and handles busy/busy-label/aria-busy. It dispatches summer-action and contains no import, no string, no network call and no cookie or storage access. albums.css follows the S3 contract and uses only --c-* variables.
  • toolbar.buttons: [create, delete, discogsSync] with the discogsSync stub. The smoke test checks the localized toolbarActions label in en and pl, the stub answer with fill: {}, 403 when the CSRF header is missing, and 403 for a Genres-only admin on the toolbar, widget and partial routes.
  • The Phase 10 pins moved in the same commits: Albums field count 7, widget in phase10BuiltinFieldTypes, and the Albums toolbar compared per controller.

Task Commits

fonoteka.go:

  1. Task 1: statistics strip - e9b1373 (feat)
  2. Task 2: Load from Discogs widget - fbc5692 (feat)
  3. Task 3: Sync with Discogs toolbar action - 9868a0a (feat)

summercms.go:

  • c3efbc3 fix(10.1-03): fill numeric model fields from JSON numbers (deviation 1, found in Task 2)

Commit count: 4 plan commits (1 in summercms.go, 3 in fonoteka.go). In summercms.go, git rev-list --count 720ee57..c3efbc3 is 2 because a concurrent session committed 9d23ac5 docs(11.2): capture phase context in that range. That commit does not belong to this plan.

Files Created/Modified

  • controllers/albums_admin_controller.go: stats view model, PartialData, AdminJS, AdminCSS, AdminActions (two stubs), compile-time pact assertions
  • controllers/albums/_stats.htm: the strip template
  • controllers/albums/config_list.yaml: headerPartial: stats, the discogsSync toolbar button
  • models/album/fields.yaml: year and the discogs widget
  • assets/js/discogs-lookup.js, assets/css/albums.css: element and styles
  • admin.go: embed list
  • lang/{en,pl}/lang.yaml: item.year, the stats group and the discogs.* additions
  • admin_phase101_smoke_test.go: TestPhase101AlbumsSmoke
  • admin_albums_test.go, admin_phase10_controllers_test.go, admin_phase10_copy_test.go: shape pins
  • modules/lagoon/fill.go, fill_test.go, README.md (summercms.go): json.Number conversion

Decisions Made

  • lagoon.Fill parses json.Number for integer, unsigned and float fields. A fraction or an overflow into an integer field is an error. String fields keep the number's text through the existing conversion.
  • The stats template uses {{- -}} trimming so no whitespace-only text nodes appear between items. Only the file's trailing newline stays as a root text node, and the smoke test ignores it.
  • The Discogs actions use the literal golem15.fonoteka.access_albums, the same code the controller requires, and no permission is added to the catalog.

Deviations from Plan

Auto-fixed Issues

1. [Rule 1 - Bug] Saving a type: number field into an integer model column was a 500

  • Found during: Task 2 (smoke widget subtest, PUT with year: 1977)
  • Issue: cabana decodes save bodies with UseNumber, so year arrives as json.Number, a string kind. lagoon.Fill could not convert it to *int, so CapabilityError produced a 500. No earlier form had a number field backed by an integer column, so the SPA's Save of Release year would have failed.
  • Fix: convertNumber in modules/lagoon/fill.go, with TestFillJSONNumber and one README sentence (CLAUDE.md module README rule).
  • Files modified: modules/lagoon/fill.go, modules/lagoon/fill_test.go, modules/lagoon/README.md (summercms.go)
  • Verification: go vet ./... && go test ./... in summercms.go is green, and TestPhase101AlbumsSmoke/widget persists year 1977 and format LP.
  • Committed in: c3efbc3 (summercms.go)

Total deviations: 1 auto-fixed (1 bug) Impact on plan: This was needed for the plan's "a PUT carrying year 1977 and format LP persists both" and for the human check's save-and-reload step. It is a framework change outside the plan's fonoteka-only file list, but it contains nothing application-specific, and the hygiene gate passes.

Issues Encountered

None beyond the deviation.

Known Stubs

File Line Stub Resolved by
../fonoteka.go/plugins/golem15/fonoteka/controllers/albums_admin_controller.go 71 discogsLookup answers the fixed fill {year: 1977, format: "LP"} and makes no Discogs call (D-02, intentional) Phase 14
../fonoteka.go/plugins/golem15/fonoteka/controllers/albums_admin_controller.go 82 discogsSync answers stub_not_implemented and changes nothing (D-02, intentional) Phase 14

Both are recorded in .planning/WINDOWS.md as entries 6 and 7.

User Setup Required

None. No external service configuration required.

Next Phase Readiness

  • Plan 10.1-04 can add the named unit tests and the phase gate. Its two-collection acceptance test can build on phase101Album and phase101Stats in the smoke test.
  • The browser check from Task 3 is still outstanding (D5 above): summer serve in fonoteka.go, Albumy in pl at full width and at 768px, the widget on create and update, the toolbar toast, and Gatunki without the Albums stylesheet.

Self-Check: PASSED

  • FOUND: controllers/albums/_stats.htm, assets/js/discogs-lookup.js, assets/css/albums.css, admin_phase101_smoke_test.go
  • FOUND commits: e9b1373, fbc5692, 9868a0a (fonoteka.go); c3efbc3 (summercms.go)
  • Plan verification: go vet ./... ./plugins/golem15/fonoteka/... and go test ./... in fonoteka.go, go vet ./... && go test ./... in summercms.go, scripts/check-phase10.sh --hygiene ("phase10 hygiene passed"), and every task's acceptance greps: all pass.

Phase: 10.1-runtime-admin-extension-point Completed: 2026-09-29