11-Jul-2026 — Resolvable walk links, upload attribution, email image hosts, walk export improvements #313

build 761commit d865a56


Walk links no longer 404 and edit no longer opens an empty walk

A walk with no stored url (an awaiting-leader slot, or a walk whose title had been cleared) still produced a title-or-date based slug for its links. That fabricated slug matched nothing on lookup, so clicking a walk title went to "Page not found", and the Edit button silently rendered a blank default walk dated today - closing which dropped the real walk from the in-memory programme until a refresh.

The slug logic was duplicated across six link builders (walkSlug, groupEventLink, event-contact, email-composer, committee-query, the walk-notification email footer), each fabricating slightly differently, so any single fix left the rest broken. Separately, clearing a title blanked the walk's url, and the copy-walk action never regenerated one.

Existing walks with a missing url become reachable again by id and pick up a proper url the next time they are saved; no data migration was needed.

Ramblers walk upload reports who ran it

The Serenity walk-upload scenario hardcoded the actor to "Walks Admin", so the steps could not distinguish Nick from Kerry. The uploader's name already reached the metadata file but was dropped by the extractor and ignored by the scenario.

Welcome / new-joiner email images now load when sent

Notification image overrides are stored as relative paths (api/aws/s3/site-content/...jpeg). The batch send rewrote these to absolute URLs, but the generic transactional path - which sends the welcome email - passed the raw relative path to Brevo. In the in-app preview a relative src resolves against the browser page origin so it looked fine; a real email client has no base href, so the images failed (as seen in the new-joiner welcomes).

Quieter cookie handling on the walks upload

The cookie-banner dismissal ran on every navigation, cluttering the upload report even when there was nothing to clear. It now runs only when a banner is actually present (a Check performs the clearing in its branch), so its dismissal steps appear only when there is something to dismiss. Fully hiding the check's own line (the IsHidden/IsSilent reporting markers) lives in serenity-js itself, on the feat/hidden-and-silent-activities fork branch, rather than as a patch here.

A single headless switch

Every Chromium launch (walks upload, site migration, integration worker) now goes through one resolveHeadless() / launchChromium() in server/lib/shared/playwright-browser.ts, with the default in a clearly-named DEFAULT_HEADLESS constant and overridable by PLAYWRIGHT_HEADLESS via the shared asBoolean() helper. The CLI --no-headless flag feeds the same one place.

Angular 21

Documentation updated to reflect the Angular 21 upgrade: AGENTS.md, README.md, codebase-evolution-stats.html and docs/contributing/agentic-development.md moved from Angular 20 to Angular 21.