Remove CiTargetHealthState enum and lane fields from the shared forge model
Intent: Delete the CiTargetHealthState enum, the TargetUnhealthy/BlockedByTargetHealth execution reason variants, and the target_health_state/target_health_summary fields from CiLane so that downstream crates no longer reference target health concepts at the API boundary.
Affected files: crates/pika-forge-model/src/lib.rs
Evidence
@@ -99,8 +99,6 @@ string_enum! {
Waiting => "waiting",
WaitingForCapacity => "waiting_for_capacity",
BlockedByConcurrencyGroup => "blocked_by_concurrency_group",
- TargetUnhealthy => "target_unhealthy",
- BlockedByTargetHealth => "blocked_by_target_health",
@@ -191,13 +187,6 @@ impl CiLaneFailureKind {
-string_enum! {
- pub enum CiTargetHealthState {
- Healthy => "healthy",
- Unhealthy => "unhealthy",
- }
-}
@@ -271,10 +260,6 @@ pub struct CiLane {
- pub target_health_state: Option<CiTargetHealthState>,
- pub target_health_summary: Option<String>,
The pika-forge-model crate is the shared API contract between the server (pika-git) and the CLI (ph). Three changes are made here:
-
Execution reason variants removed — TargetUnhealthy and BlockedByTargetHealth are deleted from the CiLaneExecutionReason string enum, along with their label() mapping. The CiLaneExecutionReason enum in the forge model mirrors the one in ci_state.rs; both must stay in sync.
-
CiTargetHealthState enum removed entirely — The string_enum! block that defined Healthy / Unhealthy is deleted. No replacement is needed because health tracking no longer exists.
-
CiLane struct fields removed — target_health_state: Option<CiTargetHealthState> and target_health_summary: Option<String> are dropped from the serializable CiLane struct. Both fields had #[serde(default)] so older clients that still include them in JSON payloads will simply ignore the unknown keys (standard serde behavior with deny_unknown_fields not set).
Remove target health types, constants, and snapshot logic from ci_state.rs
Intent: Delete the CiTargetHealthState enum, the CiTargetHealthSnapshot struct and its methods, the cooloff/timestamp helpers, and the TargetUnhealthy execution reason variant from the internal CI state module so that no in-process health evaluation logic remains.
Affected files: crates/pika-git/src/ci_state.rs
Evidence
@@ -1,14 +1,10 @@
-use chrono::{DateTime, Duration, Utc};
-pub const CI_TARGET_HEALTH_INFRA_FAILURE_THRESHOLD: i64 = 2;
-pub const CI_TARGET_HEALTH_COOLOFF_MINUTES: i64 = 15;
@@ -60,7 +56,6 @@ pub enum CiLaneExecutionReason {
- TargetUnhealthy,
@@ -134,10 +126,6 @@ impl CiLaneFailureKind {
- pub const fn counts_toward_target_health(self) -> bool {
- matches!(self, Self::Timeout | Self::Infrastructure)
- }
@@ -159,80 +147,6 @@
-pub enum CiTargetHealthState {...}
-pub struct CiTargetHealthSnapshot {...}
-pub fn next_target_cooloff_until(...)
-pub fn parse_ci_timestamp(...)
ci_state.rs is the internal domain module for CI lane enums and helper functions. The following are removed:
- Constants
CI_TARGET_HEALTH_INFRA_FAILURE_THRESHOLD (2) and CI_TARGET_HEALTH_COOLOFF_MINUTES (15) — these governed when a target flipped to unhealthy and how long the cooloff lasted.
TargetUnhealthy variant from CiLaneExecutionReason plus its as_str(), label(), and FromStr arms.
counts_toward_target_health() on CiLaneFailureKind — used to decide whether a failure should increment the consecutive counter.
CiTargetHealthState enum (Healthy/Unhealthy) with Display, FromStr, and as_str() implementations.
CiTargetHealthSnapshot struct and its effective_state(), is_currently_unhealthy(), and cooloff_active_until() methods.
next_target_cooloff_until() and parse_ci_timestamp() helper functions.
- All associated unit tests (
target_health_snapshot_only_blocks_while_cooloff_is_active, next_cooloff_until_is_in_the_future).
The chrono import is trimmed down since DateTime, Duration, and Utc are no longer needed in this module.
Strip target health logic from the CI store scheduler and lane finish paths
Intent: Remove all target-health-aware scheduling gates (unhealthy target ID lookups, skip logic during claim, health updates on lane finish) and the hydration/snapshot loading functions from the CI store so that the scheduler treats all targets equally regardless of recent failures.
Affected files: crates/pika-git/src/ci_store.rs
Evidence
@@ -1,16 +1,15 @@
-use std::collections::{HashMap, HashSet};
+use std::collections::HashSet;
@@ -1256,8 +1253,6 @@
- let unhealthy_targets =
- current_unhealthy_target_ids(&tx, Utc::now()).context("load unhealthy ci targets")?;
@@ -1419,11 +1401,11 @@
- let (suite_id, ci_target_key): (i64, Option<String>) = tx
+ let suite_id: i64 = tx
@@ -1449,13 +1431,6 @@
- update_target_health_after_lane_finish(
@@ -1867,137 +1816,6 @@
-fn load_ci_target_health_snapshots(...)
-fn hydrate_lane_target_health(...)
-fn hydrate_nightly_lane_target_health(...)
-fn current_unhealthy_target_ids(...)
-fn update_target_health_after_lane_finish(...)
The ci_store.rs module contains the bulk of the scheduling and state-mutation logic. This is the largest set of deletions in the branch.
Scheduler claim paths
Both claim_pending_branch_ci_lane_runs() and claim_pending_nightly_ci_lane_runs() previously:
- Called
current_unhealthy_target_ids() to build a HashSet<String> of blocked targets.
- For each candidate lane, looked up
ci_target_key and skipped the lane if the target was in the unhealthy set.
Both checks are removed. The only remaining skip condition is the concurrency-group gate.
Lane finish paths
finish_branch_ci_lane_run() and finish_nightly_ci_lane_run() previously fetched ci_target_key alongside the run ID and called update_target_health_after_lane_finish(). Now they only fetch the run ID (SELECT branch_ci_run_id FROM ... / SELECT nightly_run_id FROM ...).
Deleted helper functions
load_ci_target_health_snapshots() — batch-loaded health rows by target ID into a HashMap.
hydrate_lane_target_health() / hydrate_nightly_lane_target_health() — post-load decoration of lane records with their target's health snapshot.
current_unhealthy_target_ids() — queried ci_target_health table and filtered to currently-unhealthy targets.
update_target_health_after_lane_finish() — the 120+ line function that incremented failure counters, flipped state to unhealthy, set cooloff timestamps, or reset on success.
parse_target_health_state() — SQLite row parser for the health state enum.
Record structs
The target_health: Option<CiTargetHealthSnapshot> field is removed from both BranchCiLaneRecord and NightlyLaneRecord.
Execution reason refresh
refresh_queued_lane_reasons_for_table() no longer receives or checks unhealthy_targets. The TargetUnhealthy reason assignment branch is removed, simplifying the function to only handle concurrency-group blocking and capacity-waiting logic.
Add migration 0026 to drop the ci_target_health table and rewrite stale rows
Intent: Provide a SQL migration that resets any lanes stuck with execution_reason='target_unhealthy' back to 'queued' and drops the ci_target_health table so the schema no longer contains health-tracking artifacts.
Affected files: crates/pika-git/migrations/0026_drop_ci_target_health.sql, crates/pika-git/src/storage.rs
Evidence
@@ -0,0 +1,9 @@
+UPDATE branch_ci_run_lanes
+SET execution_reason = 'queued'
+WHERE execution_reason = 'target_unhealthy';
+
+UPDATE nightly_run_lanes
+SET execution_reason = 'queued'
+WHERE execution_reason = 'target_unhealthy';
+
+DROP TABLE IF EXISTS ci_target_health;
@@ -673,6 +671,11 @@ fn migrations() -> Vec<Migration> {
+ Migration {
+ version: 26,
+ name: "0026_drop_ci_target_health",
+ sql: include_str!("../migrations/0026_drop_ci_target_health.sql"),
+ },
@@ -493,12 +493,10 @@ fn ci_queue_state_schema_present
- let has_target_health = table_exists(conn, "ci_target_health")?;
- && has_target_health)
A new migration file 0026_drop_ci_target_health.sql ensures a clean transition for existing databases:
-
Rewrite stale execution reasons — Any branch_ci_run_lanes or nightly_run_lanes rows that still have execution_reason = 'target_unhealthy' are updated to 'queued'. This unblocks lanes that were parked due to the now-removed health gate, allowing them to be picked up by the next scheduler cycle.
-
Drop the table — DROP TABLE IF EXISTS ci_target_health removes the health-tracking table entirely. IF EXISTS is used for idempotency.
In storage.rs:
- The migration is registered as version 26 in the
migrations() vector.
- The
ci_queue_state_schema_present() guard function no longer checks for ci_target_health table existence — the schema is considered complete without it.
- The existing storage test that verified
ci_target_health exists is flipped to assert !table_exists(conn, "ci_target_health").
- A new test
drop_target_health_migration_rewrites_legacy_unhealthy_rows sets up a database at migration 25, inserts rows with target_unhealthy execution reasons and a ci_target_health entry, then opens a Store (which applies migration 26) and verifies the rows were rewritten to queued and the table was dropped.
Remove target health references from web views and API serialization
Intent: Delete the target_health_state and target_health_summary fields from the view structs and API mapping functions, and remove the health-related status tones, so the web layer no longer surfaces health information.
Affected files: crates/pika-git/src/web.rs, crates/pika-git/src/web/api.rs, crates/pika-git/src/web/views.rs, crates/pika-git/templates/branch_ci_live.html, crates/pika-git/templates/nightly_live.html
Evidence
@@ -241,8 +238,6 @@ struct CiLaneView {
- target_health_state: Option<String>,
- target_health_summary: Option<String>,
@@ -271,8 +266,6 @@ struct NightlyLaneView {
- target_health_state: Option<String>,
- target_health_summary: Option<String>,
@@ -437,8 +430,6 @@ fn ci_status_tone
- | "target_unhealthy"
- | "blocked_by_target_health"
@@ -58,10 +58,6 @@ fn map_api_ci_lane_from_view
- target_health_state: view
- .target_health_state
- .map(ForgeApiTargetHealthState::from),
- target_health_summary: view.target_health_summary,
The web layer spans several files:
web.rs (view structs and helpers)
CiLaneView and NightlyLaneView both lose target_health_state: Option<String> and target_health_summary: Option<String>.
ci_status_tone() no longer maps "target_unhealthy" or "blocked_by_target_health" to the "warning" tone.
- The import of
CiTargetHealthSnapshot, CiTargetHealthState, and ForgeApiTargetHealthState is removed.
web/api.rs (API response mapping)
map_api_ci_lane_from_view() and map_api_nightly_lane() both drop the target_health_state and target_health_summary field mappings when constructing the CiLane API response.
web/views.rs (record → view conversion)
The view-building functions that populated target_health_state and target_health_summary from the store records are simplified to omit those fields entirely.
Templates
branch_ci_live.html and nightly_live.html templates have their target-health-related conditional rendering blocks removed.
Update CLI rendering and snapshot logic in the ph crate
Intent: Remove target health display from the CLI lane status renderer, drop the health state from the wait snapshot format, and clean up unused imports so the CLI no longer references health concepts.
Affected files: crates/ph/src/api.rs, crates/ph/src/commands.rs, crates/ph/src/tests.rs
Evidence
@@ -5,8 +5,8 @@
- CiTargetHealthState, ForgeCiStatus, LaneMutationResponse,
+ ForgeCiStatus, LaneMutationResponse,
@@ -382,15 +382,6 @@ fn render_lane_status_line
- if let Some(summary) = lane.target_health_summary.as_deref() {
- line.push_str(" · ");
- line.push_str(summary);
- } else if matches!(
- lane.target_health_state.as_ref(),
- Some(CiTargetHealthState::Unhealthy)
- ) {
- line.push_str(" · target unhealthy");
- }
@@ -412,7 +403,7 @@ fn render_lane_snapshot_fragment
- "{}:{}:{}:{}:{}",
+ "{}:{}:{}:{}",
@@ -211,7 +211,7 @@
-fn branch_status_renders_blocked_unhealthy_and_failure_details() {
+fn branch_status_renders_waiting_and_failure_details() {
The ph CLI crate renders branch status to the terminal and computes a stable snapshot string used by branch wait to detect state changes.
api.rs
The re-export of CiTargetHealthState is removed from the import block.
commands.rs
render_lane_status_line() — The block that appended " · <target_health_summary>" or " · target unhealthy" is deleted. Lane status lines now end after the failure kind (if present) and the target/run ID suffix.
render_lane_snapshot_fragment() — The format string drops from 5 colon-separated fields to 4, removing the target_health_state segment. This means existing branch wait invocations will see a snapshot change on upgrade, which is the correct behavior since the health dimension no longer exists.
tests.rs
- The test is renamed from
branch_status_renders_blocked_unhealthy_and_failure_details to branch_status_renders_waiting_and_failure_details.
- The entire
CiLane entry for the apple-sanity lane (which was the unhealthy-target test case) is removed, and lane_count is reduced from 3 to 2.
- All
target_health_state and target_health_summary fields are removed from the remaining lane constructors.
- Assertions for
"apple-sanity queued · target unhealthy", "target apple-host unhealthy", and "target_unhealthy" in the snapshot are deleted.
Remove target health integration tests from the branch store and web test suites
Intent: Delete the test that verified unhealthy targets block scheduling, the test that tracked infra failures resetting after success, and update remaining tests that referenced health fields so the test suite compiles and passes without target health.
Affected files: crates/pika-git/src/branch_store.rs, crates/pika-git/src/web/tests/api.rs, crates/pika-git/src/web/tests/render.rs, crates/pika-git/src/web/tests/unit.rs
Evidence
@@ -888,32 +888,6 @@ mod tests {
- fn target_health_row(
@@ -1465,7 +1439,7 @@
- fn queue_reasons_and_unhealthy_targets_do_not_block_unrelated_claims() {
+ fn queue_reasons_do_not_block_unrelated_claims() {
@@ -1608,108 +1540,6 @@
- fn target_health_tracks_infra_failures_and_resets_after_success() {
branch_store.rs tests
target_health_row() helper deleted — This helper queried the ci_target_health table directly for assertions; it is no longer needed.
- Test renamed —
queue_reasons_and_unhealthy_targets_do_not_block_unrelated_claims becomes queue_reasons_do_not_block_unrelated_claims. The unhealthy_branch setup, the ci_target_health INSERT, and all assertions about TargetUnhealthy execution reasons and target_health snapshots are removed. The test now only verifies concurrency-group blocking and capacity-waiting behavior.
target_health_tracks_infra_failures_and_resets_after_success deleted entirely — This was a ~100 line integration test that exercised the full health lifecycle: two consecutive infra failures triggering unhealthy state, cooloff blocking claims, cooloff expiry allowing retry, and success resetting the counter. With the feature removed, the entire test is unnecessary.
Web test suites
web/tests/api.rs, web/tests/render.rs, and web/tests/unit.rs all have target_health_state and target_health_summary fields removed from test fixture construction, keeping the test data aligned with the updated structs.