Pipeline health and circuit breakers¶
The scheduled forecast workflow keeps a small durable health state in .state/pipeline_health.json. The file is uploaded beside the forecast-history assets so consecutive failures survive GitHub Actions runners and cache eviction.
Stage health¶
Four production stages have explicit health and circuit state:
| Stage | Persistent-failure threshold | Purpose |
|---|---|---|
market_data |
2 | input fetch/validation health |
forecast |
2 | forecast execution health |
history |
2 | durable-history publication health |
x_post |
3 | X session/publication health |
A first failure is transient: the stage becomes degraded, but its circuit remains closed. Consecutive failures reaching the threshold are persistent: the stage becomes open and its circuit opens. A successful observation deterministically resets the consecutive-failure count to zero and closes the circuit.
The state records last success/failure times, failure class/detail and a bounded event history. pipeline_health_report.json and pipeline_health_summary.md provide a per-run view.
Publication safety gate¶
Generating and persisting a forecast is intentionally separate from publishing it publicly. The X post is blocked when any of these conditions is present:
- the current market-data validation is unhealthy;
- the current forecast step failed;
- model/feature drift is
severe; - durable-history publication failed in the current run;
- the market-data, forecast or history circuit is open;
- the X-post circuit is open and not ready for a recovery probe.
This means a severe drift state can still be recorded in forecast history while public publication is suppressed. Skipping a public post is preferred to publishing from degraded evidence.
The workflow uses two gates. A preliminary gate checks data/forecast/drift/X health before the X session preflight and idempotency reservation. The durable history and reservation are then persisted. A final gate verifies current history health immediately before the X write.
X circuit recovery¶
Three consecutive X failures open the X circuit. The default cooldown is 120 minutes and can be overridden with BTC_X_CIRCUIT_COOLDOWN_MINUTES.
After the cooldown, exactly one gate evaluation changes the circuit to half_open and allows one recovery probe. A second concurrent/repeated gate sees half_open and blocks. A successful X preflight/publication closes the circuit and resets the counter. A failed probe reopens it and restarts the cooldown.
This behavior combines with the durable X idempotency registry: a health recovery attempt is never allowed to blindly replay an ambiguous prior publication.
Current-run vs persistent failures¶
Circuit thresholds distinguish transient from persistent failures, but some current failures are immediately unsafe for publication. For example, the first invalid market-data validation or the first failed durable-history upload blocks that run even though the corresponding circuit has not yet opened.
A later successful run resets that stage deterministically. No manual counter reset is required for normal recovery.
Optional notification hook¶
If a repository secret named PIPELINE_HEALTH_WEBHOOK_URL is configured, the workflow can POST a small sanitized JSON health notification when health is degraded/open or publication is blocked. The webhook URL itself is never included in the payload or output.
If the secret is absent, notification is a no-op. The core health/circuit behavior does not depend on any external notification service.
Operational files¶
.state/pipeline_health.json— durable cross-run statepipeline_health_report.json— current machine-readable health reportpipeline_health_summary.md— current Actions-friendly summarymarket_data_validation.json— current data-health signaldrift_report.json— current drift signalx_post_status.json— X preflight/publication signalsource_health.json— optional-source quarantine and degradation signal.state/optional_source_retention.json— bounded immutable optional-source replay inputs
All health-state transitions are deterministic from these observations plus the persisted prior state and configured cooldown/thresholds.