docs(10.1-03): complete Albums extension proof plan
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
---
|
||||
schema_version: 1
|
||||
open_count: 1
|
||||
open_count: 3
|
||||
waived_count: 0
|
||||
fixed_count: 4
|
||||
total_count: 5
|
||||
last_updated: 2026-09-27T16:22:23.140Z
|
||||
total_count: 7
|
||||
last_updated: 2026-09-29T00:27:33.153Z
|
||||
---
|
||||
|
||||
# Broken Windows Ledger
|
||||
@@ -20,6 +20,8 @@ last_updated: 2026-09-27T16:22:23.140Z
|
||||
| 3 | 10 | stub | admin/src/views/ListView.vue | | read-only tracer list without search, sort, paging or row links; full list screens in 10-03 | fixed | | 2026-09-27T13:36:36.179Z | 2026-09-27T15:01:17.178Z |
|
||||
| 4 | 10 | stub | admin/src/components/form/registry.ts | | relation-manager is not registered; the Collections editors tab renders the unsupported-field box until the relation manager lands in 10-04 | fixed | | 2026-09-27T15:01:12.833Z | 2026-09-27T15:27:25.933Z |
|
||||
| 5 | 10 | deviation | scripts/check-phase10.sh | | check-phase10.sh --go allow-lists exactly the two pre-existing fonoteka parity failures (TestMigrateSeedsCanonicalGenres, TestSchemaMatchesPHPSnapshot; deferred-items.md) and refuses once they pass; fix belongs to a Phase 9 follow-up | open | | 2026-09-27T16:22:23.140Z | |
|
||||
| 6 | 10.1 | stub | plugins/golem15/fonoteka/controllers/albums_admin_controller.go | 71 | fonoteka.go: discogsLookup widget action is a D-02 stub answering fixed fill {year: 1977, format: LP}; Phase 14 replaces it with the Discogs client | open | | 2026-09-29T00:27:32.945Z | |
|
||||
| 7 | 10.1 | stub | plugins/golem15/fonoteka/controllers/albums_admin_controller.go | 82 | fonoteka.go: discogsSync toolbar action is a D-02 stub answering stub_not_implemented and changing nothing; Phase 14 replaces it with the Discogs sync | open | | 2026-09-29T00:27:33.153Z | |
|
||||
|
||||
````json
|
||||
[
|
||||
@@ -87,6 +89,32 @@ last_updated: 2026-09-27T16:22:23.140Z
|
||||
"recorded_at": "2026-09-27T16:22:23.140Z",
|
||||
"resolved_at": null,
|
||||
"milestone": "v1.0"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"kind": "stub",
|
||||
"phase": "10.1",
|
||||
"file": "plugins/golem15/fonoteka/controllers/albums_admin_controller.go",
|
||||
"line": 71,
|
||||
"description": "fonoteka.go: discogsLookup widget action is a D-02 stub answering fixed fill {year: 1977, format: LP}; Phase 14 replaces it with the Discogs client",
|
||||
"status": "open",
|
||||
"reason": "",
|
||||
"recorded_at": "2026-09-29T00:27:32.945Z",
|
||||
"resolved_at": null,
|
||||
"milestone": "v1.0"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"kind": "stub",
|
||||
"phase": "10.1",
|
||||
"file": "plugins/golem15/fonoteka/controllers/albums_admin_controller.go",
|
||||
"line": 82,
|
||||
"description": "fonoteka.go: discogsSync toolbar action is a D-02 stub answering stub_not_implemented and changing nothing; Phase 14 replaces it with the Discogs sync",
|
||||
"status": "open",
|
||||
"reason": "",
|
||||
"recorded_at": "2026-09-29T00:27:33.153Z",
|
||||
"resolved_at": null,
|
||||
"milestone": "v1.0"
|
||||
}
|
||||
]
|
||||
````
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
---
|
||||
phase: 10.1-runtime-admin-extension-point
|
||||
plan: 03
|
||||
subsystem: admin
|
||||
tags: [go, fonoteka, cabana, custom-elements, html-template, lagoon, admin, discogs-stub]
|
||||
|
||||
requires:
|
||||
- phase: 10.1-runtime-admin-extension-point
|
||||
provides: "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"
|
||||
provides:
|
||||
- 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
|
||||
affects: [10.1-04 unit tests and gate, 14 Discogs client]
|
||||
|
||||
actuals:
|
||||
tokens: 15900
|
||||
tasks: 3
|
||||
commits: 4
|
||||
plan_head_before: 720ee5796b617a2ae48460ca85a796baf868b5a7
|
||||
plan_head_after: c3efbc3428654e05ce13719e3ea4b308bfdc94fa
|
||||
fonoteka_head_before: be3fbf4d23315e10abe37ef405400e7907ae33fa
|
||||
fonoteka_head_after: 9868a0a26957d4657e7084755655dd3a6f6c8f11
|
||||
|
||||
tech-stack:
|
||||
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"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- ../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
|
||||
modified:
|
||||
- ../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
|
||||
|
||||
key-decisions:
|
||||
- "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"
|
||||
|
||||
patterns-established:
|
||||
- "Application extension surfaces get a smoke test through the assembled router at the admin prefix with a real cookie admin and a collection binding"
|
||||
|
||||
requirements-completed: [ADMIN-07]
|
||||
|
||||
coverage:
|
||||
- id: D1
|
||||
description: "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"
|
||||
requirement: ADMIN-07
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "../fonoteka.go/plugins/golem15/fonoteka/admin_phase101_smoke_test.go#TestPhase101AlbumsSmoke/stats"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D2
|
||||
description: "Albums form year field and Load from Discogs widget whose stub action fills year 1977 and format LP without saving; a save persists both"
|
||||
requirement: ADMIN-07
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "../fonoteka.go/plugins/golem15/fonoteka/admin_phase101_smoke_test.go#TestPhase101AlbumsSmoke/widget"
|
||||
status: pass
|
||||
- kind: unit
|
||||
ref: "modules/lagoon/fill_test.go#TestFillJSONNumber"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D3
|
||||
description: "Controller JS and CSS served from the embedded AdminFS with JS/CSS content types, nosniff and an ETag; undeclared plugin files are 404"
|
||||
requirement: ADMIN-07
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "../fonoteka.go/plugins/golem15/fonoteka/admin_phase101_smoke_test.go#TestPhase101AlbumsSmoke/assets"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D4
|
||||
description: "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"
|
||||
requirement: ADMIN-07
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "../fonoteka.go/plugins/golem15/fonoteka/admin_phase101_smoke_test.go#TestPhase101AlbumsSmoke/toolbar"
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "../fonoteka.go/plugins/golem15/fonoteka/admin_phase10_copy_test.go#TestPhase10ControllerCopy"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D5
|
||||
description: "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"
|
||||
verification: []
|
||||
human_judgment: true
|
||||
rationale: "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"
|
||||
|
||||
duration: 11min
|
||||
completed: 2026-09-29
|
||||
status: 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*
|
||||
Reference in New Issue
Block a user