Back to feed

sledtools/pika branch #31

incus-ci

pikaci: remove Incus lane selector backend logic

Target branch: master

Merge Commit: fa0be53e1ff18caf4c9914ab11725813be8f523c

branch: merged tutorial: ready ci: success
Open CI Details

Continuous Integration

CI: success

Compact status on the review page, with full logs on the CI page.

Open CI Details

Latest run #37 success

8 passed

head 651e6b6c9c9d3a8316ad92634e4e4cba482941e9 · queued 2026-03-24 14:27:37 · 8 lane(s)

queued 14s · ran 24s

check-pika-rust · success check-pika-followup · success check-notifications · success check-agent-contracts · success check-pikachat · success check-pikachat-typescript · success check-pikachat-openclaw-e2e · success check-fixture · success

Summary

This branch removes the granular Incus lane selector backend logic from pikaci, replacing the per-job/per-lane/per-target matching system driven by the PIKACI_REMOTE_LINUX_VM_INCUS_LANES environment variable with a simpler PIKACI_REMOTE_LINUX_VM_BACKEND=incus|microvm|auto override. The selector_key methods on StagedLinuxRustLane and StagedLinuxRustTarget are deleted, the incus_experiment_selectors_match function and its CSV parser are removed, and all test helpers and test cases referencing the old lane-selector env var are either removed or migrated to use the new forced-backend API. A fail-fast guard is added to main() that errors out if the removed PIKACI_REMOTE_LINUX_VM_INCUS_LANES variable is still set, guiding operators toward the replacement.

Tutorial Steps

Add fail-fast guard for the removed environment variable

Intent: Prevent silent misconfiguration by immediately erroring if an operator still has the removed PIKACI_REMOTE_LINUX_VM_INCUS_LANES variable set, directing them to the replacement PIKACI_REMOTE_LINUX_VM_BACKEND variable.

Affected files: crates/pikaci/src/main.rs

Evidence
@@ -86,6 +86,7 @@ enum RunOutputArg {
 fn main() -> anyhow::Result<()> {
     let cli = Cli::parse();
+    fail_if_removed_backend_selector_is_set()?;
@@ -199,6 +200,18 @@ fn main() -> anyhow::Result<()> {
+fn fail_if_removed_backend_selector_is_set() -> anyhow::Result<()> {
+    let Ok(value) = std::env::var("PIKACI_REMOTE_LINUX_VM_INCUS_LANES") else {
+        return Ok(());
+    };
+    if value.trim().is_empty() {
+        return Ok(());
+    };
+    bail!(
+        "PIKACI_REMOTE_LINUX_VM_INCUS_LANES has been removed; use PIKACI_REMOTE_LINUX_VM_BACKEND=incus|microvm|auto"
+    );
+}

A new fail_if_removed_backend_selector_is_set function is called at the very top of main(), before any run options are constructed. It checks whether PIKACI_REMOTE_LINUX_VM_INCUS_LANES is set to a non-empty value and, if so, returns an actionable error message telling the operator to switch to PIKACI_REMOTE_LINUX_VM_BACKEND=incus|microvm|auto.

This is a deliberate "break loudly" pattern: rather than silently ignoring the old variable (which could lead operators to believe their lane-level overrides are still active), the program refuses to start. Empty or unset values are tolerated so that environments that merely export the variable without a value are not affected.

Remove the PIKACI_REMOTE_LINUX_VM_INCUS_LANES constant and selector_key methods

Intent: Delete the per-lane and per-target selector key machinery that mapped jobs to Incus eligibility strings, since backend selection is now a simple global toggle.

Affected files: crates/pikaci/src/model.rs

Evidence
@@ -97,7 +97,6 @@ pub enum RemoteLinuxVmBackend {
-const REMOTE_LINUX_VM_INCUS_LANES_ENV: &str = "PIKACI_REMOTE_LINUX_VM_INCUS_LANES";
@@ -206,39 +205,6 @@ pub enum StagedLinuxRustLane {
-    pub fn selector_key(self) -> &'static str {
-        match self {
-            Self::PikaFollowupAndroidTestCompile => "pika_followup_android_test_compile",
             ...28 variants removed...
@@ -398,20 +364,6 @@ impl StagedLinuxRustTarget {
-    pub fn selector_key(self) -> &'static str {
-        match self {
-            Self::PreMergePikaRust => "pre_merge_pika_rust",
             ...9 variants removed...

Three pieces of the lane-selector infrastructure are removed from model.rs:

  1. The REMOTE_LINUX_VM_INCUS_LANES_ENV constant — no longer referenced anywhere.
  2. StagedLinuxRustLane::selector_key() — a 28-arm match that mapped each lane variant to a snake_case string used for CSV matching.
  3. StagedLinuxRustTarget::selector_key() — a 9-arm match that did the same for target groups.

These methods were only consumed by incus_experiment_selectors_match, which is also removed in this branch. Without a per-job matching system, there is no need for stable string keys on these enums.

Remove the incus_experiment_selectors_match function and its CSV parser

Intent: Eliminate the runtime logic that parsed PIKACI_REMOTE_LINUX_VM_INCUS_LANES into a comma-separated list and matched it against job ID, lane key, target ID, and target key.

Affected files: crates/pikaci/src/model.rs

Evidence
@@ -601,29 +553,6 @@ fn should_default_remote_linux_vm_to_incus(_job: &JobSpec) -> bool {
-fn incus_experiment_selectors_match(job: &JobSpec) -> bool {
-    let Some(lane) = job.staged_linux_rust_lane() else {
-        return false;
-    };
-    ...
-}
-
-fn parse_remote_linux_vm_incus_selectors(raw: &str) -> impl Iterator<Item = &str> {
-    raw.split(',')
-        .map(str::trim)
-        .filter(|value| !value.is_empty())
-}

incus_experiment_selectors_match was the core of the old lane-selector system. It would:

  1. Extract the StagedLinuxRustLane from the job spec.
  2. Read PIKACI_REMOTE_LINUX_VM_INCUS_LANES and parse it as a comma-separated list.
  3. Match each selector against the job ID, the lane's selector_key(), the target's target_id, or the target's selector_key() (case-insensitive).

The companion parse_remote_linux_vm_incus_selectors helper that handled the CSV splitting is also removed. Together these constituted about 25 lines of matching logic that is no longer needed.

Simplify select_remote_linux_vm_backend to remove job-dependent branching

Intent: Make backend selection purely environment-driven by removing the job parameter dependency from the selection function and the should_default_remote_linux_vm_to_incus helper.

Affected files: crates/pikaci/src/model.rs

Evidence
@@ -567,12 +519,12 @@ pub enum PreparedOutputInvocationMode {
-fn select_remote_linux_vm_backend(job: &JobSpec) -> RemoteLinuxVmBackend {
+fn select_remote_linux_vm_backend(_job: &JobSpec) -> RemoteLinuxVmBackend {
     if let Some(forced) = forced_remote_linux_vm_backend() {
         return forced;
     }
-    if should_default_remote_linux_vm_to_incus(job) || incus_experiment_selectors_match(job) {
+    if should_default_remote_linux_vm_to_incus() {
         return RemoteLinuxVmBackend::Incus;
     }
@@ -591,7 +543,7 @@ fn forced_remote_linux_vm_backend() -> Option<RemoteLinuxVmBackend> {
-fn should_default_remote_linux_vm_to_incus(_job: &JobSpec) -> bool {
+fn should_default_remote_linux_vm_to_incus() -> bool {

The select_remote_linux_vm_backend function's job parameter is now unused (prefixed with _). The decision tree simplifies to:

  1. If PIKACI_REMOTE_LINUX_VM_BACKEND is set, use that value (forced override).
  2. If the prepared-output SSH host points at pika-build or localhost, default to Incus.
  3. Otherwise, fall back to Microvm.

The old || incus_experiment_selectors_match(job) disjunction is removed entirely, and should_default_remote_linux_vm_to_incus no longer accepts a &JobSpec parameter since it only inspects the SSH host environment variable. The _job parameter is kept on select_remote_linux_vm_backend to preserve the call-site signature, likely to be cleaned up in a follow-up.

Migrate executor.rs test helper from with_incus_lane_env to with_forced_backend

Intent: Update the executor test helper to work with the new RemoteLinuxVmBackend enum directly instead of raw lane-selector strings.

Affected files: crates/pikaci/src/executor.rs

Evidence
@@ -4071,31 +4071,38 @@ mod tests {
-    fn with_incus_lane_env<T>(value: Option<&str>, action: impl FnOnce() -> T) -> T {
+    fn with_forced_backend<T>(
+        value: Option<RemoteLinuxVmBackend>,
+        action: impl FnOnce() -> T,
+    ) -> T {
@@ -4611,7 +4618,7 @@ mod tests {
-        with_incus_lane_env(Some("pika-actionlint"), || {
+        with_forced_backend(Some(RemoteLinuxVmBackend::Incus), || {

In executor.rs, the test helper with_incus_lane_env is replaced by with_forced_backend, which accepts Option<RemoteLinuxVmBackend> instead of Option<&str>. The helper now sets/removes PIKACI_REMOTE_LINUX_VM_BACKEND (with the string "incus" or "microvm") rather than PIKACI_REMOTE_LINUX_VM_INCUS_LANES.

The single test that used it — remote_linux_vm_prepare_artifact_is_none_for_incus_backend — is updated to pass Some(RemoteLinuxVmBackend::Incus) instead of the lane-selector string "pika-actionlint". This makes the test express its intent more clearly: it is testing behavior under the Incus backend, not behavior when a particular lane is selected.

Remove lane-selector test infrastructure and tests from model.rs

Intent: Delete all test helpers, environment setup/teardown logic, and test cases that exercised the removed lane-selector matching system.

Affected files: crates/pikaci/src/model.rs

Evidence
@@ -667,20 +596,15 @@ mod tests {
-    fn with_incus_lane_env<T>(value: Option<&str>, action: impl FnOnce() -> T) -> T {
-        with_remote_linux_vm_envs(value, None, None, action)
-    }
@@ -689,20 +613,9 @@ mod tests {
-        let previous_lane = std::env::var(super::REMOTE_LINUX_VM_INCUS_LANES_ENV).ok();
         ...removed save/restore of INCUS_LANES env var...
@@ -763,46 +666,6 @@ mod tests {
-    fn staged_linux_lane_exposes_stable_incus_selector_key()
-    fn staged_linux_target_exposes_stable_incus_selector_key()
-    fn remote_linux_vm_incus_selector_parser_ignores_empty_entries()
@@ -868,7 +731,7 @@ mod tests {
-    fn remote_linux_vm_backend_stays_microvm_away_from_pika_build_without_selector()
+    fn remote_linux_vm_backend_stays_microvm_away_from_pika_build()
@@ -889,7 +752,7 @@ mod tests {
-    fn remote_linux_vm_backend_can_select_incus_by_job_id_or_lane_selector()
+    fn remote_linux_vm_backend_env_can_force_incus_away_from_pika_build()

Significant test changes in model.rs:

Removed helpers:

  • with_incus_lane_env — was a convenience wrapper that forwarded to with_remote_linux_vm_envs with the lane-selector parameter.
  • The lane_selectors parameter is removed from with_remote_linux_vm_envs, along with all save/restore logic for PIKACI_REMOTE_LINUX_VM_INCUS_LANES.

Removed tests (3):

  • staged_linux_lane_exposes_stable_incus_selector_key — verified selector_key() on lanes.
  • staged_linux_target_exposes_stable_incus_selector_key — verified selector_key() on targets.
  • remote_linux_vm_incus_selector_parser_ignores_empty_entries — exercised the CSV parser.

Removed tests (2 multi-case):

  • remote_linux_vm_backend_can_select_incus_by_job_id_or_lane_selector — tested matching by job ID and lane key.
  • remote_linux_vm_backend_can_select_incus_by_target_id_or_target_selector — tested matching by target ID and target key.
  • pikachat_openclaw_target_selector_still_opt_in_matches_away_from_pika_build — tested selector matching on a non-pika-build host.

Renamed/migrated tests (2):

  • remote_linux_vm_backend_stays_microvm_away_from_pika_build_without_selectorremote_linux_vm_backend_stays_microvm_away_from_pika_build (removed "without_selector" qualifier).
  • remote_linux_vm_backend_can_select_incus_by_job_id_or_lane_selectorremote_linux_vm_backend_env_can_force_incus_away_from_pika_build (now uses with_remote_linux_vm_backend_env(Some("incus"), ...) instead of lane selectors).

Remove lane-selector test infrastructure from run.rs

Intent: Clean up the run module's test helpers by removing the with_incus_lane_env helper and all references to PIKACI_REMOTE_LINUX_VM_INCUS_LANES, migrating the remaining test to use with_remote_linux_vm_backend_env.

Affected files: crates/pikaci/src/run.rs

Evidence
@@ -4356,72 +4356,14 @@ mod tests {
-    fn with_incus_lane_env<T>(value: Option<&str>, action: impl FnOnce() -> T) -> T {
-        let _guard = ENV_LOCK
             ...58 lines of save/restore logic for three env vars removed...
@@ -4430,6 +4372,0 @@ mod tests {
-        match previous_selector {
-            Some(value) => unsafe {
-                std::env::set_var("PIKACI_REMOTE_LINUX_VM_INCUS_LANES", value)
-            },
-            None => unsafe { std::env::remove_var("PIKACI_REMOTE_LINUX_VM_INCUS_LANES") },
-        }
@@ -4661,7 +4597,7 @@ mod tests {
-        with_incus_lane_env(Some("pika-actionlint"), || {
+        with_remote_linux_vm_backend_env(Some("incus"), || {

The run.rs test module undergoes the largest single deletion: the entire 58-line with_incus_lane_env helper is removed. This helper managed save/restore for three environment variables (INCUS_LANES, BACKEND, and SSH_HOST), but its primary purpose was to set the lane-selector variable.

The with_remote_linux_vm_backend_env helper is also cleaned up: it no longer saves/restores PIKACI_REMOTE_LINUX_VM_INCUS_LANES since that variable is no longer part of the system.

The test build_run_plan_records_incus_backend_prepare_without_fake_nix_build is migrated from with_incus_lane_env(Some("pika-actionlint"), ...) to with_remote_linux_vm_backend_env(Some("incus"), ...), directly expressing that it wants the Incus backend forced on rather than relying on a lane selector to trigger it.

Update migration plan documentation

Intent: Reflect the removal of the lane selector in the incus-migration-plan.md document so it accurately describes the current state of operator overrides.

Affected files: docs/incus-migration-plan.md

Evidence
@@ -867,8 +867,8 @@ Current `pika-build` proof status:
-  `PIKACI_REMOTE_LINUX_VM_BACKEND=microvm` remains the rollback escape hatch, and
-  `PIKACI_REMOTE_LINUX_VM_INCUS_LANES` still exists as a narrower debug selector
+  `PIKACI_REMOTE_LINUX_VM_BACKEND=incus|microvm|auto` is now the only supported
+  operator override for backend selection

The docs/incus-migration-plan.md document is updated to reflect that PIKACI_REMOTE_LINUX_VM_INCUS_LANES no longer exists. The new text states that PIKACI_REMOTE_LINUX_VM_BACKEND=incus|microvm|auto is now the only supported operator override for backend selection, replacing the previous language about INCUS_LANES being a "narrower debug selector" alongside the backend override.

Diff