Phase D — releases pipeline + Cloudflare-fact-verifier + populated detail UI#13
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
docs/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.rebuild-on-dispatch.ymlworkflow listening forrepository_dispatch: product-release-published.rebuild-nightly.yml(cron 03:00 UTC daily) + TECH_STACK §3.4 rewrite (GH-Actions-Cron path documented; rejected-Worker-Cron rationale captured).scripts/checks/{dpf,cwa-retention}.mjs+ 6 fixtures,scripts/run-verifier.mjsorchestrator, schema migration oncloudflare-facts.json, freshness-gate extension (30-day threshold).<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
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.Test plan
7e0949c..<merge-commit>lands on main via merge-commit (per project workflow §8.4).deploy-production.ymlpost-merge./produkteempty state,/produkte/thalura-plugin404,/produkte/pipeline-test-fixture404, both en/ mirrors same. Both privacy pages renderverified_atdate AND 3 hidden ISO meta tags.gh workflow listshowsrebuild-on-dispatch,rebuild-nightly,verify-cloudflare-factsalongside the existing three.workflow_dispatchofverify-cloudflare-factsfor a smoke run against real DPF + CWA sources.plans/HANDOFFS/.Cross-repo work post-merge (out-of-band)
plans/active/pass-2/g-d-8/thalura-adoption-prompt.mdrunbook.🤖 Generated with Claude Code