Skip to content

Fix the Europe/Berlin midnight boundary that makes E2E red and today-queues wrong outside Berlin #196

Description

@alexeygrigorev

Raw On-Call finding from main SHA fdf359b8a8e81f0984de6040defd971f8ef7326f (orchestrator intake).

Validate Backend Browser E2E run https://github.com/DataTalksClub/dataops/actions/runs/31849854631 finished red: 2 failed, 67 passed. The failures are NOT from the #192 merge that triggered the run.

e2e/canonical-capability-behavior.spec.js:311  Home preserves real ready data and honest partial failure recovery
e2e/canonical-route-parity.spec.js:511         contains task and workflow dialog focus and restores recreated route targets

Both exhausted all 3 attempts with fresh fixture IDs, so neither is a flake.

Root cause

The specs date their fixtures in Berlin (berlinToday() at backend/e2e/canonical-route-parity.spec.js:12, and an Intl Berlin format at canonical-capability-behavior.spec.js:331), but the app resolves "today" in the browser timezone:

// frontend/src/core/workspace.js:239
export function todayIsoDate() {
  return toIsoDate(new Date());   // toIsoDate uses getFullYear/getMonth/getDate
}

backend/playwright.config.js sets no timezoneId, so the browser inherits UTC on the runner. The run started 23:17 UTC = 01:17 Berlin the next day, so fixtures were dated 2026-08-15 while the app queried 2026-08-14.

Evidence (proved, not inferred)

Within the same UTC window:

Why this is two problems, not one

  1. CI determinism: any push landing between 22:00 and 24:00 UTC turns E2E red, then the same commit goes green later with no change. This is exactly the kind of standing noise that made this gate untrustworthy for 16+ runs before Fix deterministic /docs 404 failures in frontend module characterization #190.
  2. Probable product defect: this portal is explicitly Berlin-scoped — backend/src/routes/calendar.ts enforces timeZone must be Europe/Berlin — but todayIsoDate() uses browser-local time. An operator working from another timezone would see the wrong "today" queue.

Pinning timezoneId: Europe/Berlin in the Playwright config would make CI deterministic but would MASK the product behavior. Grooming must decide whether the app should resolve "today" in Berlin, and treat the CI fix as a consequence of that decision rather than the fix itself.

Side effect

Capability evidence exits 1 on a failed suite, so it printed no stable-state count. The 4 uncovered states are all home.*, owned by the failing canonical-capability-behavior.spec.js:311 journey. process-docs.unavailable is NOT among them, so #192 emitted its evidence correctly; the 138 to 139 increment simply cannot print until the suite is green.

Do not resolve this by re-running the workflow after 00:00 UTC. The same commit goes green purely because the boundary passed, which records a misleading green.

Related: #190, #192.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1ImportantbugSomething is brokenfrontendFrontend UIneeds groomingRaw intake that needs PM groomingtestingTests and QA

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions