Back to feed

sledtools/pika branch #24

pika-git-1

ci: pin selector coverage for forge and claude paths

Target branch: master

Merge Commit: 682fd2e238b1ab9d7f0579479776220474d25d88

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

Continuous Integration

CI: failed

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

Open CI Details

Latest run #33 failed

1 failed

head 5a359e2293e28a2757ab729e37bbbe2e94b1efe5 · queued 2026-03-24 13:38:58 · 1 lane(s)

queued 14s · ran 1s

manual rerun of run #29

check-apple-host-sanity · failed

Summary

This branch hardens CI selector coverage by expanding the forge-lanes manifest to include previously untracked paths (AGENTS.md, .agents/, crates/pika-news/, crates/ph/**) and then adds comprehensive pinning tests—in both the Rust ci_manifest test suite and the Python forge-github-ci-shim test suite—that assert specific file-path inputs resolve to exactly the expected CI lane IDs. A shared helper function is extracted in each test file to reduce boilerplate, and several existing tests are tightened from loose contains-checks to exact equality assertions.

Tutorial Steps

Expand forge-lanes.toml path selectors for agent and crate paths

Intent: Ensure that changes to AGENTS.md, the .agents/ directory, crates/pika-news/, and crates/ph/ trigger the correct CI lanes (pika_followup, notifications, and agent_contracts) instead of silently skipping CI.

Affected files: ci/forge-lanes.toml

Evidence
@@ -37,6 +37,8 @@ title = "check-pika-followup"
 staged_linux_target = "pre-merge-pika-followup"
 paths = [
   "ci/forge-lanes.toml",
+  "AGENTS.md",
+  ".agents/**",
@@ -78,10 +80,12 @@ paths = [
   ".github/workflows/pre-merge.yml",
   "scripts/pikaci-staged-linux-remote.sh",
   "scripts/ci-add-known-host.sh",
+  "crates/pika-news/**",
   "crates/pikaci/**",
@@ -78,10 +80,12 @@
+  "crates/ph/**",
   "crates/pikahut/**",
@@ -101,6 +105,8 @@ title = "check-agent-contracts"
 staged_linux_target = "pre-merge-agent-contracts"
 paths = [
   "ci/forge-lanes.toml",
+  "AGENTS.md",
+  ".agents/**",
@@ -113,10 +119,12 @@
+  "crates/pika-news/**",
   "crates/pikaci/**",
+  "crates/ph/**",

Four new glob patterns are added across two lane definitions in ci/forge-lanes.toml:

  1. check-pika-followup lane gains AGENTS.md and .agents/** so that agent workflow documentation and skill definitions trigger the followup lane.
  2. check-pika-followup lane (notifications section) gains crates/pika-news/** and crates/ph/** so forge-web and the ph CLI crate are covered.
  3. check-agent-contracts lane mirrors those same four additions (AGENTS.md, .agents/**, crates/pika-news/**, crates/ph/**).

Without these entries, changes to any of these paths would not trigger the relevant CI lanes, creating a coverage gap where code could land without being validated.

Extract a shared test helper and refactor existing Rust tests

Intent: Reduce duplication across Rust unit tests by extracting a `selected_lane_ids` helper that parses the manifest, runs lane selection for a set of changed paths, and returns lane IDs. Existing tests are refactored to use this helper, making them shorter and more consistent.

Affected files: crates/pika-news/src/ci_manifest.rs

Evidence
@@ -170,13 +170,23 @@ fn matches_any_path(changed_paths: &[String], patterns: &[String]) -> anyhow::Re
 mod tests {
     use super::{parse_manifest, select_branch_lanes};
 
-    #[test]
-    fn docs_only_branch_selects_expected_lanes() {
+    fn selected_lane_ids(changed: &[&str]) -> Vec<String> {
         let manifest =
             parse_manifest(include_str!("../../../ci/forge-lanes.toml")).expect("manifest");
-        let changed = vec!["docs/testing/ci-selectors.md".to_string()];
-        let selected = select_branch_lanes(&manifest, &changed).expect("select lanes");
-        let ids = selected.into_iter().map(|lane| lane.id).collect::<Vec<_>>();
@@ -187,11 +197,7 @@ mod tests {
 
     #[test]
     fn rust_core_branch_selects_heavier_relevant_lanes() {
-        let manifest =
-            parse_manifest(include_str!("../../../ci/forge-lanes.toml")).expect("manifest");
-        let changed = vec!["rust/src/lib.rs".to_string()];
-        let selected = select_branch_lanes(&manifest, &changed).expect("select lanes");
-        let ids = selected.into_iter().map(|lane| lane.id).collect::<Vec<_>>();
+        let ids = selected_lane_ids(&["rust/src/lib.rs"]);

A selected_lane_ids function is introduced at the top of the tests module in ci_manifest.rs. It accepts a slice of path strings, parses the checked-in forge-lanes.toml via include_str!, calls select_branch_lanes, and returns the lane IDs as a Vec<String>.

The two pre-existing tests (docs_only_branch_selects_expected_lanes and rust_core_branch_selects_heavier_relevant_lanes) are rewritten to call this helper, eliminating five duplicated lines per test. This makes adding future selector-pinning tests trivial.

Tighten the pikachat-claude TypeScript lane test to exact-match multiple paths

Intent: Replace the loose contains/not-contains assertions with an exact equality check against a single-element vec, and expand coverage to three representative pikachat-claude paths using a loop.

Affected files: crates/pika-news/src/ci_manifest.rs

Evidence
@@ -201,17 +207,18 @@ mod tests {
 
     #[test]
-    fn typescript_plugin_branch_selects_only_typescript_lane() {
+    fn pikachat_claude_paths_select_only_typescript_lane() {
+        for path in [
+            "pikachat-claude/src/access.test.ts",
+            "pikachat-claude/src/channel-runtime.ts",
+            "pikachat-claude/package.json",
+        ] {
+            assert_eq!(
+                selected_lane_ids(&[path]),
+                vec!["pikachat_typescript".to_string()],
+                "{path} should select only the dedicated TypeScript lane"
+            );
+        }

The previous typescript_plugin_branch_selects_only_typescript_lane test used four separate assert!/assert! calls checking contains/not-contains. The replacement pikachat_claude_paths_select_only_typescript_lane iterates over three representative paths and asserts the returned lane list is exactly ["pikachat_typescript"]. This catches both false negatives (missing the lane) and false positives (extra unwanted lanes) in a single assertion, with a descriptive message identifying which path failed.

Add new Rust pinning tests for forge-web, ph CLI, and agent paths

Intent: Pin the lane selection behavior for the newly added manifest paths so that future changes to forge-lanes.toml are caught by failing tests if they accidentally alter selector routing.

Affected files: crates/pika-news/src/ci_manifest.rs

Evidence
@@ -296,4 +303,36 @@ command = ["./nightly.sh"]
+    #[test]
+    fn forge_web_changes_select_control_plane_lanes() {
+        assert_eq!(
+            selected_lane_ids(&["crates/pika-news/src/web.rs"]),
+            vec!["notifications".to_string(), "agent_contracts".to_string()]
+        );
+    }
+
+    #[test]
+    fn ph_cli_changes_select_control_plane_lanes() {
+        assert_eq!(
+            selected_lane_ids(&["crates/ph/src/lib.rs"]),
+            vec!["notifications".to_string(), "agent_contracts".to_string()]
+        );
+    }
+
+    #[test]
+    fn agents_root_file_selects_agent_workflow_lanes() {
+        assert_eq!(
+            selected_lane_ids(&["AGENTS.md"]),
+            vec!["pika_followup".to_string(), "agent_contracts".to_string()]
+        );
+    }
+
+    #[test]
+    fn agents_skill_selects_agent_workflow_lanes() {
+        assert_eq!(
+            selected_lane_ids(&[".agents/skills/land/SKILL.md"]),
+            vec!["pika_followup".to_string(), "agent_contracts".to_string()]
+        );
+    }

Four new unit tests are added to the Rust test module, each using selected_lane_ids and assert_eq! for exact lane-list matching:

TestInput pathExpected lanes
forge_web_changes_select_control_plane_lanescrates/pika-news/src/web.rs[notifications, agent_contracts]
ph_cli_changes_select_control_plane_lanescrates/ph/src/lib.rs[notifications, agent_contracts]
agents_root_file_selects_agent_workflow_lanesAGENTS.md[pika_followup, agent_contracts]
agents_skill_selects_agent_workflow_lanes.agents/skills/land/SKILL.md[pika_followup, agent_contracts]

These tests directly exercise the path patterns added in step 1, closing the loop between manifest change and test coverage.

Extract a shared load_shim helper in the Python test suite

Intent: Eliminate duplicated importlib boilerplate in the Python test file and make it easy for new tests to load the shim module.

Affected files: scripts/test_forge_github_ci_shim.py

Evidence
@@ -24,6 +24,15 @@ def git(cwd: Path, *args: str) -> str:
     return completed.stdout.strip()
 
 
+def load_shim():
+    spec = importlib.util.spec_from_file_location("forge_github_ci_shim", SCRIPT)
+    assert spec is not None
+    assert spec.loader is not None
+    shim = importlib.util.module_from_spec(spec)
+    spec.loader.exec_module(shim)
+    return shim
+
@@ -39,18 +48,61 @@
-        spec = importlib.util.spec_from_file_location("forge_github_ci_shim", SCRIPT)
-        self.assertIsNotNone(spec)
-        self.assertIsNotNone(spec.loader)
-        shim = importlib.util.module_from_spec(spec)
-        assert spec and spec.loader
-        spec.loader.exec_module(shim)
+        shim = load_shim()

A module-level load_shim() function replaces the five-line inline importlib dance that was previously inlined in the test method. The existing test_lane_to_matrix_entry_marks_apple_github_step_as_apple_remote test is updated to call load_shim(), and the two new tests added in the next step also rely on it. This keeps each test focused on assertions rather than setup mechanics.

Add Python-side pinning tests for forge and agent workflow paths

Intent: Mirror the Rust selector-pinning tests in the Python shim test suite, ensuring the Python CI entrypoint routes the same paths to the same lanes. This provides dual coverage across both the Rust library and the Python orchestration layer.

Affected files: scripts/test_forge_github_ci_shim.py

Evidence
@@ -39,18 +48,61 @@
+    def test_checked_in_manifest_covers_forge_and_agent_workflow_paths(self) -> None:
+        shim = load_shim()
+        manifest = shim.load_manifest(REPO_ROOT / "ci" / "forge-lanes.toml")
+        lanes = shim.lane_catalog(manifest, "branch")
+
+        def selected_ids(path: str) -> list[str]:
+            return [
+                lane["id"]
+                for lane in lanes
+                if not lane.get("paths") or shim.match_path_any([path], lane.get("paths", []))
+            ]
+
+        self.assertEqual(
+            selected_ids("crates/pika-news/src/web.rs"),
+            ["notifications", "agent_contracts"],
+        )
+        self.assertEqual(
+            selected_ids("crates/ph/src/lib.rs"),
+            ["notifications", "agent_contracts"],
+        )
+        self.assertEqual(
+            selected_ids("AGENTS.md"),
+            ["pika_followup", "agent_contracts"],
+        )
+        self.assertEqual(
+            selected_ids(".agents/skills/land/SKILL.md"),
+            ["pika_followup", "agent_contracts"],
+        )
@@ -39,18 +48,61 @@
+    def test_checked_in_manifest_routes_pikachat_claude_paths_to_typescript_lane(self) -> None:
+        shim = load_shim()
+        manifest = shim.load_manifest(REPO_ROOT / "ci" / "forge-lanes.toml")
+        lanes = shim.lane_catalog(manifest, "branch")
+
+        def selected_ids(path: str) -> list[str]:
+            return [
+                lane["id"]
+                for lane in lanes
+                if not lane.get("paths") or shim.match_path_any([path], lane.get("paths", []))
+            ]
+
+        for path in [
+            "pikachat-claude/src/access.test.ts",
+            "pikachat-claude/src/channel-runtime.ts",
+            "pikachat-claude/package.json",
+        ]:
+            self.assertEqual(selected_ids(path), ["pikachat_typescript"], path)

Two new Python test methods are added to ForgeGithubCiShimTests:

  1. test_checked_in_manifest_covers_forge_and_agent_workflow_paths — loads the real forge-lanes.toml, builds the lane catalog via shim.lane_catalog, and asserts exact lane ID lists for four paths: crates/pika-news/src/web.rs, crates/ph/src/lib.rs, AGENTS.md, and .agents/skills/land/SKILL.md.

  2. test_checked_in_manifest_routes_pikachat_claude_paths_to_typescript_lane — performs the same catalog-based selection for three pikachat-claude/ paths and asserts each resolves to exactly ["pikachat_typescript"].

Both tests use a local selected_ids closure that filters the lane catalog using shim.match_path_any, replicating the real selection logic. This gives the Python CI entrypoint the same pinning guarantees established by the Rust tests.

Diff