docs(16.2-05): complete Blog Editor release and UAT acceptance plan
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
---
|
||||
phase: 16.2-plugin-provided-field-types-and-the-blog-editor-formwidget
|
||||
plan: 05
|
||||
subsystem: release
|
||||
tags: [release, uat, deploy, summercms-tag, journal, blog-editor, embeds, csp, grzybyfunkcjonalne]
|
||||
|
||||
requires:
|
||||
- phase: 16.2-01
|
||||
provides: pact.HasFormFieldTypes, meta.notices, the field upload route and admin SPA plugin fields (released as summercms v0.1.18)
|
||||
- phase: 16.2-02
|
||||
provides: the Blog Editor formwidget and its editor corpus in sm-journal-plugin
|
||||
- phase: 16.2-03
|
||||
provides: Journal post and category content on the Blog Editor, the editor-output policy extension and the strip-and-report save
|
||||
- phase: 16.2-04
|
||||
provides: vue-journal-layer v0.2.0 (embed expansion) pinned by vue-grzybyfunkcjonalne-app main 465286a
|
||||
provides:
|
||||
- "summercms annotated tag v0.1.18 (tag object 04dc5bd, commit f1d8a75) on origin"
|
||||
- "sm-journal-plugin main 9d5bfc4 on origin, go.mod requiring summercms v0.1.18"
|
||||
- "sm-grzybyfunkcjonalne-app master 6f4f222 on origin: journal gitlink 9d5bfc4, vue-grzybyfunkcjonalne-app gitlink 465286a, README Blog Editor note, report-only CSP frame-src https://www.youtube-nocookie.com"
|
||||
- "UAT (uat.grzybyfunkcjonalne.pl) running the Blog Editor release, with rollback copies of the previous binary and Nuxt output"
|
||||
affects: [16.2-06, sm-journal-plugin, sm-grzybyfunkcjonalne-app, uat]
|
||||
|
||||
actuals:
|
||||
tokens: 9000
|
||||
tasks: 3
|
||||
commits: 0
|
||||
plan_head_before: f1d8a757fd5c1174f7f1959779602e2ad11b0de2
|
||||
plan_head_after: f1d8a757fd5c1174f7f1959779602e2ad11b0de2
|
||||
commits_note: "summercms.go got the tag only (no commit). Code commits live in the sibling repositories below."
|
||||
plugin_commits_repo: ../sm-journal-plugin
|
||||
plugin_head_before: c60abc6
|
||||
plugin_head_after: 9d5bfc4a3597be8ba4abee26d80dc2b81d343da8
|
||||
plugin_commits: 1
|
||||
host_commits_repo: ../sm-grzybyfunkcjonalne-app
|
||||
host_head_before: 0b93ac2
|
||||
host_head_after: 6f4f22296b6295e2750e3d376bd0300c8031ba51
|
||||
host_commits: 1
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Restart-anchored log check: the Go log is filtered from the River client_id line the restart logged, read straight from /var/log/supervisor, instead of `supervisorctl tail` (which fails on UAT)"
|
||||
|
||||
key-files:
|
||||
created: []
|
||||
modified:
|
||||
- ../sm-journal-plugin/go.mod
|
||||
- ../sm-grzybyfunkcjonalne-app/plugins/golem15/journal
|
||||
- ../sm-grzybyfunkcjonalne-app/vue-grzybyfunkcjonalne-app
|
||||
- ../sm-grzybyfunkcjonalne-app/README.md
|
||||
- ../sm-grzybyfunkcjonalne-app/deploy/nginx/grzybyfunkcjonalne-staging.conf
|
||||
- ../sm-grzybyfunkcjonalne-app/scripts/check-deploy.sh
|
||||
- ../sm-grzybyfunkcjonalne-app/DEPLOY.md
|
||||
|
||||
key-decisions:
|
||||
- "Human chose release-now at the Task 1 gate with the reply \"release v0.1.18\"; summercms v0.1.18 is published and a later defect gets v0.1.19"
|
||||
- "On UAT the Nuxt rollback copy lives at /srv/grzybyfunkcjonalne/nuxt/.output.prev, not go/build/nuxt/.output.prev as the plan assumed"
|
||||
- "The site-ok log check reads /var/log/supervisor/grzybyfunkcjonalne-go.log from the restart's River client_id anchor; `sudo supervisorctl tail` fails on UAT (ExpatError) and would pass vacuously"
|
||||
|
||||
patterns-established:
|
||||
- "A UAT release check that reads a log must prove it read lines (anchor present) before counting zero errors"
|
||||
|
||||
requirements-completed: [D-02, D-05, D-10, D-17, D-18]
|
||||
|
||||
coverage:
|
||||
- id: R1
|
||||
description: "Release: tag v0.1.18, plugin and host bumps pushed; UAT binary sha256 equals the local build; both programs RUNNING; rollback copies kept; on-server smoke passes"
|
||||
requirement: "D-07"
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "Task 2 verify: smoke: ok (98 urls); deploy-ok (with the corrected Nuxt rollback path)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: R2
|
||||
description: "D-03: an untouched admin save of post 64 (markdown with the raw author footer) leaves md5(content), md5(content_html) and its en translation row unchanged"
|
||||
requirement: "D-03"
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "Task 3 verify: posts-unchanged and translations-unchanged; post 64 updated_at moved to 2026-10-10 11:26:13+00, so the save reached the database"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: R3
|
||||
description: "D-03: an untouched admin save of HTML_POST (post 1) leaves its bytes unchanged"
|
||||
requirement: "D-03"
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "posts-unchanged passed for post 1, but post 1 updated_at is still 2026-10-09 17:33:51+00 and no post other than 64 and 105 was written after RESTART_TS, so no save of post 1 reached the database and the pass is vacuous"
|
||||
status: inconclusive
|
||||
human_judgment: true
|
||||
rationale: "A human must save post 1 untouched again; then re-run the posts-unchanged SELECT and confirm post 1's updated_at moved"
|
||||
- id: R4
|
||||
description: "D-02, D-05, D-18: the Blog Editor test post stores content = content_html with a YouTube placeholder, a figcaption and an uploaded image URL under journal/posts"
|
||||
requirement: "D-18"
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "Task 3 verify: edit-ok (1:1:1:1:1)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: R5
|
||||
description: "D-10 amended: a style element and an onclick attribute added in the source tab are stripped on save and reported"
|
||||
requirement: "D-10"
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "edit-ok fifth column 1 (no <style, no onclick stored)"
|
||||
status: pass
|
||||
- kind: manual
|
||||
ref: "Human reply \"Did tests, all points passed, bravo.\" covers the step 3 notice; the notice text was not quoted"
|
||||
status: pass
|
||||
human_judgment: true
|
||||
- id: R6
|
||||
description: "D-17: the published test post's public page contains a youtube-nocookie iframe; the Go log since the restart has no panic or SQLSTATE line"
|
||||
requirement: "D-17"
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "Task 3 verify: site-ok (1 iframe; 14 log lines after the anchor, 0 panic|SQLSTATE)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: R7
|
||||
description: "D-19: a one-word category edit saves without a removal notice and shows on the site"
|
||||
requirement: "D-19"
|
||||
verification: []
|
||||
human_judgment: true
|
||||
rationale: "Not among the four steps the human ran; no journal category row was updated after RESTART_TS, so no read-only check covers it"
|
||||
|
||||
duration: "about 25 min of executor time across three runs, plus the human UAT session (11:26 to 11:30Z)"
|
||||
completed: 2026-10-10
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Phase 16.2 Plan 05: Release and UAT Acceptance of the Blog Editor Summary
|
||||
|
||||
**summercms v0.1.18, sm-journal-plugin 9d5bfc4 and host 6f4f222 are released to UAT. The build there matches the local sha256, and the on-server smoke passes on 98 URLs. On real data, an untouched save of post 64 is byte-identical. A Blog Editor test post stores identical content and content_html with an uploaded figure and a YouTube embed, the embed renders as a youtube-nocookie iframe on the site, and markup that was not allowed was stripped on save. The untouched save of HTML post 1 has no database evidence yet, and the D-19 category check is still open.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** about 25 min of executor time across three runs, plus the human session.
|
||||
- **Started:** about 2026-10-10T11:10Z, after plan 04 closed.
|
||||
- **Completed:** 2026-10-10T11:36Z
|
||||
- **Tasks:** 3/3 (Task 1 blocking-human decision, Task 2 tracer, Task 3 blocking-human human-action)
|
||||
- **Files modified:** 7 across sm-journal-plugin and sm-grzybyfunkcjonalne-app. summercms.go got only the tag.
|
||||
|
||||
## Release Sheet (Task 1)
|
||||
|
||||
- **Gate:** the Task 1 verify printed `release-gate-ok`. It covered vet in both Go repositories, the six-test cabana smoke set, the journal classes and controllers tests, TestDocsTree, the admin vitest suite, the admin dist and OpenAPI drift gates, the journal vitest suite and the journal dist gate. All three repositories were clean and not behind origin.
|
||||
- **Tag:** `v0.1.18`. v0.1.17 was the latest tag, and v0.1.18 did not exist on origin.
|
||||
- **Commits pushed per repository:**
|
||||
- summercms.go master `c21fe36..f1d8a75` (19 commits, including planning docs).
|
||||
- sm-journal-plugin main `c60abc6..9d5bfc4` (9 commits: plans 02 and 03, plus the go.mod bump).
|
||||
- Host master `0b93ac2..6f4f222`.
|
||||
- **UAT D-07 split (read-only SELECT):** 102 posts whose content starts with `<` and are served as content, and 3 markdown posts served as content_html. Re-read after Task 3, so the count includes test post 105. Before the test post it was 101:3.
|
||||
- **Suites not run here (Docker host needed):**
|
||||
- The modules/cabana testcontainers suites.
|
||||
- The sm-journal-plugin root harness: TestPostsFormCompiles, TestPostsAdminCreateSmoke, TestPostsAdminResaveKeepsHTML, TestPostsAdminSaveSanitizesHTML and TestCategoriesAdminContent.
|
||||
- ./models.
|
||||
- These are WINDOWS.md entries 14 to 16, which go to plan 06.
|
||||
- **Review findings:** neither 16.2-REVIEW.md nor 16.2-SECURITY.md exists, so there are no open high findings.
|
||||
- **Human reply, verbatim:** "release v0.1.18" (release-now).
|
||||
|
||||
## Task Commits
|
||||
|
||||
| Task | Name | Repo | Commit |
|
||||
|---|---|---|---|
|
||||
| 1 | Release gate | (none) | release-gate-ok, no commit |
|
||||
| 2 | Tag summercms | summercms.go | annotated tag `v0.1.18` (object `04dc5bdabecd95a58b5b7d3a02ed465a45fbc99a`, commit `f1d8a757fd5c1174f7f1959779602e2ad11b0de2`) |
|
||||
| 2 | Require summercms v0.1.18 | sm-journal-plugin | `9d5bfc4` chore(deps): require summercms v0.1.18 |
|
||||
| 2 | Bump gitlinks, README note, CSP frame-src | sm-grzybyfunkcjonalne-app | `6f4f222` chore: bump journal plugin (Blog Editor) and the site app (embeds) |
|
||||
| 3 | UAT acceptance (read-only checks) | (none) | no commit |
|
||||
|
||||
**Pushed heads:**
|
||||
- summercms.go master `f1d8a75`, plus tag `v0.1.18`.
|
||||
- sm-journal-plugin main `9d5bfc4a3597be8ba4abee26d80dc2b81d343da8`.
|
||||
- sm-grzybyfunkcjonalne-app master `6f4f22296b6295e2750e3d376bd0300c8031ba51`, with journal gitlink `9d5bfc4a…` and vue-grzybyfunkcjonalne-app gitlink `465286a880f0aae6118c2915aa1475a5da353295` (was `ada03b2`; it pins vue-journal-layer v0.2.0).
|
||||
|
||||
## Deploy (Task 2)
|
||||
|
||||
- **Build sha256:** local `01c0b64a53a3b95f6481be21408cfa7450828414516e6868c3801e937680edc4`. The remote copy matched, first as `bin/grzybyfunkcjonalne.new` and again after the move.
|
||||
- **Previous binary:** `bin/grzybyfunkcjonalne.prev`, sha256 `e5b0ad28ca0384993470ac5a3177d502b7bd0b67c553488f86b43ae3363af232`.
|
||||
- **Nuxt rollback copy:** `/srv/grzybyfunkcjonalne/nuxt/.output.prev`.
|
||||
- **Migrations:** `migrate` printed `✓ migrations applied`, rc 0.
|
||||
- **RESTART_TS:** `2026/10/10 11:17:46` (server time, UTC). Log anchor: `client_id=alexandria_2026_10_10T11_17_46_316300`.
|
||||
- **Smoke:** `smoke: ok (98 urls)`, then `deploy-ok`.
|
||||
|
||||
**Snapshot taken before the human saves (read-only):**
|
||||
- `HTML_POST=1` (10-faktow-na-temat-cordycepsu).
|
||||
- `SNAPSHOT_POSTS`:
|
||||
```
|
||||
1:a026264105f900175b9770e637e5ec5c:a026264105f900175b9770e637e5ec5c
|
||||
64:0e4be845390b1656871513957d471a7c:e785e14e80f6400534e16b4769749d3c
|
||||
```
|
||||
- `SNAPSHOT_TRANSLATIONS`: `64:en:862fc5b362cc9856e6736a2078dca3ba`. Post 1 has no translation rows.
|
||||
- `POST64_STATE=1:1`. The plan expected `x:0`. Post 64's content and content_html both still contain a `<style` element: one `.author-footer` CSS block, at offset 4584 in content and 5035 in content_html. The style block was not removed from content_html on UAT as the plan assumed. This is reported, not a failure. The untouched re-save kept it byte-for-byte, and a re-read after Task 3 still prints `1:1`.
|
||||
|
||||
## UAT Acceptance (Task 3)
|
||||
|
||||
**Human reply, verbatim:** "Did tests, all points passed, bravo."
|
||||
|
||||
The human confirmed that all four steps passed, including the step 3 strip notice, but did not quote the notice text.
|
||||
|
||||
**Read-only checks, run after the reply:**
|
||||
|
||||
| Check | Result | Detail |
|
||||
|---|---|---|
|
||||
| posts-unchanged | pass | The md5 rows of posts 1 and 64 match the snapshot exactly |
|
||||
| translations-unchanged | pass | `64:en:862fc5b3…` matches; there are still no rows for post 1 |
|
||||
| edit-ok | pass | Test post `uat-16-2-blog-editor` (id 105) prints `1:1:1:1:1`: content = content_html, a `data-embed="youtube"` placeholder, a figcaption, an image under `/storage/app/uploads/public/journal/posts/`, and neither `<style` nor `onclick` |
|
||||
| site-ok | pass | The public page `/blog/uat-16-2-blog-editor`, fetched on the server, has 1 `src="https://www.youtube-nocookie.com/embed/` iframe. The Go log has 14 lines after the restart anchor, with 0 panic and 0 SQLSTATE |
|
||||
|
||||
**Which rows the saves wrote:** only post 64 (11:26:13Z) and post 105 (11:29:56Z) have `updated_at` after RESTART_TS. Post 105 is published. Post 1 still has `updated_at` 2026-10-09 17:33:51Z. The admin save runs GORM `Save`, which always sets `updated_at`, so no save of post 1 reached the database. Either it was not saved, or a different post was opened. The posts-unchanged pass for post 1 is therefore vacuous, and D-03 is proven on post 64 only (see Open Items).
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `../sm-journal-plugin/go.mod`: nominal require of summercms v0.1.18. The go.work replace stays.
|
||||
- `../sm-grzybyfunkcjonalne-app/plugins/golem15/journal` and `vue-grzybyfunkcjonalne-app`: gitlink bumps.
|
||||
- `../sm-grzybyfunkcjonalne-app/README.md`: an Admin note on the Blog Editor, lossless untouched saves, and stripped markup being reported.
|
||||
- `../sm-grzybyfunkcjonalne-app/deploy/nginx/grzybyfunkcjonalne-staging.conf`, `scripts/check-deploy.sh` and `DEPLOY.md`: `frame-src https://www.youtube-nocookie.com` in the report-only CSP, its check needle and its docs line.
|
||||
|
||||
## Decisions Made
|
||||
|
||||
- Release now with v0.1.18 (human, Task 1).
|
||||
- The real Nuxt rollback path is `nuxt/.output.prev`.
|
||||
- The site-ok log check reads the supervisor log file from the restart anchor.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 1 - Bug] The plan's Nuxt rollback path was wrong**
|
||||
- **Found during:** Task 2
|
||||
- **Issue:** The plan copies, checks and restores `go/build/nuxt/.output(.prev)`. On UAT the Nuxt server runs from `/srv/grzybyfunkcjonalne/nuxt/.output`.
|
||||
- **Fix:** The rollback copy was made at `/srv/grzybyfunkcjonalne/nuxt/.output.prev`, and the deploy-ok check tested that path.
|
||||
- **Files modified:** none (server layout only)
|
||||
- **Commit:** none
|
||||
|
||||
**2. [Rule 1 - Bug] The site-ok log check passed vacuously**
|
||||
- **Found during:** Task 2 (log check), applied in Task 3
|
||||
- **Issue:** `sudo supervisorctl tail -65536 grzybyfunkcjonalne-go` fails on UAT with an ExpatError. The pipeline then counted 0 matching lines, so the check would pass whatever the log contained.
|
||||
- **Fix:** Read `/var/log/supervisor/grzybyfunkcjonalne-go.log` directly. The command first requires the restart's River anchor (`client_id=alexandria_2026_10_10T11_17_46_316300`) to be present, then counts panic|SQLSTATE only in the lines after it:
|
||||
`ssh grzybyfunkcjonalne-uat 'F=/var/log/supervisor/grzybyfunkcjonalne-go.log; A=client_id=alexandria_2026_10_10T11_17_46_316300; grep -q "$A" "$F" && awk -v a="$A" "found{print} index(\$0,a){found=1}" "$F" | grep -ciE "panic|SQLSTATE"' | grep -qx 0`
|
||||
The anchor was still in the file (14 lines after it), so the timestamp fallback was not needed. The file holds 6 older SQLSTATE lines, all from before the restart.
|
||||
- **Files modified:** none
|
||||
- **Commit:** none
|
||||
|
||||
---
|
||||
|
||||
**Total deviations:** 2, both Rule 1 bugs in the plan's server commands. No shipped code changed.
|
||||
**Impact on plan:** Both fixes make the checks stricter. They do not loosen them.
|
||||
|
||||
## Open Items (pending human)
|
||||
|
||||
1. **D-03 on an HTML post is unproven.** Save post 1 (`10-faktow-na-temat-cordycepsu`) in `/gfadmin/golem15/journal/posts` without touching the editor. Then re-run the posts-unchanged SELECT and confirm that post 1's `updated_at` moved past 2026-10-10 11:17:46Z.
|
||||
2. **D-19 category check:** in a category with content, change one word in the Blog Editor and save. No removal notice should appear, and the category page on the site should show the change. Also open post 1's source tab and confirm the stored HTML is unchanged. Not human-verified yet; pending for /msd-verify-work.
|
||||
3. **Vhost install (root):** install `/srv/grzybyfunkcjonalne/deploy/grzybyfunkcjonalne-staging.conf` (frame-src https://www.youtube-nocookie.com), then `sudo nginx -t && sudo systemctl reload nginx`. This blocks nothing, because the CSP is report-only.
|
||||
4. **Test post `uat-16-2-blog-editor` (id 105) is still published.** Unpublish or delete it when it is no longer needed. The human may keep it for now.
|
||||
5. **Docker-host suites:** these go to plan 06 (WINDOWS.md entries 14 to 16).
|
||||
|
||||
**Rollback, if any open item fails** (on the server, in `/srv/grzybyfunkcjonalne`):
|
||||
- Move `go/bin/grzybyfunkcjonalne.prev` back to `go/bin/grzybyfunkcjonalne`.
|
||||
- Move `nuxt/.output.prev` back to `nuxt/.output`.
|
||||
- Run `sudo supervisorctl restart grzybyfunkcjonalne-go grzybyfunkcjonalne-nuxt`.
|
||||
|
||||
## Auth Gates
|
||||
|
||||
None. The executor never asked for, printed or stored a credential. All admin actions were done by the human. On UAT the executor ran only SELECT queries and on-server curl, apart from the Task 2 deploy steps the human approved at the Task 1 gate.
|
||||
|
||||
## User Setup Required
|
||||
|
||||
See Open Items 1 to 4.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
- Plan 06 (unit tests) can run. Its tests ship with the next summercms tag, v0.1.19.
|
||||
- /msd-verify-work should pick up Open Items 1 and 2.
|
||||
|
||||
---
|
||||
*Phase: 16.2-plugin-provided-field-types-and-the-blog-editor-formwidget*
|
||||
*Completed: 2026-10-10*
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- FOUND on origin: tag `v0.1.18` (object `04dc5bd`).
|
||||
- FOUND: sm-journal-plugin `9d5bfc4`, equal to origin/main. go.mod requires summercms v0.1.18, and the replace stays.
|
||||
- FOUND: host `6f4f222`, equal to origin/master. The gitlinks are journal `9d5bfc4` and vue-grzybyfunkcjonalne-app `465286a`.
|
||||
- FOUND: the `frame-src https://www.youtube-nocookie.com` needle in both the staging vhost and check-deploy.sh (1 each).
|
||||
- Task 3 checks: posts-unchanged, translations-unchanged, edit-ok and site-ok were all printed in this run.
|
||||
Reference in New Issue
Block a user