test(release): verify client feedback rollout
This commit is contained in:
275
docs/client-feedback-deployment-readiness.md
Normal file
275
docs/client-feedback-deployment-readiness.md
Normal file
@@ -0,0 +1,275 @@
|
||||
# Client feedback rollout: deployment readiness
|
||||
|
||||
Verification date: 2026-07-31
|
||||
|
||||
## Decision
|
||||
|
||||
Tasks 1–17 are ready for a coordinated schema-changing deployment. Local code,
|
||||
CMS ownership, isolated database migrations, the full serial Vitest integration
|
||||
suite, the production webpack build, generated sitemaps, and public/draft
|
||||
behavior have been verified.
|
||||
|
||||
The rollout is not yet complete in production. Production is still serving
|
||||
revision `740e302c58f8`, before Tasks 1–17, and the production-write gates listed
|
||||
below remain intentionally blocked. No production database, media, publication
|
||||
state, or deployment was changed during Task 18.
|
||||
|
||||
## Commit sequence
|
||||
|
||||
The clean root worktree and this verification worktree both started at
|
||||
`07e9458aaf2796f5f48a69302acd26914032b879`. The root `main` history contains
|
||||
exactly these 17 Task 1–17 commits after production revision `740e302c58f8`:
|
||||
|
||||
| Task | Commit | Subject |
|
||||
| --- | --- | --- |
|
||||
| 1 | `fec1bab2e521d38af9de3c7c3c315c0da91795e7` | `fix(cms): enforce public publication contract` |
|
||||
| 2 | `b18bbbe36ad395cb26011d6032e06f460ded3f2a` | `fix(cms): filter unpublished relationships` |
|
||||
| 3 | `8dc17c0fea9420b2854a074d3924d4341147f04b` | `fix(cms): remove unpublished content fallbacks` |
|
||||
| 4 | `ab47571139a177339a002ff62336a61f7a44d4c6` | `refactor(cms): remove obsolete page fields` |
|
||||
| 5 | `e3c47c4500150be0fb40caa6cb4ae2619f61789c` | `feat(cms): add network page video upload` |
|
||||
| 6 | `c2dc42f243e58c7cb81e2e23e2aa2e34110ca4fc` | `feat(cms): add press contact image` |
|
||||
| 7 | `ebecf66e6fcb8514d5206bfc80ad36f4c9939e4e` | `fix(participation): repair process timeline` |
|
||||
| 8 | `30efb4b36ee7c0187c0274f319679846287ae3cb` | `feat(network): add responsive sponsor hierarchy` |
|
||||
| 9 | `95037e99089837a64a4315f6d2a467e8e5be61f5` | `feat(cms): clarify sponsor ordering` |
|
||||
| 10 | `adda9ddbd07a7bb04947c349a2117dd31879d190` | `fix(home): slow sponsor ticker` |
|
||||
| 11 | `f465839af0e49fa5021272c0a85786a90a763589` | `fix(home): honor phase headline line breaks` |
|
||||
| 12 | `5b62abf8d4b205d8603f3a0d34393d47d30412e8` | `fix(home): rebalance application form split` |
|
||||
| 13 | `d7a720460f542fc6c34ad3bd46d2eb5be1e2be6e` | `docs(home): verify application panel color` |
|
||||
| 14 | `4bb265dac1b7862db1e468d63126a07e9eaa8aec` | `fix(participation): clarify eligibility hierarchy` |
|
||||
| 15 | `3c28f6ff9c53486b87c2d45ffb57e35bc5a025d7` | `fix(about): rebalance relevance section` |
|
||||
| 16 | `0a19abd0b080095748f1de28dfe829da62316e96` | `fix(about): align facts and caption` |
|
||||
| 17 | `07e9458aaf2796f5f48a69302acd26914032b879` | `fix(seo): correct sitemap routes` |
|
||||
|
||||
Task 18 is the single commit containing this report and the regression-test
|
||||
cleanup described below. Its exact hash is recorded in the Task 18 handoff
|
||||
because a commit cannot embed its own hash.
|
||||
|
||||
No Task 1–17 integration is missing, and no unrelated tracked changes were
|
||||
present at the start of Task 18.
|
||||
|
||||
## Automated verification
|
||||
|
||||
All database-backed checks used copies of the current local `content.db`. The
|
||||
source database SHA-256 was
|
||||
`fb7de5d9f1fcf3a7c27494efe7f3111cba4173bc986a38ea62c9cf6d5840425e` and was
|
||||
never migrated or otherwise mutated.
|
||||
|
||||
### Integration tests
|
||||
|
||||
- Focused publication, relationship, sitemap, CMS-field, media, responsive
|
||||
section, timeline, sponsor, press, home, and about tests passed.
|
||||
- Complete serial suite against a freshly copied and migrated database:
|
||||
**21 files, 92 tests, all passed**.
|
||||
- Serial command shape used to avoid SQLite parallel-write contention:
|
||||
|
||||
```bash
|
||||
DATABASE_URL=file:/absolute/path/to/isolated.db \
|
||||
PAYLOAD_SECRET=task18-isolated-verification \
|
||||
PAYLOAD_MIGRATING=true \
|
||||
SITE_URL=http://127.0.0.1:3100 \
|
||||
./node_modules/.bin/vitest run \
|
||||
--config ./vitest.config.mts \
|
||||
--maxWorkers=1 \
|
||||
--no-file-parallelism \
|
||||
--hookTimeout=30000
|
||||
```
|
||||
|
||||
`PAYLOAD_MIGRATING=true` is required for tests against an explicitly migrated
|
||||
database so Payload does not also attempt its development schema-push path.
|
||||
|
||||
### Task 18 regression fix
|
||||
|
||||
`tests/int/publication-contract.int.spec.ts` created unique publication fixtures
|
||||
but deleted only their media record. A second run against the same isolated
|
||||
database therefore failed unique `slug` and `stableId` validation. Task 18 now
|
||||
deletes every created document before deleting the media record. The focused
|
||||
suite passed twice consecutively afterward, and the complete 92-test serial
|
||||
suite remained green.
|
||||
|
||||
### Static and build checks
|
||||
|
||||
- `./node_modules/.bin/tsc --noEmit --incremental false`: passed.
|
||||
- `payload generate:types`: passed; `src/payload-types.ts` was byte-for-byte
|
||||
unchanged.
|
||||
- `git diff --check`: passed.
|
||||
- Task 18 changed-file Prettier check: passed.
|
||||
- Production webpack build against the migrated database:
|
||||
`next build --webpack`: passed and generated all expected application,
|
||||
Payload, preview, and sitemap routes.
|
||||
- Production-safe sitemap postbuild with
|
||||
`NEXT_PUBLIC_SERVER_URL=https://srv1.bayerischer-mittelstandspreis.de`:
|
||||
passed. Generated `robots.txt` and `sitemap.xml` contain only the canonical
|
||||
HTTPS origin.
|
||||
- Local generated and dynamic sitemap endpoints returned `200`; `/`, the
|
||||
representative article, event, and winner URLs were present, no localhost or
|
||||
placeholder origin was present, and the unpublished Task 18 fixture was
|
||||
absent.
|
||||
|
||||
## Migration verification
|
||||
|
||||
Initial status on the copied current database was 5 recorded migrations and 18
|
||||
pending migrations. The isolated upgrade completed all pending migrations and
|
||||
ended with all 23 repository migrations recorded. `PRAGMA integrity_check`
|
||||
returned `ok`.
|
||||
|
||||
Every migration introduced by Tasks 1–17 was additionally verified through its
|
||||
own copied-database down/up cycle, with integrity checks after both directions:
|
||||
|
||||
- `20260731_143000_remove_press_record_fallbacks`
|
||||
- `20260731_160000_focused_payload_cleanup`
|
||||
- `20260731_170000_netzwerk_greeting_video`
|
||||
- `20260731_180000_press_contact_image`
|
||||
- `20260731_190000_responsive_sponsor_hierarchy`
|
||||
- `20260731_200000_home_phase_headline_line_break`
|
||||
|
||||
All six targeted down/up cycles passed. The real `content.db` was never used as
|
||||
a migration target.
|
||||
|
||||
## Browser verification
|
||||
|
||||
The production build was served locally against the isolated migrated database
|
||||
and the copied local media directory. Anonymous and authenticated preview checks
|
||||
used separate browser state.
|
||||
|
||||
### Route and viewport matrix
|
||||
|
||||
The following routes were checked at approximately 1440, 1024, 768, and an
|
||||
exact 390 CSS pixels:
|
||||
|
||||
- `/`
|
||||
- `/teilnahme`
|
||||
- `/der-bmp`
|
||||
- `/netzwerk`
|
||||
- `/presse`
|
||||
- `/presse/events/nominierten-auswahl-2026`
|
||||
- `/presse/blog/goldene-bavaria`
|
||||
- `/preistraeger/agrarquant-gmbh`
|
||||
|
||||
All 32 combinations rendered their expected CMS-owned heading and substantive
|
||||
main content with zero broken images. There was no page-level horizontal
|
||||
overflow except for the pre-existing event-detail issue below.
|
||||
|
||||
Specific checks also passed:
|
||||
|
||||
- Homepage phase heading rendered the CMS newline as two lines.
|
||||
- Shared `#bewerben` section computed to `rgb(36, 54, 106)` (`#24366A`).
|
||||
- `/teilnahme` timeline advanced from `01 / 04` through `04 / 04`; all four
|
||||
desktop/tablet steps were reachable, the 1024px section was 667px tall, and
|
||||
the next-section gap was `0`. At 390px all four mobile headings rendered in
|
||||
document order, controls were hidden, and the next-section gap was `0`.
|
||||
- Eligibility groups and the dates transition remained in the intended mobile
|
||||
scan order.
|
||||
- `/der-bmp` relevance layout was split on desktop; all three facts were
|
||||
centered and the image caption had a visible dark controlled background.
|
||||
- `/netzwerk` rendered tier bands with current counts 2 / 2 / 8 in authoritative
|
||||
order. With no Netzwerk video selected, no video action rendered.
|
||||
- `/presse` rendered the safe `copy-only` contact layout with no contact image
|
||||
selected.
|
||||
|
||||
### Anonymous public versus authenticated preview
|
||||
|
||||
An isolated draft press article was created with slug
|
||||
`task18-preview-fixture`.
|
||||
|
||||
- Anonymous direct browsing returned the 404 page and exposed neither the
|
||||
fixture title nor body.
|
||||
- After authenticating an isolated Payload user and entering through
|
||||
`/next/preview`, the same path rendered the draft title.
|
||||
- Publication and relationship integration tests independently prove this
|
||||
boundary for pages, winners, jury members, partners, events, and posts.
|
||||
|
||||
## Read-only production smoke
|
||||
|
||||
No production login or write was performed. Browser and HTTP checks produced:
|
||||
|
||||
| Production route | Result |
|
||||
| --- | --- |
|
||||
| `/` | `200`, expected main content, no broken images |
|
||||
| `/teilnahme` | `200`, expected main content, no broken images |
|
||||
| `/der-bmp` | `200`, expected main content, no broken images |
|
||||
| `/netzwerk` | `200`, expected main content, no broken images |
|
||||
| `/presse` | `200`, expected main content, no broken images |
|
||||
| `/presse/blog/goldene-bavaria` | `200`, expected article detail |
|
||||
| `/presse/events/nominierten-auswahl-2026` | `404` before rollout |
|
||||
| `/preistraeger/agrarquant-gmbh` | `404` before rollout |
|
||||
| `/sitemap.xml` | `200` |
|
||||
| `/pages-sitemap.xml` | `200` |
|
||||
| `/posts-sitemap.xml` | `200` |
|
||||
| `/robots.txt` | `200` |
|
||||
|
||||
The read-only VPS status script confirmed deployed revision `740e302c58f8`,
|
||||
which is exactly the parent of Task 1. Further container/status output requires
|
||||
interactive server `sudo` and was not attempted.
|
||||
|
||||
The current production sitemap is therefore expectedly stale: it still emits
|
||||
`/startseite`, emits three `/posts/<slug>` URLs that each return `404`, and has
|
||||
no event or winner detail entries. The corrected local sitemap cannot be judged
|
||||
live until the verified commit sequence is deployed.
|
||||
|
||||
## Known tooling and pre-existing limitations
|
||||
|
||||
- Repo-wide ESLint does not reach file evaluation because the current flat
|
||||
configuration throws `Cannot redefine plugin "@typescript-eslint"`.
|
||||
- A rollout-wide Prettier audit reports 41 already-committed Task 1–17 files as
|
||||
nonconforming. Task 18's changed files pass Prettier; bulk reformatting was
|
||||
intentionally excluded from this release-verification commit.
|
||||
- The installed global pnpm is `11.9.0`, while this repo requires pnpm `^9` or
|
||||
`^10`. `pnpm vps:status` is blocked by the engine check; invoking the
|
||||
read-only script directly reached production and confirmed the revision.
|
||||
- The in-app browser enforces a 480px minimum CSS viewport. The exact 390px
|
||||
matrix was completed through the installed Chrome browser surface instead.
|
||||
- In-app navigation to local XML endpoints was blocked by the browser client;
|
||||
read-only HTTP checks verified all four sitemap/robots endpoints instead.
|
||||
- The representative event detail overflows horizontally at exactly 768px:
|
||||
the pre-existing `EventDetail.tsx` layout combines 80px side padding, a 64px
|
||||
gap, and a fixed 400px sidebar. Tasks 1–17 did not change this component, so
|
||||
Task 18 did not expand scope to redesign it. The same route has no overflow
|
||||
at 390, 1024, or 1440px.
|
||||
|
||||
## Required production procedure and blocked gates
|
||||
|
||||
These actions are intentionally not performed by Task 18. They require explicit
|
||||
production-write coordination and a supported pnpm `^9` or `^10` runtime.
|
||||
|
||||
1. Obtain explicit production-write authorization and identify the exact commit
|
||||
to deploy.
|
||||
2. Create the documented server-side SQLite backup:
|
||||
|
||||
```bash
|
||||
pnpm vps:backup-db
|
||||
```
|
||||
|
||||
3. Deploy the committed schema-changing rollout with migrations enabled:
|
||||
|
||||
```bash
|
||||
RUN_MIGRATIONS=1 pnpm deploy:vps
|
||||
```
|
||||
|
||||
Per `docs/vps-deployment.md`, this mode also creates a pre-migration database
|
||||
copy before `pnpm payload migrate` runs in the one-off app container.
|
||||
|
||||
4. Confirm the deployed revision, container health, database/media state, and
|
||||
logs:
|
||||
|
||||
```bash
|
||||
pnpm vps:status
|
||||
```
|
||||
|
||||
5. Obtain the client-supplied Netzwerk video, upload/select it through Payload
|
||||
on the Netzwerk page, and verify that changing it does not affect the
|
||||
homepage video. No video asset was supplied in this task.
|
||||
6. Obtain the approved press-contact image, upload/select it with useful alt
|
||||
text through Payload on the Presse page, and verify the two-column desktop
|
||||
and stacked mobile layouts. No image asset was supplied in this task.
|
||||
7. Identify approved disposable production records before testing Unpublish and
|
||||
Republish. Record IDs and `_status`, verify anonymous list/detail removal,
|
||||
verify authenticated preview visibility, republish, and confirm restoration.
|
||||
No disposable record was approved in this task.
|
||||
8. Re-run the production route, sitemap, robots, media, responsive, and
|
||||
publication smoke matrix after deployment. In particular, every URL emitted
|
||||
by the corrected sitemap must return `200`.
|
||||
|
||||
Blocked gates at handoff are therefore: client Netzwerk video, client press
|
||||
image, approved production unpublish record(s), supported deployment tooling,
|
||||
interactive server privileges where required, and explicit production-write
|
||||
authorization.
|
||||
@@ -53,6 +53,7 @@ const lexicalDocument = {
|
||||
}
|
||||
|
||||
let mediaID: number
|
||||
const cleanup: Array<() => Promise<unknown>> = []
|
||||
|
||||
const publicationCases: PublicationCase[] = [
|
||||
{
|
||||
@@ -139,6 +140,10 @@ describe('draft-enabled collection publication contract', () => {
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
for (const removeFixture of cleanup.reverse()) {
|
||||
await removeFixture()
|
||||
}
|
||||
|
||||
if (mediaID) {
|
||||
await payload.delete({ collection: 'media', id: mediaID })
|
||||
}
|
||||
@@ -170,6 +175,20 @@ describe('draft-enabled collection publication contract', () => {
|
||||
data: data(`${collection}-unpublished`),
|
||||
draft: false,
|
||||
})
|
||||
cleanup.push(
|
||||
() =>
|
||||
payload.delete({
|
||||
collection,
|
||||
context: { disableRevalidate: true },
|
||||
id: published.id,
|
||||
}),
|
||||
() =>
|
||||
payload.delete({
|
||||
collection,
|
||||
context: { disableRevalidate: true },
|
||||
id: unpublished.id,
|
||||
}),
|
||||
)
|
||||
|
||||
const storedDraft = await payload.update({
|
||||
collection,
|
||||
|
||||
Reference in New Issue
Block a user