Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion scripts/tools/daily-checkin-data.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import {
approximateDeployedSha,
buildHealthEmailQuery,
buildWorkersQuery,
collectWorkflowRuns,
deriveBaselineFlag,
Expand Down Expand Up @@ -344,7 +345,7 @@ const d1 = probe(() => {
? d1Query(`SELECT queue, job_type, substr(exception, 1, 160) exception, COUNT(*) count, MIN(failed_at) first_failed_at, MAX(failed_at) last_failed_at FROM failed_jobs WHERE failed_at >= ${sinceSec} GROUP BY queue, job_type, substr(exception, 1, 160) ORDER BY count DESC LIMIT 10`)
: null
const healthEmail = has('daily_health_checks')
? parseHealthEmailRows(d1Query(`SELECT report_date, health_status, delivery_status, recipient, sent_at, error, summary_json FROM daily_health_checks ORDER BY report_date DESC LIMIT 2`))
? parseHealthEmailRows(d1Query(buildHealthEmailQuery()))
: null
const recentJobBatches = has('job_batches')
? d1Query(`SELECT id, name, total_jobs, pending_jobs, failed_jobs, created_at, updated_at, finished_at FROM job_batches ORDER BY updated_at DESC LIMIT 10`)
Expand Down
11 changes: 11 additions & 0 deletions scripts/tools/daily-checkin-observability.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,17 @@ function stringArray(value, field) {
return value
}

// The gate is one sent operator report per Melbourne date, so the read has to
// show enough days to verify continuity. The old two-row page could only ever
// verify the two newest dates, which is how a gap three days back stayed
// invisible. Melbourne runs up to one day ahead of UTC, so eight UTC days is
// the smallest bound that always covers seven Melbourne dates. `report_date`
// is the table's primary key, so a row cap equal to the window can never hide
// an in-window date.
export function buildHealthEmailQuery() {
return `SELECT report_date, health_status, delivery_status, recipient, sent_at, error, summary_json FROM daily_health_checks WHERE report_date >= date('now', '-8 days') ORDER BY report_date DESC LIMIT 8`
}

export function parseHealthEmailRows(rows) {
return rows.map((row) => {
const summary = (() => {
Expand Down
19 changes: 19 additions & 0 deletions test/unit/daily-checkin-observability.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
import {
approximateDeployedSha,
buildHealthEmailQuery,
buildWorkersQuery,
collectWorkflowRuns,
deriveBaselineFlag,
Expand Down Expand Up @@ -107,6 +108,24 @@ describe('daily check-in observability', () => {
)).toContain('filter: {scriptName: "skilld-dev"')
})

// The one-sent-report-per-Melbourne-date gate needs more than the two newest
// days: a two-row page cannot show continuity, so a gap three days back was
// invisible. report_date is the table's primary key, so the row cap can equal
// the window without ever hiding an in-window date.
it('bounds the health email read to a window that covers a week of Melbourne dates', () => {
const query = buildHealthEmailQuery()

const windowDays = Number(query.match(/report_date >= date\('now', '-(\d+) days'\)/)?.[1])
// Melbourne runs up to one day ahead of UTC, so eight UTC days is the
// smallest bound that always covers seven Melbourne dates.
expect(windowDays).toBeGreaterThanOrEqual(8)

const limit = Number(query.match(/LIMIT (\d+)\s*$/)?.[1])
expect(limit).toBeGreaterThanOrEqual(windowDays)

expect(query).toContain('ORDER BY report_date DESC')
})

// The exact shape from issue #195: the routine ran on 2026-09-09 and not
// again until the 21:40Z slot six days later, so every "overnight" rate in
// the archive covered six days while reading as an ordinary window.
Expand Down
Loading