Back to feed

sledtools/pika branch #106

pika-orch-incus-cleanup-23

Declare payload manifest identity explicitly

Target branch: master

Merge Commit: 45e6fb69033c082b388287a6b60acf72459aba31

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

10 passed

head 248f021bf2bf8d8315ef0df4d546add524db112c · queued 2026-03-26 02:20:01 · 10 lane(s)

queued 13s · ran 1m 58s

check-pika-rust · success check-pika-followup · success check-notifications · success check-agent-contracts · success check-rmp · success check-pikachat · success check-pikachat-typescript · success check-apple-host-sanity · success check-pikachat-openclaw-e2e · success check-fixture · success

Summary

This branch eliminates implicit payload-manifest identity inference from the CI build scripts and replaces it with explicit, declarative identity fields. Previously, the Python manifest-generation script inside linux-rust.nix guessed the payload kind by probing the filesystem (checking whether a bin/ directory existed). Now, each Nix derivation declares its own PIKACI_PAYLOAD_MANIFEST_KIND, PIKACI_PAYLOAD_MANIFEST_MOUNT_NAME, and PIKACI_PAYLOAD_MANIFEST_GUEST_PATH as environment variables, and the Python script simply reads them. On the Rust side, two new accessor methods on StagedLinuxRustPayloadRole expose the canonical kind and mount-name strings so they can be referenced programmatically elsewhere in the orchestrator, with corresponding unit tests to lock in the expected values.

Tutorial Steps

Add payload_manifest_kind and payload_manifest_mount_name accessors to StagedLinuxRustPayloadRole

Intent: Give the Rust orchestrator model first-class methods that return the canonical manifest identity strings for each payload role, enabling callers to pass these values to the Nix build environment rather than relying on downstream inference.

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

Evidence
@@ -220,6 +220,20 @@ impl StagedLinuxRustPayloadRole {
+    pub fn payload_manifest_kind(self) -> &'static str {
+        match self {
+            Self::WorkspaceDeps => "staged_linux_workspace_deps_v1",
+            Self::WorkspaceBuild => "staged_linux_workspace_build_v1",
+        }
+    }
+
+    pub fn payload_manifest_mount_name(self) -> &'static str {
+        match self {
+            Self::WorkspaceDeps => "workspace_deps_root",
+            Self::WorkspaceBuild => "workspace_build_root",
+        }
+    }

Two new pub fn methods are added to StagedLinuxRustPayloadRole at crates/pikaci/src/model.rs:223-234.

  • payload_manifest_kind returns the schema-level kind string (staged_linux_workspace_deps_v1 or staged_linux_workspace_build_v1).
  • payload_manifest_mount_name returns the mount name used in the manifest's mounts array (workspace_deps_root or workspace_build_root).

Both methods take self by copy (the enum is Copy) and return &'static str, keeping them zero-cost. These strings are the single source of truth that the Nix derivations will now consume instead of re-deriving them.

Add unit tests for the new accessor methods

Intent: Lock in the expected string values for both enum variants so that any accidental rename or mismatch between the Rust model and the Nix layer is caught at `cargo test` time.

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

Evidence
@@ -1085,6 +1099,14 @@ mod tests {
+        assert_eq!(
+            roles[0].payload_manifest_kind(),
+            "staged_linux_workspace_deps_v1"
+        );
+        assert_eq!(
+            roles[0].payload_manifest_mount_name(),
+            "workspace_deps_root"
+        );
@@ -1095,6 +1117,14 @@ mod tests {
+        assert_eq!(
+            roles[1].payload_manifest_kind(),
+            "staged_linux_workspace_build_v1"
+        );
+        assert_eq!(
+            roles[1].payload_manifest_mount_name(),
+            "workspace_build_root"
+        );

The existing test that exercises StagedLinuxRustPayloadRole ordering and path methods is extended with four new assert_eq! calls (two per variant). These appear at crates/pikaci/src/model.rs:1102-1108 and crates/pikaci/src/model.rs:1120-1126.

The assertions verify:

Variantpayload_manifest_kind()payload_manifest_mount_name()
WorkspaceDeps (roles[0])staged_linux_workspace_deps_v1workspace_deps_root
WorkspaceBuild (roles[1])staged_linux_workspace_build_v1workspace_build_root

Declare manifest identity as environment variables in Nix derivations

Intent: Move the source of truth for each derivation's payload kind, mount name, and guest path from runtime heuristics into explicit Nix-level attributes, so the values are visible in the build graph and reviewable without reading the Python script.

Affected files: nix/ci/linux-rust.nix

Evidence
@@ -1759,6 +1753,9 @@ rec {
+    PIKACI_PAYLOAD_MANIFEST_KIND = "staged_linux_workspace_deps_v1";
+    PIKACI_PAYLOAD_MANIFEST_MOUNT_NAME = "workspace_deps_root";
+    PIKACI_PAYLOAD_MANIFEST_GUEST_PATH = "/staged/linux-rust/workspace-deps";
@@ -1771,6 +1768,9 @@ rec {
+    PIKACI_PAYLOAD_MANIFEST_KIND = "staged_linux_workspace_build_v1";
+    PIKACI_PAYLOAD_MANIFEST_MOUNT_NAME = "workspace_build_root";
+    PIKACI_PAYLOAD_MANIFEST_GUEST_PATH = "/staged/linux-rust/workspace-build";

Three new attributes are added to each of the two crane-based derivations in nix/ci/linux-rust.nix:

WorkspaceDeps derivation (~line 1755):

PIKACI_PAYLOAD_MANIFEST_KIND = "staged_linux_workspace_deps_v1";
PIKACI_PAYLOAD_MANIFEST_MOUNT_NAME = "workspace_deps_root";
PIKACI_PAYLOAD_MANIFEST_GUEST_PATH = "/staged/linux-rust/workspace-deps";

WorkspaceBuild derivation (~line 1770):

PIKACI_PAYLOAD_MANIFEST_KIND = "staged_linux_workspace_build_v1";
PIKACI_PAYLOAD_MANIFEST_MOUNT_NAME = "workspace_build_root";
PIKACI_PAYLOAD_MANIFEST_GUEST_PATH = "/staged/linux-rust/workspace-build";

Because these are set as top-level derivation attributes, Nix automatically exports them as environment variables during the build, making them available to the postInstall phase that runs the manifest-generation script.

Consume declared identity in the manifest-generation Python script

Intent: Replace the filesystem-probing heuristic with simple environment-variable reads, making the script purely data-driven and removing the conditional mount-construction logic.

Affected files: nix/ci/linux-rust.nix

Evidence
@@ -109,13 +109,18 @@ let
+    export PIKACI_PAYLOAD_MANIFEST_KIND
+    export PIKACI_PAYLOAD_MANIFEST_MOUNT_NAME
+    export PIKACI_PAYLOAD_MANIFEST_GUEST_PATH
@@ -131,25 +136,14 @@ if os.environ.get("PIKACI_PAYLOAD_MANIFEST_HAS_LIB") == "1":
-kind = "staged_linux_workspace_build_v1" if (out / "bin").is_dir() else "staged_linux_workspace_deps_v1"
+kind = os.environ["PIKACI_PAYLOAD_MANIFEST_KIND"]
+mount_name = os.environ["PIKACI_PAYLOAD_MANIFEST_MOUNT_NAME"]
+guest_path = os.environ["PIKACI_PAYLOAD_MANIFEST_GUEST_PATH"]
@@ -131,25 +136,14 @@
-if kind == "staged_linux_workspace_deps_v1":
-    mounts.append(
-        {
-            "name": "workspace_deps_root",
-            ...
-        }
-    )
-elif kind == "staged_linux_workspace_build_v1":
-    mounts.append(
-        {
-            "name": "workspace_build_root",
-            ...
-        }
-    )
+mounts = [
+    {
+        "name": mount_name,
+        "relative_path": ".",
+        "guest_path": guest_path,
+        "read_only": True,
+    }
+]

The inline Python script undergoes two simplifications:

  1. Kind detection — The old heuristic "staged_linux_workspace_build_v1" if (out / "bin").is_dir() else "staged_linux_workspace_deps_v1" is replaced by a direct os.environ["PIKACI_PAYLOAD_MANIFEST_KIND"] read. This eliminates a fragile assumption that the presence of a bin/ directory determines the payload role.

  2. Mount construction — The if/elif branches that hard-coded different name and guest_path values per kind are collapsed into a single, unconditional mount entry that reads mount_name and guest_path from the environment. This removes ~15 lines of branching logic and makes the script agnostic to which payload role it is building.

Three export statements are added to the surrounding bash wrapper to ensure the variables propagate into the Python subprocess's environment. The net effect is a reduction of about 11 lines in the Nix file while making the manifest generation deterministic from declared inputs rather than inferred filesystem state.

Diff