docs(11-08): complete gap plan summary

This commit is contained in:
Jakub Zych
2026-09-30 21:10:08 +02:00
parent e6777bda97
commit 63bcbc31b0
6 changed files with 276 additions and 25 deletions

View File

@@ -97,7 +97,7 @@ Requirements for v1 (the Płytarium port). Each maps to roadmap phases. "User" b
### Search (SRCH) ### 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 - [ ] **SRCH-02**: The reindex command asserts zero documents with collection_id 0 before and after, and can drop the legacy index
### Integrations (INTG) ### Integrations (INTG)
@@ -227,7 +227,7 @@ Which phases cover which requirements. Updated during roadmap creation.
| RT-01 | Phase 11 | Gaps Found | | RT-01 | Phase 11 | Gaps Found |
| RT-02 | Phase 11 | Gaps Found | | RT-02 | Phase 11 | Gaps Found |
| RT-03 | 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 | | SRCH-02 | Phase 14 | Pending |
| INTG-01 | Phase 14 | Pending | | INTG-01 | Phase 14 | Pending |
| INTG-02 | Phase 14 | Pending | | INTG-02 | Phase 14 | Pending |

View File

@@ -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. 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. 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 **Research flag:** yes
Plans: Plans:
@@ -527,6 +527,9 @@ Plans:
**Wave 5** *(blocked on Wave 4 completion)* **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 - [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) ### 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. **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 | | 8. OAuth2.1 authorization server | 10/10 | Complete | 2026-09-23 |
| 9. Backend admin authentication and schema pipeline | 12/12 | In Progress| | | 9. Backend admin authentication and schema pipeline | 12/12 | In Progress| |
| 10. Admin Vue SPA | 5/5 | Complete | 2026-09-27 | | 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.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 | - | | 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 | - | | 12. Płytarium API — Collections and Albums | 0/TBD | Not started | - |

View File

@@ -3,17 +3,17 @@ gsd_state_version: "1.0"
milestone: v1.0 milestone: v1.0
current_phase: 11 current_phase: 11
current_phase_name: Jobs, realtime and search infrastructure current_phase_name: Jobs, realtime and search infrastructure
status: executing status: verifying
stopped_at: Completed 11-07-PLAN.md stopped_at: Completed 11-08-PLAN.md
last_updated: "2026-09-30T14:09:27.946Z" last_updated: "2026-09-30T19:09:44.022Z"
last_activity: 2026-09-29 last_activity: 2026-09-30
last_activity_desc: Phase 11 execution started last_activity_desc: Phase 11 execution started
state_head: dd97fd12a60a31067c6a0d254161d31e36b90c6c state_head: e6777bda9765d792268069432220b32d394c7bc8
progress: progress:
total_phases: 19 total_phases: 19
completed_phases: 9 completed_phases: 9
total_plans: 86 total_plans: 92
completed_plans: 85 completed_plans: 86
milestone_name: milestone milestone_name: milestone
--- ---
@@ -28,10 +28,10 @@ See: .planning/PROJECT.md (updated 2026-09-16)
## Current Position ## Current Position
Phase: 11 (Jobs, realtime and search infrastructure) — READY TO EXECUTE Phase: 11 (Jobs, realtime and search infrastructure) — EXECUTING
Plan: 7 of 7 Plan: 8 of 8
Status: Ready to execute Status: Phase complete — ready for verification
Last activity: 2026-09-29 — Phase 11 execution started Last activity: 2026-09-30 — Phase 11 execution started
Progress: [██████░░░░] 60% Progress: [██████░░░░] 60%
@@ -140,6 +140,7 @@ Progress: [██████░░░░] 60%
| Phase 11 P06 | 23 min | 3 tasks | 39 files | | Phase 11 P06 | 23 min | 3 tasks | 39 files |
| Phase 11 P04 | 17 min | 2 tasks | 19 files | | Phase 11 P04 | 17 min | 2 tasks | 19 files |
| Phase 11 P07 | 59min | 3 tasks | 40 files | | Phase 11 P07 | 59min | 3 tasks | 40 files |
| Phase 11 P08 | unknown (manual close-out) | 3 tasks | 13 files |
## Accumulated Context ## 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]: 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]: 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 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 ### Pending Todos
@@ -415,6 +418,6 @@ Items acknowledged and carried forward from previous milestone close:
## Session Continuity ## Session Continuity
Last session: 2026-09-30T12:57:09.320Z Last session: 2026-09-30T19:09:33.936Z
Stopped at: Completed 11-07-PLAN.md Stopped at: Completed 11-08-PLAN.md
Resume file: None Resume file: None

View File

@@ -1,6 +1,6 @@
{ {
"phase": "09", "phase": "11",
"session": "codex-thread-id-01a0d740-5347-7f00-9bb6-24c3bbe01139", "session": "codex-thread-id-01a0f286-f1a5-7ce0-b0b8-102d6d67e017",
"pid": 30, "pid": 2,
"updated_at": 1790317872927 "updated_at": 1790778676127
} }

View File

@@ -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.

View File

@@ -56,13 +56,18 @@
{ {
"number": "11", "number": "11",
"name": "Jobs, realtime and search infrastructure", "name": "Jobs, realtime and search infrastructure",
"status": "pending" "status": "in_progress"
}, },
{ {
"number": "11.1", "number": "11.1",
"name": "SummerCMS documentation for humans and AI agents", "name": "SummerCMS documentation for humans and AI agents",
"status": "pending" "status": "pending"
}, },
{
"number": "11.2",
"name": "Ready to share: summercms.io website and newsletter plugin",
"status": "pending"
},
{ {
"number": "12", "number": "12",
"name": "Płytarium API — Collections and Albums", "name": "Płytarium API — Collections and Albums",
@@ -86,8 +91,8 @@
], ],
"next": { "next": {
"command": "/gsd:progress --next", "command": "/gsd:progress --next",
"label": "Advance to the next step", "label": "Advance to the next step (verify)",
"reason": "Phase 10.1 of 18 · executing" "reason": "Phase 11 of 19 · ready to verify"
}, },
"updated_at": "2026-09-28T20:34:17.786Z" "updated_at": "2026-09-30T19:09:22.774Z"
} }