Skip to content

Pass 3 — production rollout (G/H/I/J/K, 14 backlog rows)#19

Merged
blackbrowed-labs merged 10 commits into
mainfrom
dev
May 12, 2026
Merged

Pass 3 — production rollout (G/H/I/J/K, 14 backlog rows)#19
blackbrowed-labs merged 10 commits into
mainfrom
dev

Conversation

@larsweiser
Copy link
Copy Markdown
Collaborator

Summary

Promotes Pass 3 from dev to main for production rollout. Closes Pass 2 backlog rows #5, #9, #10, #11, #12, #14, #17, #18, #19, #20, #21, #22, #23 via commits + #4/#24/#25 via controller-side scratch-space + retroactive close. Pass 2 backlog: 23/25 DONE after this merge.

Pass 3 itself shipped to dev as PR #18 (merge beba227); this PR is the dev → main promotion gated by Lars's staging walk.

Changes since main

Stats: 25 files / +782 / −1023 (net −241 LOC). CSS bundle delta: −13.4 KB (−23.6%) in dist/_astro/*.css.

Staging verification — passed

Controller-side smoke (post-merge to dev):

  • ✅ Staging deploy green
  • ✅ 10 canonical URLs return 200 on dev.blackbrowedlabs.com
  • ✅ I.1 → J.3 cascade: .contact-form{margin-inline:auto} confirmed in built CSS
  • ✅ J.2 extraction: .products-index__item rule served from shared module
  • ✅ Verifier meta-markers on staging show 2026-05-12
  • ✅ CWA beacon correctly absent on staging (0 script tags)

Lars-side visual walk: confirmed at 1645px / 1024px / 360px × light/dark on:

  • Contact pages (I.1 + J.3): content + form centred, mobile H1 flush-left preserved
  • Impressum + en/legal (I.1 + J.4): content centred; EN-legal courtesy-note redesign reads correctly
  • Datenschutz + en/privacy (I.1 + J.4): content centred; mobile section spacing tighter
  • Products (J.2): identity-preserving extraction, no visual regression

Post-merge production smoke checks

  • All canonical URLs on https://blackbrowedlabs.com/ return 200 with 0 redirect hops
  • CWA beacon <script> PRESENT on /datenschutz/ + /en/privacy/ (count = 1 each)
  • CWA dashboard shows pageviews after smoke-curling /datenschutz/
  • Verifier meta-markers on production show 2026-05-12
  • Email DNS (IONOS .com MX/SPF/DMARC) preserved post-deploy
  • Live verifier (gh workflow run verify-cloudflare-facts.yml --ref main) runs the new single-request DPF variant + classifies correctly
  • rebuild-nightly workflow remains scheduled

🤖 Generated with Claude Code

blackbrowed-labs and others added 10 commits May 12, 2026 14:53
Closes Pass 2 backlog #23 (workflow-permission claims need runtime
verification). Adds a fifth bullet to the "Process rules from Pass 1"
section codifying the lesson from the G D.10/11 chain: workflow
permissions claims for non-standard API endpoints (anything outside
issues / pull-requests / contents) must be verified by runtime
dispatch, doc citation, or a PAT-not-GITHUB_TOKEN runbook step before
merge. Renames the section to "Process rules from Pass 1+2".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add dpf-parser-broken-empty.json exercising the
parseActiveOrgs-returns-empty branch (line 193 of dpf.mjs), and a
new co-located fixture matrix doc at scripts/checks/__fixtures__/
README.md documenting which fixture exercises which branch.

The orchestrator's existing mock-scenario routing handles the new
fixture stem without any registry edit (dpf-* prefix matches the
dpf check's scenarioPrefix; the orchestrator computes the fixture
path from MOCK_SCENARIO directly).

Branch coverage status post-commit:
- Branch A (missing structural markers): dpf-parser-broken.json
- Branch B (parseActiveOrgs throws):     JSON-fixture-unreachable by
  construction; documented in matrix
- Branch C (orgs.length === 0):          dpf-parser-broken-empty.json

Branch B is unreachable from pure JSON: the throw path requires
runtime-only objects (e.g., a toString-throws prototype) that JSON
cannot encode. The String(p.OrganizationPublicDisplayName ?? '')
coercion at scripts/checks/dpf.mjs:170 is load-bearing for this
unreachability claim — the matrix doc records this so a future
implementer who edits that line updates the classification.

Refs: plans/active/pass-2/backlog.md #21

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Close five Pass 2 backlog rows in one commit; all touch the scripts/
verifier subtree or the verify-cloudflare-facts.yml workflow and all
are cosmetic with zero behaviour change.

- #17 cwa-retention.mjs: fix JSDoc Source URL drift (data-retention ->
  faq) so the header comment matches the sourceUrl constant set in
  G D.10.1.
- #18 cwa-retention.mjs: drop dead NUMBER_WORDS entry `twentyfour`
  (regex never emits the concatenated form) and add `'twenty four'`
  (regex CAN emit the spaced form via `\s-?` alternation).
- #19 dpf.mjs: correct spec section reference from sec 6.3 to the actual
  load-bearing sections sec 3.2 / 3.4 / 9.1 / 9.3.
- #20 verify-cloudflare-facts.yml: PR-body reads naturally for both
  value-change and absent-from-list cases instead of forcing the
  DPF-shaped "went absent" framing onto CWA-style figure shifts.
- #10 scripts/*.mjs: uniform "Run from the project root" docstring
  paragraph in build-headers.mjs, extract-tokens.mjs, and
  run-verifier.mjs (the three top-level scripts surviving after G D.11
  deleted check-cloudflare-facts-freshness.mjs); check modules under
  scripts/checks/ already document the convention via their
  orchestrator. Perms uniform: no +x bit on any .mjs file.

Verifier regression baseline (six MOCK_SCENARIO runs in --dry-run)
re-verified post-edit; statuses unchanged.

Refs: plans/active/pass-2/backlog.md #10, #17, #18, #19, #20
Closes Pass 2 backlog row #5 (paper-trail residue). The PR-triggered CI
workflow (.github/workflows/ci-pr.yml, job `build-pr`) shipped in April
under Pass 2 backlog row #7's umbrella — commit 8831927 authored it,
6103789 refined it — and the protect-main ruleset (id 15468856) was
updated on 2026-04-28 to require the build-pr check before merge.
Row #5 was never closed when its dependency landed.

§8.4 still asserted "Status checks deliberately not required" and
pointed at backlog #5 as future work. Both halves now stale; replaced
with the as-deployed description (workflow steps, ruleset id, strict
required-checks policy, job-name pinning rationale). Per CLAUDE.md
§Scope boundaries, TECH_STACK.md is not edited without explicit
instruction; backlog row #5's deferred-since-April-2026 scope is that
instruction.

No workflow YAML or ruleset change. Phase K is reconciliation, not
implementation — verified by gh api + gh run list during planning.
Backlog row #5 heading DONE-marker is controller-side housekeeping
batched after this commit lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…actForm (#14)

Add margin-inline centring to max-width: 68ch inner-content selectors on
Contact (DE+EN), Impressum + en/legal, Datenschutz + en/privacy, plus the
ContactForm component's .contact-form max-width: 32rem block. Body content
now centres within the 1200px outer container at desktop widths instead of
sitting flush-left with a ~700px right-side gap.

- 18 CSS rule modifications across 6 page files (kontakt, en/contact,
  impressum, en/legal, datenschutz, en/privacy) plus 1 in ContactForm.astro
  (Option B per locked pre-flight decision 2026-05-12).
- Selectors using the margin shorthand (kontakt/en-contact h1, content,
  content p, content h2 + impressum/legal lede, address, p) get their inline
  slot rewritten 0 → auto. Selectors without an existing margin declaration
  (impressum/legal/datenschutz/privacy __content + ContactForm .contact-form)
  gain a standalone margin-inline: auto.
- Mobile (<768px) unchanged: existing max-width: none resets on inner
  selectors make margin-inline: auto a no-op at single-column viewports.
  Explicit margin: 0 0 1.5rem mobile reset added to .contact h1 (which
  retains max-width: 14ch mobile-side) to preserve the Phase B.1
  flush-left mobile H1.
- Out of scope: ueber / en/about (two-column .about__top grid handles
  centring intentionally) and product-detail pages (already symmetric
  via .product-detail__container { margin: 0 auto; max-width: 70ch; }).

Closes Pass 2 backlog row #14. Phase J's planned CSS extraction will fold
these into shared modules; this gate ships the layout fix only.
Replace the upstream `Search: 'Cloudflare'` + `RowsPerPage: 10`
single-filtered request with `Search: ''` + `RowsPerPage: 5000`
(one request returning the full ~3,600-row active list) plus the
existing in-process substring filter on 'cloudflare' inside
parseActiveOrgs. The check no longer depends on Cloudflare's listed
name continuing to contain that exact substring at the upstream
search engine — a rebrand, an acquisition-driven rename, or an
upstream search-semantics flip (exact-match) does NOT silently flip
the verifier to `absent`, provided the new name still contains the
substring 'cloudflare'.

Phase G.3.A's investigation confirmed the API honours
`RowsPerPage >= SumCount` in a single response (~2.7 s, ~1.8 MB
payload, no rate-limit signals on a burst probe). No pagination loop
needed — the simplest possible diff. See
plans/active/pass-3/g/dpf-pagination-investigation.md for the
probe-by-probe evidence.

Trade: ~5 KB / ~0.8 s becomes ~1.8 MB / ~2.7 s per weekly cron run,
well inside the 30-min workflow timeout (spec §7.5).

Refs: plans/active/pass-2/backlog.md #22
      plans/active/pass-3/g/dpf-pagination-investigation.md
…+ J.1 conventions doc)

Lifts ~862 LOC of byte-identical scoped CSS out of the four products page
templates (produkte/index, produkte/[slug], en/products/index,
en/products/[slug]) into a single src/styles/products.css module, imported
once via global.css. Closes Pass 2 backlog row #12.

Class names stay BEM-namespaced (products-index__*, product-detail__*,
release__*, breadcrumb__*, version-chip*, cta*) — no markup churn. All
four scoped <style> blocks are deleted in their entirety; no page-private
CSS survives because the DE/EN siblings were byte-identical except for
documentation comments.

Inside the .release__body block, the previous :global() pseudo-wrappers
(needed for Astro-scoped <style> to reach raw markdown elements rendered
via set:html) are removed — the new module is non-scoped so the selectors
target the rendered <h1>/<p>/<ul>/etc. directly.

Co-introduces docs/CSS_CONVENTIONS.md (Phase J.1, folded into this commit
per the J plan's J.1 standalone-vs-fold decision). The doc records the
module-location, class-naming, extraction-scope, specificity, and import-
order disciplines that J.2 enforces and J.3 will follow.

Verified locally: npm run check 0 errors / 0 warnings, npm run build clean,
dist/_astro CSS bundle drops 56,575 → 43,203 bytes (−13.1 KB raw, four
per-route _slug_/index@_@astro.*.css chunks merged into BaseLayout's
single bundle). Every products class still emits in built HTML (grep);
no astro- scoped hashes leak. Real-browser staging walk deferred to
Wave 4 integration PR per controller instruction.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lifts ~120 LOC of CSS out of src/components/forms/ContactForm.astro's
scoped <style> block into src/styles/forms.css, imported once via
global.css. Closes Pass 2 backlog #9.

Class names stay BEM-namespaced (.contact-form, .contact-form__*) —
no markup churn, no migration to the bundle's .btn / .field global
vocabulary (per docs/CSS_CONVENTIONS.md §2 the bundle vocabulary is
reference-only and not adopted at runtime). The scoped <style>
block is deleted in its entirety — no component-private CSS
survives.

Carries over Phase I.1's `margin-inline: auto` rule on .contact-form
(Option B, Lars decision 2026-05-12) into the extracted module so
contact-form centring is preserved through the move. forms.css
documents this carry-over inline.

global.css imports forms.css alphabetically before products.css per
docs/CSS_CONVENTIONS.md §5.

Headroom: future forms (subscribe, feedback, request-a-demo) add
their own BEM-namespaced rule block to forms.css side-by-side with
the existing .contact-form__* rules, per the conventions doc §3.

Verified locally: npm run check 0/0, npm run build clean. Contact
page class list unchanged in dist/ (DE + EN). Minified rule
.contact-form{...margin-inline:auto...} confirmed in
dist/_astro/BaseLayout*.css. Real-browser staging walk deferred to
Wave 4 integration PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two consistency fixes across the four legal pages. Closes backlog #11.

1. Mobile section margin (datenschutz.astro + en/privacy.astro):
   .datenschutz__section { margin-top: 2rem } in the mobile media
   query -> 1.75rem, matching impressum.astro + en/legal.astro per
   the original Phase B.2 spec.

2. Courtesy-note pattern (en/legal.astro):
   .impressum__courtesy retires its pull-up margin (-2rem 0 2.5rem)
   for the Datenschutz pattern: margin 0 0 1.75rem + padding-top
   0.75rem + border-top hairline + italic. The pull-up relied on
   H1 bottom margin; the new pattern is explicit and reads better
   for variable-length courtesy text. en/privacy.astro already
   followed the preferred pattern -- unchanged.

DE Impressum has no courtesy note; DE Datenschutz has no courtesy
note. Only en/legal.astro's note was on the divergent pattern.

Verified locally: npm run check 0/0, npm run build clean. Dist
grep: 1.75rem confirmed in both DE/EN privacy mobile media queries;
en/legal courtesy hairline + italic preserved post-pull-up retire.
Real-browser staging walk deferred to Wave 4 integration PR per
Phase J controller direction.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eeea

Pass 3 — backlog burn-down (G/H/I/J/K, 9 commits, 14 rows closed)
@blackbrowed-labs blackbrowed-labs merged commit dbc9da8 into main May 12, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants