Back to feed

sledtools/pika branch #126

pika-orch-incus-cleanup-23

Refactor pikaci runtime config

Target branch: master

Merge Commit: d93bcb183a7ebf4b02d0610f9a22d8f3f7f81cb4

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 #162 success

6 passed

head d5e9788d4f9000c99099e4ad9edc8c0ca7c825f6 · queued 2026-03-27 00:11:12 · 6 lane(s)

queued 6s · ran 34s

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 refactors the pikaci CI runner's job specification model by replacing three loose top-level fields on JobSpec (staged_linux_command, host_setup_command, mount_host_rust_toolchain) with a single discriminated JobRuntimeConfig enum. The enum has three variants—HostProcess, Incus, and Tart—each carrying only the configuration knobs relevant to that backend. This eliminates impossible field combinations (e.g., host_setup_command on an Incus job), makes backend-specific configuration type-safe and self-documenting, and is enforced by a new structural invariant (PIKACI-005). The change touches the data model, all job construction sites in main.rs and test modules, the public re-exports in lib.rs, and one test helper in the Incus executor module.

Tutorial Steps

Introduce runtime config types in the data model

Intent: Define the new `HostProcessRuntimeConfig`, `IncusRuntimeConfig`, `TartRuntimeConfig` structs and the `JobRuntimeConfig` enum that replaces the three loose fields on `JobSpec`. Wire up accessor methods that extract backend-specific values from the enum, plus a `debug_assert` that the runtime config variant agrees with the execution config's declared runtime kind.

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

Evidence
@@ -30,6 +30,37 @@ pub enum GuestCommand {
@@ -37,10 +68,8 @@ pub struct JobSpec {
@@ -182,12 +211,17 @@ impl JobSpec {
@@ -208,7 +242,10 @@ impl JobSpec {
@@ -216,11 +253,17 @@ impl JobSpec {

The core of the refactor lives in model.rs. Three new structs capture backend-specific knobs:

pub struct HostProcessRuntimeConfig;

pub struct IncusRuntimeConfig {
    pub staged_linux_command: Option<StagedLinuxCommandConfig>,
}

pub struct TartRuntimeConfig {
    pub host_setup_command: Option<&'static str>,
    pub mount_host_rust_toolchain: bool,
}

These are unified under a single enum:

pub enum JobRuntimeConfig {
    HostProcess(HostProcessRuntimeConfig),
    Incus(IncusRuntimeConfig),
    Tart(TartRuntimeConfig),
}

JobSpec loses its three flat fields (staged_linux_command, host_setup_command, mount_host_rust_toolchain) and gains a single runtime_config: JobRuntimeConfig field.

The existing accessor methods (staged_linux_command(), host_setup_command(), mount_host_rust_toolchain()) are preserved for backward compatibility at the call-site level, but now dispatch through match on the runtime config enum. The runtime_kind() method switches from reading self.execution.runtime to deriving the kind from self.runtime_config().kind(), and a debug_assert_eq! in runtime_config() cross-checks that the execution config and runtime config agree—catching misconstruction during development without runtime cost in release builds.

Update public re-exports in lib.rs

Intent: Expose the new runtime config types (`HostProcessRuntimeConfig`, `IncusRuntimeConfig`, `TartRuntimeConfig`, `JobRuntimeConfig`) from the crate's public API so that downstream consumers (primarily `main.rs`) can construct jobs with the new model.

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

Evidence
@@ -5,17 +5,18 @@ mod snapshot;

lib.rs adds the four new types to its pub use model::{...} block:

  • HostProcessRuntimeConfig
  • IncusRuntimeConfig
  • JobRuntimeConfig
  • TartRuntimeConfig

The StagedLinuxCommandConfig type was already re-exported. The diff simply extends the existing pub use statement with the new symbols, keeping alphabetical order.

Refactor job construction in main.rs

Intent: Migrate every job construction site in the CLI entry point from flat fields to the new `JobRuntimeConfig` enum, introducing helper functions `remote_incus_runtime()` and `tart_runtime()` to reduce boilerplate.

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

Evidence
@@ -6,10 +6,12 @@ use catalog::{PikaStagedLinuxLane
@@ -27,6 +29,21 @@ const TART_HOST_SETUP_COMMAND
@@ -34,10 +51,8 @@ fn remote_incus_job_base()
@@ -48,10 +63,8 @@ fn host_local_job_base()
@@ -62,10 +75,8 @@ fn tart_job_base()
@@ -674,7 +685,7 @@ fn target_spec
@@ -964,9 +974,9 @@ fn agent_contract_jobs()
@@ -1268,7 +1290,9 @@ fn pikachat_openclaw_e2e_jobs()
@@ -1405,7 +1435,9 @@ fn rmp_jobs()
@@ -1444,8 +1476,7 @@ fn tart_agent_button_job
@@ -1521,7 +1552,7 @@ fn tart_ios_unit_suite_job
@@ -1561,7 +1592,7 @@ fn tart_ios_ui_test_job
@@ -1622,7 +1653,7 @@ fn tart_desktop_package_tests_job

Two helper functions are introduced at the top of main.rs:

fn remote_incus_runtime(
    staged_linux_command: Option<StagedLinuxCommandConfig>,
) -> JobRuntimeConfig {
    JobRuntimeConfig::Incus(IncusRuntimeConfig {
        staged_linux_command,
    })
}

fn tart_runtime(mount_host_rust_toolchain: bool) -> JobRuntimeConfig {
    JobRuntimeConfig::Tart(TartRuntimeConfig {
        host_setup_command: Some(TART_HOST_SETUP_COMMAND),
        mount_host_rust_toolchain,
    })
}

The three base job constructors (remote_incus_job_base, host_local_job_base, tart_job_base) each replace the three flat fields with a single runtime_config field.

Every individual job spec construction throughout main.rs follows the same mechanical pattern:

  • Incus jobs with a staged command: staged_linux_command: Some(...) becomes runtime_config: remote_incus_runtime(Some(...)).
  • Incus jobs without a staged command: The field is removed entirely (inherited from ..remote_incus_job_base()).
  • Host-local jobs: staged_linux_command: None is removed (inherited from ..host_local_job_base()), or explicitly set to JobRuntimeConfig::HostProcess(HostProcessRuntimeConfig) when overriding the base.
  • Tart jobs: mount_host_rust_toolchain and staged_linux_command: None are replaced with runtime_config: tart_runtime(true/false).

Update the Incus executor test helper

Intent: Fix the one test helper in the Incus executor submodule that constructs a `JobSpec` inline, migrating it to the new runtime config model.

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

Evidence
@@ -645,12 +645,14 @@ pub(super) fn build_snapshot_mount_plan_for_test(

The build_snapshot_mount_plan_for_test function in executor/incus.rs constructs a JobSpec directly. The three flat fields are replaced with:

runtime_config: super::super::JobRuntimeConfig::Incus(
    super::super::IncusRuntimeConfig {
        staged_linux_command: None,
    },
),

The host_setup_command: None and mount_host_rust_toolchain: false lines are removed since they are no longer part of JobSpec.

Migrate executor module tests

Intent: Update all test job constructors and individual test cases in `executor.rs` to use the new runtime config types, removing all references to the deleted flat fields.

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

Evidence
@@ -2473,7 +2473,8 @@ mod tests {
@@ -2518,6 +2519,12 @@ mod tests {
@@ -2525,10 +2532,8 @@ mod tests {
@@ -2539,10 +2544,8 @@ mod tests {
@@ -2553,7 +2556,6 @@ mod tests {
@@ -2612,7 +2614,6 @@ mod tests {
@@ -3126,7 +3127,6 @@ mod tests {
@@ -3196,7 +3196,6 @@ mod tests {
@@ -3658,7 +3657,6 @@ mod tests {
@@ -3724,7 +3722,6 @@ mod tests {
@@ -3782,7 +3779,6 @@ mod tests {
@@ -3825,7 +3821,6 @@ mod tests {

The test module in executor.rs receives the same treatment:

  1. New imports: HostProcessRuntimeConfig, IncusRuntimeConfig, JobRuntimeConfig.
  2. A local remote_incus_runtime() helper mirrors the one in main.rs.
  3. remote_incus_job_base() and host_local_job_base() replace flat fields with runtime_config.
  4. Every test that previously set staged_linux_command: None as an override now simply omits it, relying on the base constructor's default runtime_config.

Migrate model module tests

Intent: Update the extensive test suite in `model.rs` to construct jobs using the new runtime config types, and remove all remaining references to the deleted flat fields.

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

Evidence
@@ -400,10 +443,11 @@ fn should_default_remote_linux_vm_to_incus()
@@ -722,6 +766,14 @@ mod tests {
@@ -729,10 +781,8 @@ mod tests {
@@ -743,10 +793,8 @@ mod tests {
@@ -760,9 +808,9 @@ mod tests {
@@ -856,9 +904,9 @@ mod tests {
@@ -950,7 +1000,6 @@ mod tests {
@@ -972,7 +1021,6 @@ mod tests {

The model.rs test module follows the identical migration pattern:

  • A local remote_incus_runtime() helper is added.
  • remote_incus_job_base() and host_local_job_base() use runtime_config instead of the three flat fields.
  • Tests constructing jobs with staged_linux_command: Some(...) now wrap the value in remote_incus_runtime(Some(...)).
  • Tests that set staged_linux_command: None as an override simply drop the line, inheriting from the base.

Migrate run module tests

Intent: Update all test job construction in the run planner/executor test suite to use the new runtime config model.

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

Evidence
@@ -4450,8 +4450,9 @@ mod tests {
@@ -4551,6 +4552,14 @@ mod tests {
@@ -4558,10 +4567,8 @@ mod tests {
@@ -4572,10 +4579,8 @@ mod tests {
@@ -4627,7 +4632,6 @@ mod tests {
@@ -5130,9 +5133,9 @@ mod tests {
@@ -6528,7 +6526,9 @@ mod tests {
@@ -8290,9 +8296,9 @@ EOF
@@ -8319,7 +8325,6 @@ EOF

The run.rs test module is the largest consumer of JobSpec in tests. The migration is mechanical and consistent with the other modules:

  • New imports for HostProcessRuntimeConfig, IncusRuntimeConfig, JobRuntimeConfig.
  • A local remote_incus_runtime() helper.
  • Updated remote_incus_job_base() and host_local_job_base() constructors.
  • All individual test specs migrated from flat fields to runtime_config.
  • Redundant staged_linux_command: None overrides removed.

Add structural invariant PIKACI-005

Intent: Codify the design rule that backend-specific configuration must live inside the runtime config enum, not as ad-hoc top-level fields on JobSpec. This prevents regression.

Affected files: invariants/invariants.toml

Evidence
@@ -42,3 +42,13 @@ hint = "Focus on JobSpec

A new invariant is appended to invariants/invariants.toml:

[[invariant]]
id = "PIKACI-005"
area = "pikaci"
kind = "must"
statement = "Backend-specific setup lives in explicit runtime config data, not ad hoc top-level JobSpec fields."
scope = ["crates/pikaci/**"]
hint = "Focus on the JobSpec data model and how main.rs builds jobs. Fail if production JobSpec still has loose top-level fields like staged_linux_command, host_setup_command, or mount_host_rust_toolchain."

This invariant complements the existing PIKACI-004 (which forbids runtime dispatch based on naming conventions) by ensuring the data model itself groups backend knobs under the correct variant. Together they enforce that both the data shape and the dispatch logic are explicit and type-safe.

Diff