From 63bcbc31b0dabc5fc80b50cdbb2213e440798596 Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Wed, 30 Sep 2026 21:10:08 +0200 Subject: [PATCH] docs(11-08): complete gap plan summary --- .planning/REQUIREMENTS.md | 4 +- .planning/ROADMAP.md | 7 +- .planning/STATE.md | 29 ++- .planning/milestone.lock | 8 +- .../11-08-SUMMARY.md | 240 ++++++++++++++++++ .planning/state.json | 13 +- 6 files changed, 276 insertions(+), 25 deletions(-) create mode 100644 .planning/phases/11-jobs-realtime-and-search-infrastructure/11-08-SUMMARY.md diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 46b25ef..387a28b 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -97,7 +97,7 @@ Requirements for v1 (the Płytarium port). Each maps to roadmap phases. "User" b ### Search (SRCH) -- [ ] **SRCH-01**: Album documents sync to Typesense scoped by collection_id, behind a settings kill-switch, degrading gracefully when the DB or config is absent +- [x] **SRCH-01**: Album documents sync to Typesense scoped by collection_id, behind a settings kill-switch, degrading gracefully when the DB or config is absent - [ ] **SRCH-02**: The reindex command asserts zero documents with collection_id 0 before and after, and can drop the legacy index ### Integrations (INTG) @@ -227,7 +227,7 @@ Which phases cover which requirements. Updated during roadmap creation. | RT-01 | Phase 11 | Gaps Found | | RT-02 | Phase 11 | Gaps Found | | RT-03 | Phase 11 | Gaps Found | -| SRCH-01 | Phase 11 | Gaps Found | +| SRCH-01 | Phase 11 | Complete | | SRCH-02 | Phase 14 | Pending | | INTG-01 | Phase 14 | Pending | | INTG-02 | Phase 14 | Pending | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 3e97586..821b0d5 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -505,7 +505,7 @@ Plans: 4. A channel-namespace authorizer registry re-validates on every subscribe; a broadcastable model interface with bulk-write suppression emits exactly one summary event for a bulk operation. 5. Typesense sync is scoped by `collection_id` behind a settings kill-switch and degrades gracefully without DB/config. -**Plans:** 7/7 plans executed +**Plans:** 8/8 plans executed **Research flag:** yes Plans: @@ -527,6 +527,9 @@ Plans: **Wave 5** *(blocked on Wave 4 completion)* - [x] 11-07-PLAN.md — Unit tests last: full coverage, failing-when-broken T-11 evidence, check-phase11.sh gate, security review and validation map +**Wave 6** *(gap closure, blocked on Wave 5 completion)* +- [x] 11-08-PLAN.md — CR-01: Cabana writes and SaveAlbum in lagoon.Transaction, lagoon.AfterCommit refuses foreign transactions, committed artist_ids regression tests (summercms.go, fonoteka.go) + ### Phase 11.1: SummerCMS documentation for humans and AI agents (INSERTED) **Goal:** SummerCMS has a WinterCMS-style documentation set that serves both humans and AI agents. The Markdown source lives in `summercms.go/docs/` and a `summer` CLI command builds it into a static site with sidebar navigation, search, `llms.txt`/`llms-full.txt` and a raw `.md` per page. Every code example compiles and is tested, and a "Coming from WinterCMS" map plus an `acme/blog` porting walkthrough cover the migration path. It documents the framework as it stands after Phase 11 and never names a consuming application. @@ -670,7 +673,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 → | 8. OAuth2.1 authorization server | 10/10 | Complete | 2026-09-23 | | 9. Backend admin authentication and schema pipeline | 12/12 | In Progress| | | 10. Admin Vue SPA | 5/5 | Complete | 2026-09-27 | -| 11. Jobs, realtime and search infrastructure | 7/7 | In Progress| | +| 11. Jobs, realtime and search infrastructure | 8/8 | In Progress| | | 11.1. SummerCMS documentation for humans and AI agents | 0/TBD | Not started | - | | 11.2. Ready to share: summercms.io website and newsletter plugin | 0/TBD | Not started | - | | 12. Płytarium API — Collections and Albums | 0/TBD | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index aa15245..f5101db 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,17 +3,17 @@ gsd_state_version: "1.0" milestone: v1.0 current_phase: 11 current_phase_name: Jobs, realtime and search infrastructure -status: executing -stopped_at: Completed 11-07-PLAN.md -last_updated: "2026-09-30T14:09:27.946Z" -last_activity: 2026-09-29 +status: verifying +stopped_at: Completed 11-08-PLAN.md +last_updated: "2026-09-30T19:09:44.022Z" +last_activity: 2026-09-30 last_activity_desc: Phase 11 execution started -state_head: dd97fd12a60a31067c6a0d254161d31e36b90c6c +state_head: e6777bda9765d792268069432220b32d394c7bc8 progress: total_phases: 19 completed_phases: 9 - total_plans: 86 - completed_plans: 85 + total_plans: 92 + completed_plans: 86 milestone_name: milestone --- @@ -28,10 +28,10 @@ See: .planning/PROJECT.md (updated 2026-09-16) ## Current Position -Phase: 11 (Jobs, realtime and search infrastructure) — READY TO EXECUTE -Plan: 7 of 7 -Status: Ready to execute -Last activity: 2026-09-29 — Phase 11 execution started +Phase: 11 (Jobs, realtime and search infrastructure) — EXECUTING +Plan: 8 of 8 +Status: Phase complete — ready for verification +Last activity: 2026-09-30 — Phase 11 execution started Progress: [██████░░░░] 60% @@ -140,6 +140,7 @@ Progress: [██████░░░░] 60% | Phase 11 P06 | 23 min | 3 tasks | 39 files | | Phase 11 P04 | 17 min | 2 tasks | 19 files | | Phase 11 P07 | 59min | 3 tasks | 40 files | +| Phase 11 P08 | unknown (manual close-out) | 3 tasks | 13 files | ## Accumulated Context @@ -385,6 +386,8 @@ Recent decisions affecting current work: - [Phase 11]: Savepoint helpers (beachcomber, lighthouse) roll back when RELEASE fails, because a swallowed read error still aborts the Postgres transaction - [Phase 11]: lagoon after-commit callbacks always receive a clean statement on the write's connection; side-effect GORM callbacks declare Before(gorm:commit_or_rollback_transaction) - [Phase 11]: Phase gates accept a pending skip only by exact test name and pending text (TestBroadcastGoldens/created and /updated until Phase 12), in check-phase11.sh and check-phase10.1.sh +- [Phase 11]: Phase 11-08: lagoon.AfterCommit inside a foreign plain GORM transaction warns and skips the callback instead of running it immediately; Cabana writes and SaveAlbum run in lagoon.Transaction — Lagoon cannot observe a foreign commit; running early sent uncommitted, pre-pivot or rolled-back Album state to Typesense (CR-01) +- [Phase 11]: Phase 11-08: a nested lagoon.Transaction given a root handle returns an error (WR-03) — Treating it as a savepoint would commit independently while its callbacks waited on the parent buffer ### Pending Todos @@ -415,6 +418,6 @@ Items acknowledged and carried forward from previous milestone close: ## Session Continuity -Last session: 2026-09-30T12:57:09.320Z -Stopped at: Completed 11-07-PLAN.md +Last session: 2026-09-30T19:09:33.936Z +Stopped at: Completed 11-08-PLAN.md Resume file: None diff --git a/.planning/milestone.lock b/.planning/milestone.lock index 26a8926..75c2830 100644 --- a/.planning/milestone.lock +++ b/.planning/milestone.lock @@ -1,6 +1,6 @@ { - "phase": "09", - "session": "codex-thread-id-01a0d740-5347-7f00-9bb6-24c3bbe01139", - "pid": 30, - "updated_at": 1790317872927 + "phase": "11", + "session": "codex-thread-id-01a0f286-f1a5-7ce0-b0b8-102d6d67e017", + "pid": 2, + "updated_at": 1790778676127 } diff --git a/.planning/phases/11-jobs-realtime-and-search-infrastructure/11-08-SUMMARY.md b/.planning/phases/11-jobs-realtime-and-search-infrastructure/11-08-SUMMARY.md new file mode 100644 index 0000000..42a6ad7 --- /dev/null +++ b/.planning/phases/11-jobs-realtime-and-search-infrastructure/11-08-SUMMARY.md @@ -0,0 +1,240 @@ +--- +phase: 11-jobs-realtime-and-search-infrastructure +plan: 08 +subsystem: database +tags: [lagoon, after-commit, cabana, beachcomber, typesense, gorm, transactions, gap-closure] + +requires: + - phase: 11-jobs-realtime-and-search-infrastructure + provides: "11-05 beachcomber sync over lagoon.AfterCommit and the fonoteka Album binding; 11-07 clean after-commit handles, the savepoint fix and check-phase11.sh" +provides: + - "Cabana create/update/delete/bulk-delete and relation Link/Unlink run in lagoon.Transaction, so after-commit work sees the committed row and pivots" + - "fonoteka SaveAlbum runs in lagoon.Transaction; the buffered search reload happens after the ordered artist pivots commit" + - "lagoon.AfterCommit warns and skips inside a foreign plain GORM *sql.Tx; a nested lagoon.Transaction over a root handle returns an error (WR-03)" + - "Regression tests: TestAlbumsAdminSearchUsesCommittedArtists (assembled admin router) and TestSaveAlbumDefersAfterCommitUntilArtistsSync; inverted plain-transaction tests in lagoon, beachcomber and fonoteka" + - "T-11-31 and T-11-32 in 11-SECURITY-REVIEW.md, with removal check RC-14 in check-phase11.sh --removal" +affects: [11 verification re-run (CR-01), 12 albums API (SearchIDs SQL re-gate), any code that writes searchable models inside its own plain GORM transaction] + +actuals: + tokens: 5988 + tasks: 3 + commits: 7 +plan_head_before: 9033d81721d1a0fa77fd9ce234119948aa3d0996 +plan_head_after: e6777bda9765d792268069432220b32d394c7bc8 +# commits is `git rev-list --count 9033d81..e6777bd` in summercms.go. It +# includes 6f57604 docs(11.1), an unrelated commit that landed between the +# 11-08 task commits; the 11-08 commits there are 6. fonoteka.go received 1 +# more commit on top of c222e030b38a6e63760fbc032c02d2224a2c7b41: 1c88199. + +tech-stack: + added: [] + patterns: + - "Write services that trigger after-commit work run their whole unit (row plus pivots) in lagoon.Transaction and use the callback's ctx and tx throughout" + - "lagoon.AfterCommit has four documented cases: lagoon-managed (buffered), implicit single statement (buffered on the statement), foreign *sql.Tx (warned and skipped), outside a transaction (immediate)" + +key-files: + created: + - .planning/phases/11-jobs-realtime-and-search-infrastructure/11-08-SUMMARY.md + modified: + - modules/cabana/crud.go + - modules/cabana/relation.go + - modules/lagoon/transaction.go + - modules/lagoon/transaction_test.go + - modules/lagoon/README.md + - modules/beachcomber/sync_test.go + - modules/beachcomber/README.md + - scripts/check-phase11.sh + - .planning/phases/11-jobs-realtime-and-search-infrastructure/11-SECURITY-REVIEW.md + - ../fonoteka.go/plugins/golem15/fonoteka/admin_albums_test.go + - ../fonoteka.go/plugins/golem15/fonoteka/search_smoke_test.go + - ../fonoteka.go/plugins/golem15/fonoteka/classes/album_write_service.go + - ../fonoteka.go/plugins/golem15/fonoteka/classes/album_write_service_db_test.go + +key-decisions: + - "lagoon.AfterCommit inside a foreign plain GORM transaction warns and skips the callback instead of running it immediately: Lagoon cannot observe that commit, and running early leaked uncommitted or rolled-back state to Typesense" + - "A nested lagoon.Transaction given a root handle returns an error rather than being treated as a top-level transaction (WR-03 option 'return an error')" + - "T-11-31 gets its own removal check (RC-14) so check-phase11.sh --evidence accepts the 11-08 threat register; T-11-32 (medium) is proven by two hand-run mutations recorded in the review" + +patterns-established: + - "A gap plan that adds threat IDs must also add their rows to the phase security review, because the phase gate reads every 11-0*-PLAN.md threat register" + +requirements-completed: [SRCH-01] + +coverage: + - id: D1 + description: "A Cabana admin edit that replaces an Album's artists commits the submitted pivot order and sends Typesense exactly one import whose artist_ids equal that committed order" + requirement: SRCH-01 + verification: + - kind: integration + ref: "../fonoteka.go/plugins/golem15/fonoteka/admin_albums_test.go#TestAlbumsAdminSearchUsesCommittedArtists" + status: pass + - kind: integration + ref: "go test ./modules/cabana -count=1" + status: pass + human_judgment: false + - id: D2 + description: "lagoon.AfterCommit refuses a foreign plain GORM transaction with a warning; a rolled-back plain transaction reaches no search engine; lagoon-managed and implicit single-statement commits still sync; a nested lagoon.Transaction over a root handle errors without running" + requirement: SRCH-01 + verification: + - kind: integration + ref: "go test ./modules/lagoon -run '^(TestTransactionAfterCommit|TestTransactionEdges)$' -count=1 -v" + status: pass + - kind: integration + ref: "go test ./modules/beachcomber -run '^TestSyncAfterCommit$' -count=1 -v" + status: pass + - kind: integration + ref: "../fonoteka.go/plugins/golem15/fonoteka/search_smoke_test.go#TestAlbumSearchDeleteAndFailures" + status: pass + - kind: other + ref: "PHASE11_RC=RC-14 scripts/check-phase11.sh --removal" + status: pass + human_judgment: false + - id: D3 + description: "SaveAlbum runs after-commit callbacks only after the ordered artist pivots commit, and none on rollback; validation and market-price provenance tests unchanged and green" + requirement: SRCH-01 + verification: + - kind: integration + ref: "cd ../fonoteka.go && go test ./plugins/golem15/fonoteka/classes -run '^TestSaveAlbum' -count=1 -v" + status: pass + human_judgment: false + - id: D4 + description: "Phase 11 gate green in both repositories with the 11-08 threats in the security review" + verification: + - kind: other + ref: "bash scripts/check-phase11.sh --all" + status: pass + human_judgment: false + +duration: unknown (executor stopped before its summary step; see Performance) +completed: 2026-09-30 +status: complete +--- + +# Phase 11 Plan 08: Commit-safe Album search sync (gap CR-01) Summary + +**Cabana writes and fonoteka `SaveAlbum` now run in `lagoon.Transaction`, so Typesense receives an Album only after its row and ordered artist pivots commit. `lagoon.AfterCommit` warns and skips inside a foreign plain GORM transaction, so a rollback there never reaches the search engine. A nested `lagoon.Transaction` over a root handle is an error. `check-phase11.sh --all` prints "phase11 all passed".** + +This summary was written in a manual close-out. The 11-08 executor committed all three tasks and then stopped before its summary step. The close-out re-ran every verify command, the phase gate and both repositories' full suites. It fixed the gaps it found in three separate `fix(11-08)` commits. + +## Performance + +- **Duration:** not measured. The executor recorded no start time or commit ledger. Its task commits are timestamped 2026-09-30T18:14:23Z to 18:14:42Z. The follow-up test commit is at 18:49:31Z and the close-out ran from about 18:50Z. +- **Completed:** 2026-09-30 +- **Tasks:** 3 of 3 +- **Files modified:** 13 (9 in summercms.go including the security review, 4 in fonoteka.go), plus this summary and the planning state files + +## Accomplishments + +- **Task 1 (tracer): an admin artist edit indexes the committed ordered pivots.** `CRUDService.save`, `Delete` and `BulkDelete` (crud.go) and `RelationService.Link` and `Unlink` (relation.go) call `lagoon.Transaction` and use its ctx and tx. The read-only `ShowRecord` (crud.go:255) and relation query (relation.go:449) transactions are unchanged. `TestAlbumsAdminSearchUsesCommittedArtists` drives the router built by `surf.Assemble` with a PUT that replaces the artists. It asserts that the committed pivot order and the single Typesense import's `artist_ids` are the same. +- **Task 2: refuse external after-commit work inside unmanaged transactions.** `AfterCommit` keeps its lagoon-buffer and implicit-transaction branches. When the statement's connection pool is a `gorm.TxCommitter` that neither branch covers, it now logs `lagoon: after-commit callback skipped inside unmanaged transaction` and returns. `Transaction` returns `lagoon: nested transaction requires the parent transaction handle` when a parent buffer exists but the handle is not transactional. Tests that used to expect immediate plain-transaction sync now assert the opposite in lagoon, beachcomber and fonoteka. The lagoon README describes all four cases. +- **Task 3: SaveAlbum defers sync until the ordered artists commit.** `SaveAlbum` uses `lagoon.Transaction`, and the save-before-pivot order is unchanged. `TestSaveAlbumDefersAfterCommitUntilArtistsSync` registers a create callback that reads the pivots after commit and sees `[second, first]`. Its duplicate-pivot rollback case runs no callback and commits no Album. + +## Task Commits + +summercms.go: +1. `f7b6b0c`: fix(11-08), make cabana writes commit-safe (Task 1) +2. `a33b1ad`: fix(11-08), refuse unmanaged after-commit work (Task 2) +3. `2766f34`: test(11-08), keep sync failure coverage managed (Task 2 follow-up: `TestSyncFailuresNonFatal/failed_document_read_inside_a_transaction` moved to `lagoon.Transaction`; with `a33b1ad` alone that test fails) +4. `8e0083e`: fix(11-08), document foreign and nested transaction refusal (close-out) +5. `d4fc957`: fix(11-08), add the T-11-31 removal check to the phase gate (close-out) +6. `e6777bd`: fix(11-08), record T-11-31 and T-11-32 in the security review (close-out) + +fonoteka.go: +1. `1c88199`: fix(11-08), defer album sync until committed (Tasks 1 and 3 tests, Task 2 fonoteka test, `SaveAlbum`) + +Not part of 11-08: `6f57604` docs(11.1): create phase plan, which landed between the task commits. + +**Plan metadata:** docs(11-08): complete gap plan summary (this commit) + +## Files Created/Modified + +See `key-files` in the frontmatter. The production changes are `modules/cabana/crud.go`, `modules/cabana/relation.go`, `modules/lagoon/transaction.go` and `../fonoteka.go/plugins/golem15/fonoteka/classes/album_write_service.go`. The rest are tests, the lagoon and beachcomber READMEs, the gate script and the security review. + +## Verification (run in the close-out, 2026-09-30) + +Postgres-backed tests ran on testcontainers (Docker). Nothing was skipped except the two broadcast goldens that the gate allows (`TestBroadcastGoldens/created` and `/updated`, pending Phase 12). + +| Command | Result | +|---------|--------| +| Task 1: `go test ./modules/cabana -count=1` | `ok .../modules/cabana 17.754s` | +| Task 1: `(cd ../fonoteka.go && go test ./plugins/golem15/fonoteka -run '^TestAlbumsAdminSearchUsesCommittedArtists$' -count=1 -v)` | `--- PASS: TestAlbumsAdminSearchUsesCommittedArtists (0.86s)` | +| Task 2: `go test ./modules/lagoon -run '^(TestTransactionAfterCommit\|TestTransactionEdges)$' -count=1 -v` | `--- PASS: TestTransactionAfterCommit (0.13s)`, `--- PASS: TestTransactionEdges (0.08s)` | +| Task 2: `go test ./modules/beachcomber -run '^TestSyncAfterCommit$' -count=1 -v` | PASS, 6 subtests including `plain_gorm_transaction_rollback_sends_nothing` | +| Task 2: `(cd ../fonoteka.go && go test ./plugins/golem15/fonoteka -run '^TestAlbumSearchDeleteAndFailures$' -count=1 -v)` | PASS, 10 subtests including `a_foreign_gorm_transaction_rollback_never_reaches_Typesense` | +| Task 3: `(cd ../fonoteka.go && go test ./plugins/golem15/fonoteka/classes -run '^TestSaveAlbum' -count=1 -v)` | 9 PASS: `TestSaveAlbumDefersAfterCommitUntilArtistsSync` plus the 8 existing year and market-price provenance tests | +| Task 3: `go test ./plugins/golem15/fonoteka -run '^(TestAlbumsAdminSearchUsesCommittedArtists\|TestAlbumSearchDeleteAndFailures)$'` | both PASS | +| `go vet ./...` and `go test ./... -count=1` in summercms.go | vet clean; 29 packages `ok`, 0 FAIL | +| `go vet` and `go test ./... ./plugins/golem15/fonoteka/... ./plugins/golem15/user/... -count=1` in fonoteka.go | vet clean; 15 packages `ok`, 7 with no test files, 0 FAIL | +| `bash scripts/check-phase11.sh --all` (first run, on the executor's commits) | self-test, hygiene, go, postgres and named passed; **evidence refused: "review has 0 threat rows for T-11-31, want 1"** | +| `bash scripts/check-phase11.sh --all` (after `8e0083e`, `d4fc957`, `e6777bd`) | `phase11 all passed` | +| `PHASE11_RC=RC-14 bash scripts/check-phase11.sh --removal` | `RC-14 T-11-31 modules/lagoon/transaction.go: fails as required: TestTransactionAfterCommit, .../plain_gorm_transaction_is_refused, .../callback_handle_has_a_clean_statement`; restored | +| Hand mutation: Cabana `save` back to a plain `s.DB.Transaction` | `TestAlbumsAdminSearchUsesCommittedArtists` fails: `imports=[] calls=[], want one document`; file restored | +| Hand mutation: `SaveAlbum` back to a plain `gdb.Transaction` | `TestSaveAlbumDefersAfterCommitUntilArtistsSync` fails: `callbacks=0 observed=[], want one callback with [3 2]`; file restored | + +Acceptance criteria check: +- Task 1: crud.go has exactly three `lagoon.Transaction` write entry points and relation.go has two. The read-only sites are untouched. The test uses the assembled HTTP router, and it compares the import with pivots read on the committed connection after the request. **Pass.** +- Task 2: the plain-transaction tests assert zero callbacks or engine calls, and the lagoon one also asserts the warning. The rollback tests see no external request. The nested root-handle case errors without entering its function. The lagoon README covers lagoon-managed, implicit statement, foreign transaction and outside a transaction. **Pass.** The nested root-handle error was missing from the README and the `Transaction` doc comment until `8e0083e`. +- Task 3: `SaveAlbum` calls `lagoon.Transaction` with its callback ctx. The new test sees the submitted order and no callback on rollback. The existing validation and provenance tests are green. **Pass.** + +## Decisions Made + +See `key-decisions` in the frontmatter. + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] TestSyncFailuresNonFatal still relied on the old plain-transaction behaviour** +- **Found during:** Task 2, after `a33b1ad` +- **Issue:** `failed_document_read_inside_a_transaction` wrote inside `gdb.Transaction` and expected a "build document" warning. After the refusal no sync ran, so the test failed. This was confirmed in the close-out by restoring the `a33b1ad` version of the file. +- **Fix:** the subtest writes through `lagoon.Transaction`. This was left uncommitted when the executor stopped and committed as `2766f34` during the close-out window. +- **Commit:** `2766f34` + +**2. [Rule 2 - Missing critical] Documentation contradicted the new behaviour (CLAUDE.md module README rule)** +- **Found during:** close-out review of Task 2 +- **Issue:** `modules/beachcomber/README.md` still said the sync "runs immediately through the transaction's handle" inside a plain GORM transaction. `lagoon.Transaction`'s doc comment and the lagoon README did not mention the new nested root-handle error. +- **Fix:** the beachcomber README now says the sync is warned and skipped there, and points to `lagoon.Transaction` or an explicit `Sync` after commit. The lagoon README and doc comment state the nested root-handle error. +- **Files modified:** modules/beachcomber/README.md, modules/lagoon/README.md, modules/lagoon/transaction.go (comment only) +- **Commit:** `8e0083e` + +**3. [Rule 3 - Blocking] The phase gate refused 11-08's threat register** +- **Found during:** plan verification (`check-phase11.sh --all`) +- **Issue:** the evidence stage reads the threat table of every `11-0*-PLAN.md` and needs one review row per threat, plus a removal check for each high mitigated threat. T-11-31 (high) and T-11-32 (medium) had no rows. +- **Fix:** RC-14 in `removal_table`. It removes the foreign-transaction refusal and requires `TestTransactionAfterCommit` to fail, which it does. The review got rows for T-11-31 and T-11-32, the RC-14 row, and a CR-01 row in the fixes table. +- **Commits:** `d4fc957` (script), `e6777bd` (review) + +--- + +**Total deviations:** 3 (1 bug, 1 missing documentation, 1 blocking gate failure). No production behaviour was changed in the close-out; the only code change is a doc comment. + +## TDD Gate Compliance + +The executor committed each task as a single `fix` commit, with its tests in the same commit, so the RED run is not in history. The close-out replaced that evidence with mutations. RC-14 covers the Task 2 refusal. Hand mutations of the Cabana `save` transaction and of `SaveAlbum` make the Task 1 and Task 3 tests fail. Restoring the `a33b1ad` version of `sync_test.go` makes `TestSyncFailuresNonFatal` fail. + +## Issues Encountered + +- The fonoteka subtest `an album without a positive collection_id is logged and never sent` now asserts that no warning is logged, because its write happens in a rolled-back foreign transaction. Its name no longer matches its assertion. The zero-collection refusal is still covered by `ToSearchableArray` in the same subtest and by `TestAlbumSearchable/collection_id_zero_is_refused` (RC-07). The warning path for a failed build is covered by beachcomber `TestSyncFailuresNonFatal`. The subtest was not renamed; this is a cosmetic follow-up. +- Code that writes searchable models inside its own plain `gorm` transaction now gets no search sync. A Warn log appears instead of a silent skip. No production call site was found besides the Cabana and `SaveAlbum` paths this plan converted, and the full suites in both repositories pass. + +## Known Stubs + +None. + +## Threat Flags + +None. No new endpoint, auth path, file access or schema. The changes narrow when data leaves an open transaction. + +## User Setup Required + +None. + +## Next Phase Readiness + +- Phase 11 verification can be re-run to re-check CR-01. This close-out did not run it and did not mark the phase complete. +- Phase 12 still owns the SQL re-gate of `SearchIDs` results. Concurrent commits for the same Album are not serialized (flagged SRCH-01 assumption). +- RT-03 is not newly claimed: the Album `updated` broadcast payload timing on admin edits keeps the assumptions recorded in 11-03. + +## Self-Check: PASSED + +- All modified files listed in key-files exist on disk. +- summercms.go commits f7b6b0c, a33b1ad, 2766f34, 8e0083e, d4fc957 and e6777bd exist, and so does fonoteka.go commit 1c88199. diff --git a/.planning/state.json b/.planning/state.json index 7f4cdb8..b54e52d 100644 --- a/.planning/state.json +++ b/.planning/state.json @@ -56,13 +56,18 @@ { "number": "11", "name": "Jobs, realtime and search infrastructure", - "status": "pending" + "status": "in_progress" }, { "number": "11.1", "name": "SummerCMS documentation for humans and AI agents", "status": "pending" }, + { + "number": "11.2", + "name": "Ready to share: summercms.io website and newsletter plugin", + "status": "pending" + }, { "number": "12", "name": "Płytarium API — Collections and Albums", @@ -86,8 +91,8 @@ ], "next": { "command": "/gsd:progress --next", - "label": "Advance to the next step", - "reason": "Phase 10.1 of 18 · executing" + "label": "Advance to the next step (verify)", + "reason": "Phase 11 of 19 · ready to verify" }, - "updated_at": "2026-09-28T20:34:17.786Z" + "updated_at": "2026-09-30T19:09:22.774Z" }