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
16 changes: 11 additions & 5 deletions .github/workflows/status.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
name: status

# Rebuilds the public status page on GitHub Pages from the live records in S3
# and the monthly AWS Budget. It runs after the 10:15 deadline check and after
# the 14:30 scoring run (Berlin; the cron times are UTC and land after both in
# summer and winter time), and whenever the page code changes on main.
# and the monthly AWS Budget, and whenever the page code changes on main.
#
# Hourly from 07:17 to 18:17 UTC. That covers the 09:00 forecast, the 10:15
# deadline and the 14:30 scoring run (Berlin), including scoring's up to 3
# hours of waiting for late prices, in summer and winter time. GitHub often
# starts scheduled runs hours late, so with one run an hour a late start
# costs about an hour of staleness instead of half a day. Minute 17 avoids
# the busy top of the hour. Each build is a handful of S3 reads and a free
# Budgets API call, and Actions minutes are free on a public repo.
#
# GitHub pauses scheduled workflows after 60 days without commits; a manual
# run (workflow_dispatch) starts them again.

on:
schedule:
- cron: "30 9 * * *"
- cron: "15 14 * * *"
- cron: "17 7-18 * * *"
push:
branches: [main]
paths:
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ misses a promise, and keeps the evidence behind every number it publishes.

**[Live status page](https://dhamsey3.github.io/voltcast/)**: the latest forecast
against the cleared price, the running error, what's deployed and the AWS bill.
It's rebuilt twice a day from the service's own records, and the data behind it
It's rebuilt hourly during the day from the service's own records, and the data behind it
is at [`status.json`](https://dhamsey3.github.io/voltcast/status.json).

## Why this problem
Expand Down Expand Up @@ -149,8 +149,8 @@ Berlin.
## Operating it

- [Status page](https://dhamsey3.github.io/voltcast/): rebuilt by the
[status workflow](.github/workflows/status.yml) after the 10:15 deadline check and after
scoring, with a read-only role that sees only published results ([ADR 6](docs/adr/0006-status-page.md))
[status workflow](.github/workflows/status.yml) hourly from 07:17 to 18:17 UTC, with a
read-only role that sees only published results ([ADR 6](docs/adr/0006-status-page.md))
- [Runbook](docs/runbook.md): what each alarm means and what to do
- [Incidents](docs/incidents/README.md): every missed promise, written up
- [Decisions](docs/adr/): ADRs 1 to 6
Expand Down
9 changes: 9 additions & 0 deletions docs/adr/0006-status-page.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,12 @@ the whole service runs at about 1 USD a month.
run starts them again.
- Only live forecasts appear. The backfill replay stays in `voltcast report`
and the README, so the live record is never mixed with hindsight.

## Update, 2026-10-19: hourly rebuilds

In the first days live, GitHub started the two scheduled runs 3 to 4 hours
late (09:30 UTC ran at about 13:20, 14:15 at about 17:20). For most of each
morning the page didn't show that day's forecast. The workflow now runs
hourly from 07:17 to 18:17 UTC, so a late start costs about an hour. The
extra runs are free: a handful of S3 reads, a free Budgets API call, and
free Actions minutes on a public repo.
2 changes: 1 addition & 1 deletion src/voltcast/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ def render(status: dict) -> str:
<h1>Tomorrow's German power prices, forecast and graded every day</h1>
<p class="lede">Each morning voltcast publishes a price for every quarter-hour of tomorrow's {_e(status["region"])}
day-ahead auction, before bidding closes at 12:00. That afternoon it scores itself against the prices that cleared.
This page is rebuilt from the service's own records twice a day.</p>
This page is rebuilt from the service's own records every hour during the day.</p>
<p class="meta">Updated {_e(_when(status["generated_at"]))} · <a href="status.json">status.json</a> ·
<a href="{REPO_URL}">Source and design</a></p>
</header>
Expand Down
Loading