276 lines
12 KiB
Markdown
276 lines
12 KiB
Markdown
# 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.
|