Skip to content

Phase D — releases pipeline + Cloudflare-fact-verifier + populated detail UI#13

Merged
blackbrowed-labs merged 13 commits into
mainfrom
dev
Apr 29, 2026
Merged

Phase D — releases pipeline + Cloudflare-fact-verifier + populated detail UI#13
blackbrowed-labs merged 13 commits into
mainfrom
dev

Conversation

@larsweiser
Copy link
Copy Markdown
Collaborator

Summary

Phase D of Pass 2 — nine gates closed. Stands up the releases pipeline (build-time loader fetching GitHub Releases per product, event-driven rebuild dispatch, nightly safety-net cron), the Cloudflare-fact-verifier ("no silent failures" design covering DPF + CWA retention with auto-PR / Issue routing per outcome), and the populated-state detail-template UI that surfaces version chips + release-history sections.

Closes Pass 2 backlog #6 (Node 20 deprecation forward-defense), #8 (Cloudflare-fact-verifier "no silent failures" design + impl), and #13 (CTA-pattern test branch coverage — fake-fixture exercises both visibility branches; Thalura adoption runbook handles cross-repo work post-merge).

Gates

  • G D.1 — Backlog Phase B.1: contact form (Worker + page + Datenschutz + diagnostic fixes) #6 closeout: actions/checkout + actions/setup-node bumped @v4@v6 (Node-24-native); FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 env applied to all three existing workflows.
  • G D.2 (workspace-only) — verifier "no silent failures" expanded design spec.
  • G D.3docs/PRODUCT_REPO_CONVENTIONS.md (canonical release-process doc with verbatim release.yml YAML, dual UI/CLI setup paths, autonomous-agent-implementable failure-mode recovery) + fake-fixture website-side products entries.
  • G D.4 — releases content loader (build-time GitHub-API fetch via PRODUCT_REPOS_PAT, two short-circuit guards) + visibility auto-detect (replaces manual showGithubLink schema field) + TECH_STACK §5.2 + §10.1 doc revisions.
  • G D.5rebuild-on-dispatch.yml workflow listening for repository_dispatch: product-release-published.
  • G D.6rebuild-nightly.yml (cron 03:00 UTC daily) + TECH_STACK §3.4 rewrite (GH-Actions-Cron path documented; rejected-Worker-Cron rationale captured).
  • G D.7 — Cloudflare-fact-verifier impl: weekly Mon 02:00 UTC cron, scripts/checks/{dpf,cwa-retention}.mjs + 6 fixtures, scripts/run-verifier.mjs orchestrator, schema migration on cloudflare-facts.json, freshness-gate extension (30-day threshold).
  • G D.8 (workspace-only) — Thalura adoption runbook for cross-repo work (separate agent applies post-merge).
  • G D.9 — populated-state detail-template UI: version chip pair, release-history <h2> + per-release stack with hr separators, full markdown body rendering, locale-aware long-form dates, hide-on-empty.

Plus three fix-up commits resolving reviewer-flagged minors and a deploy-workflow PAT-passthrough oversight.

Verification status

  • All 9 per-gate reviews approved (per-gate verdicts: G D.1 / G D.3 / G D.5 / G D.6 / G D.9 with minors fixed in-cycle; G D.4 / G D.7 with MAJOR + MINOR findings fixed via fix-up commits; G D.2 / G D.8 workspace-only).
  • Final integration review (git diff main..dev, 12 commits, 35 files, +2567/-124) approved across all 10 cross-cutting axes: schema migration coherence, releases data-shape consistency, visibility auto-detect, DE/EN sibling page consistency, workflow YAML cross-cut, deploy-workflow + loader cross-cut, production-build regression, out-of-scope discipline, no-accidental-workspace-commits.
  • Staging build green for every gate's commit set on push.

Test plan

Cross-repo work post-merge (out-of-band)

  • Fake-fixture visibility flip to public on GitHub (drives auto-detect transition test).
  • Thalura adoption by a separate agent following plans/active/pass-2/g-d-8/thalura-adoption-prompt.md runbook.

🤖 Generated with Claude Code

blackbrowed-labs and others added 13 commits April 29, 2026 15:47
Adds FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true to deploy-staging.yml and
deploy-production.yml at workflow level. ci-pr.yml already had it. Action
audit completed; actions/checkout and actions/setup-node bumped from @v4
to @v6 (first Node-24-native major is v5; v6 is the current latest).
cloudflare/wrangler-action stays at @V3 (no new major; v3.15.0 latest).
Closes backlog #6.

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

Adds docs/PRODUCT_REPO_CONVENTIONS.md (canonical release process for product
repos: tag format, CHANGELOG, release workflow, dispatch wiring, dual
UI/CLI setup paths). Adds website-side products entries for
pipeline-test-fixture (DE + EN, draft: true). Fake-fixture repo creation
itself is cross-repo work (manual UI per
plans/active/pass-2/g-d-3/fake-fixture-ui-clickthrough.md).

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

Implements src/content.config.ts releases loader: iterates non-draft
products, fetches GitHub Releases via PRODUCT_REPOS_PAT, returns typed
entries. Two short-circuit guards (no PAT → []; no products → []).

Adds visibility auto-detect: detail templates fetch GET /repos/{owner}/{repo}
to determine repo public/private; CTA renders only on public repos.
Schema field showGithubLink retired in favor of auto-detect.

TECH_STACK §5.2 (auto-detect replaces opt-in flag) + §10.1 (Worker
secret bullet removed; deploy is static-assets-only) revised.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves all three minors flagged by G D.4 code-quality review:

1. TECH_STACK §5.3 PAT-storage phrasing self-contradiction. Updated
   line 351 from 'stored as a Worker/Actions secret' to 'stored as a
   GitHub Actions secret on the website repo (and locally in .env for
   development)' — aligns §5.3 with §10.1's static-assets-only deploy
   posture.

2. content.config.ts frontmatter regex single-quote handling. Tightened
   slug/repo/lang patterns from ["?] to ["']? for both delimiters so a
   future single-quoted scalar parses correctly. Comment added documenting
   the convention. (`draft` literal pattern unchanged — booleans never
   quoted in YAML.)

3. products.ts getProductVisibility cache race. Cache now stores the
   in-flight Promise (not the resolved value) so concurrent first-call
   renders for the same repo share one HTTP request instead of issuing
   duplicate fetches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Listens for repository_dispatch with event_type product-release-published
from product repos (per docs/PRODUCT_REPO_CONVENTIONS.md). Checks out main,
builds for production with PRODUCT_REPOS_PAT, deploys via wrangler-action.

Triggered downstream by product repos' release.yml after a new GitHub
Release is published.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds .github/workflows/rebuild-nightly.yml — GH Actions cron at 03:00 UTC
daily that re-runs production deploy. Same shape as deploy-production.yml.
workflow_dispatch enabled for manual triggers.

TECH_STACK §3.4 rewritten to align doc with reality (was: Cloudflare
Worker scheduled trigger; now: GH Actions scheduled workflow). Rationale
for rejecting Worker Cron path documented inline.

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

Adds verify-cloudflare-facts.yml weekly cron workflow + scripts/checks/dpf.mjs
+ scripts/checks/cwa-retention.mjs + 6 fixtures + scripts/run-verifier.mjs
orchestrator. Extends scripts/check-cloudflare-facts-freshness.mjs to read
_meta.last_check_attempt with 30-day threshold (down from 90).

Migrates src/data/cloudflare-facts.json to verifier-era schema (schema_version
1; per-fact status + value fields; _meta block; structured cwa_retention with
integer month values; raw_events_retention_months explicitly null per
Cloudflare's documented absence). Updates src/lib/cloudflare-facts.ts type +
adds getEffectiveVerifiedDate helper (worst-case freshness signal: older of
the two per-fact verified_at). Privacy pages (datenschutz.astro, en/privacy.astro)
read via the helper.

Verifier shape per plans/active/pass-2/g-d-2/spec.md: 1/5/15-min retry budget
(21 min total, fits 30-min workflow timeout); status-return error handling
(no throws cross check boundaries); explicit registry per spec §9.6;
hand-written validator per §9.7; status-enum rename ('ok' CheckResult →
'active' JSON) per §6.2; v1-coverage smoke step inside verifier workflow only,
mode #8 deploy-triggered limitation acknowledged per §3.8.

All synthetic failure modes (#2, #4, #5, #6) verified via MOCK_SCENARIO=
fixture routing. Mode #1 (freshness gate) verified via stale last_check_attempt.
Modes #3, #7, #8, #9 deferred to controller-side post-commit per prompt's
"may defer" guidance. Outcome record at
plans/active/pass-2/g-d-7/verifier-test-matrix.md (workspace, gitignored).

Closes backlog #8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MAJOR 1: Workflow Issue heredoc malformed — replaced cat <<EOF with
printf '%s\n' array. Avoids the YAML-block-scalar / bash-heredoc
indentation interaction the reviewer flagged. Body and EOF placement
are no longer load-bearing; YAML stays valid; result is plain markdown.

MAJOR 2 (highest impact): Mode #6 semantics broken. Spec §2.6 / §3.6
/ §4.6 require BOTH an Issue AND an auto-PR on status='changed'.
Refactored the orchestrator's per-fact update routing per spec §1.3:
  - 'ok'      → advance verified_at + last_known_good_at + value fields
  - 'changed' → advance verified_at + value fields; pin last_known_good_at
  - other     → leave all pinned
The orchestrator now always exits 0 (unless it threw) and surfaces two
GH-output flags: has_diff (drives auto-PR) and has_attention (drives
Issue). Both can fire on the same run. Workflow gating updated to
match. Mode #6 dry-run now logs "would open auto-PR AND open
verifier-alert Issue" with the new value persisted to JSON.

MAJOR 3: Smoke step ISO vs locale-formatted date. Privacy pages now
emit hidden <meta name="bbl-verified-at" content="YYYY-MM-DD"> markers
(plus per-fact -dpf / -cwa variants) via a new <slot name="head" /> in
BaseLayout. Smoke step greps for the locale-independent marker
instead of the rendered "29. April 2026" / "April 29, 2026" text.

MINOR 1: Stale-escalation comments now carry a hidden marker
(<!-- verifier-stale-Nd -->); the post step skips when the marker is
already present on the target. Six-week-stale Issues no longer
accumulate weekly duplicate comments.

MINOR 2: Mode #7 (auto-PR staleness) coverage. Path chosen: extend
the step. Stale-escalation now scans both `gh issue list` and
`gh pr list --base dev` filtered by `headRefName startswith
"verifier/"`, with the same 14/30/60d cadence and marker idempotency.

MINOR 3: Smoke step `node -e` JSON parse now Date.parse()-validates
each verified_at and exits 1 with a named error if either is
unparseable, instead of silently emitting `undefined.slice(0,10)`.

MINOR 4: formatVerifiedDate throws on empty/non-string input rather
than rendering "Invalid Date" silently.

Workspace test matrix updated with new mode #6 + mode #7 outcomes,
the rationale block at the top of the document, and post-fixup
synthetic-test outputs.

Verification:
- npm run check: 0 errors / 0 warnings / 1 unrelated hint.
- PUBLIC_ENVIRONMENT=staging npm run build: 17 pages clean.
- Synthetic scenarios (5 modes): all pass; mode #6 now correctly
  advances aggregated_retention_months 6→12 + verified_at, pins
  last_known_good_at, and would fire both PR + Issue on real runs.
- bbl-verified-at markers present in dist/{datenschutz,en/privacy}/index.html;
  absent from non-privacy pages.
- python3 yaml.safe_load: workflow YAML parses clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds version chip pair (between H1 and tagline) + release-history section
(release-stack with hr separators, locale-formatted dates, full markdown
body rendering) to product detail templates. Hidden when releases empty
(matches Phase C empty-state design discipline).

Closes the Phase C → Phase D handoff gap on populated-state UI. Fake-fixture's
v0.0.1-pre release now visible on dev's /produkte/pipeline-test-fixture/.

Also fixes G D.4 loader's unconditional draft filter — extends to env-aware
via PUBLIC_ENVIRONMENT (matches getVisibleProducts pattern). Without this
the populated-state UI couldn't be verified on dev: drafts had their
releases skipped before any GitHub API fetch.

Extracts shared formatLocaleDate helper to src/lib/date.ts (replaces
formatVerifiedDate in src/lib/cloudflare-facts.ts; consumers updated
to import directly).

i18n strings added: productDetail.releaseHistory.heading + preReleaseBadge
(DE: Versionsverlauf / Pre-Release; EN: Release history / pre-release).

Markdown rendering uses Astro's @astrojs/markdown-remark createMarkdownProcessor
(transitive dep, no new package.json entry).

Pre-release badge fallback: italics on neutral chip (no warning-tone
tokens defined in tokens.css; documented fallback in spec).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per code-quality reviewer MINOR: --radius-bbl-1 and --radius-bbl-2
tokens both exist in src/styles/tokens.css, so the var() fallback values
on G D.9-introduced rules were redundant. Drops the fallbacks on
.version-chip and .release__body :global(code) / :global(pre) in both
DE and EN [slug].astro templates.

Pre-existing .cta { border-radius: var(--radius-bbl-2, 0.375rem); }
left untouched per the no-mid-flight-retrofit rule (predates this gate).

Workspace-only fixture-release-body clarification (in
plans/active/pass-2/g-d-3/fake-fixture-ui-clickthrough.md step 6) handled
inline; not in this commit (gitignored).

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

Master plan §4 D.1 required wiring PRODUCT_REPOS_PAT into all 4 build
workflows (deploy-staging, deploy-production, rebuild-on-dispatch,
rebuild-nightly). G D.4 wired only the two NEW workflows (G D.5 +
G D.6); the two existing Pass-1-era files (deploy-staging.yml,
deploy-production.yml) were missed.

Symptom: G D.9's populated-state UI shipped to dev correctly per local
build, but the staging deploy rendered no release content because the
loader's first guard (no PAT → return []) silently fired on the runner.

Fix: add PRODUCT_REPOS_PAT to the Build step's env block in both
existing deploy workflows. Matches the master plan's intent and the
pattern already in rebuild-on-dispatch.yml + rebuild-nightly.yml.

Per master plan §4 D.1: "Wire into deploy workflows ... via env:
PRODUCT_REPOS_PAT: ${{ secrets.PRODUCT_REPOS_PAT }} on each build step.
Staging also gets it — the loader's behaviour must match across envs
so no 'works in prod, fails in staging' drift."

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

Resolves three visual issues Lars surfaced via mobile + desktop screenshots:

1. Two horizontal lines on product detail pages (between description
   and 'Release history' heading). Root cause: empty .product-detail__body
   wrapper from Phase C's stub template renders an empty bordered block
   stacked on top of G D.9's .release-history border. Fix: render the
   wrapper conditionally on hasBodyContent (entry.body with HTML comments
   stripped + trimmed). v1 products keep an empty body; future products
   with body markdown still get the wrapper rendered.

2 & 3. Mobile footer overflows iPhone 16 Pro viewport AND entire design
   appears to have right-side padding. Single root cause: .site-footer__nav
   ul lacked flex-wrap on mobile, so the 6 nav items couldn't fit in one
   row at 393px viewport, triggering horizontal body scroll which visually
   shifted the rest of the design left. Fix: add flex-wrap: wrap and a
   denser gap (0.75rem row, 1.25rem column) to the mobile media query.

Pre-existing footer responsive bug surfaced during Phase D verification;
fixing real broken behavior, not retrofitting working code (no-mid-flight-
retrofit rule allows bug fixes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@blackbrowed-labs blackbrowed-labs merged commit f76833a into main Apr 29, 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