This is an automated email from the ASF dual-hosted git repository.

luigidemasi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-kit.git

commit e1566e2efeba8d3e92aebbe5914a08dacf6f8163
Author: Jinal Sarvaiya <[email protected]>
AuthorDate: Fri Oct 30 22:39:31 2026 +0000

    fix #100: add Pi execution and run-state compatibility verification
---
 CHANGELOG.md                                       |  4 +++
 .../camel/kit/command/ship/ShipCommandTest.java    | 41 ++++++++++++++++++++--
 .../kit/generator/ShippedAssetStructureTest.java   | 10 ++++++
 .../kit/ship/controller/ShipCoordinatorTest.java   | 14 ++++++++
 .../kit/ship/controller/ShipRunStoreTest.java      | 15 ++++++++
 5 files changed, 82 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4da43c01..07a3943b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,6 +37,10 @@ and this project adheres to [Semantic 
Versioning](https://semver.org/spec/v2.0.0
   while preserving Codex CLI sandbox, approval policies and existing session 
permissions. Host-enforced tool restriction
   depends on the Codex CLI version. Fresh and regenerated workspaces receive 
the native skill and worker; runs retain
   their recorded host.
+- **Pi execution and run-state compatibility verification (#100)** — focused 
non-regression tests confirm
+  that Pi runs reject native backends on resume, round-trip `executionMode: 
PI` through the store, preserve
+  Pi execution mode across the full coordinator lifecycle (run, resume, abort, 
recovery, interruption), and
+  that the Pi Ship skill contains no native dispatch language.
 
 ### Changed
 
diff --git 
a/kit/camel-kit-core/src/test/java/org/apache/camel/kit/command/ship/ShipCommandTest.java
 
b/kit/camel-kit-core/src/test/java/org/apache/camel/kit/command/ship/ShipCommandTest.java
index 10592418..d4771ea3 100644
--- 
a/kit/camel-kit-core/src/test/java/org/apache/camel/kit/command/ship/ShipCommandTest.java
+++ 
b/kit/camel-kit-core/src/test/java/org/apache/camel/kit/command/ship/ShipCommandTest.java
@@ -199,6 +199,40 @@ class ShipCommandTest {
         assertNotEquals(task.path("taskId"), freshTask.path("taskId"));
     }
 
+    @Test
+    void piRunRejectsNativeBackendOnResumeAndSubmit() throws Exception {
+        Path project = Files.createDirectory(tempDir.resolve("project"));
+        ShipController controller = controller("state");
+        RecordingLauncher launcher = RecordingLauncher.passThrough(controller);
+
+        RunResult started = run(controller, launcher, "--project-dir", 
project.toString());
+        assertEquals(0, started.exitCode(), started.error());
+        String id = runId(started.output());
+        assertEquals(ShipRun.ExecutionMode.PI, 
controller.status(id).executionMode());
+
+        RunResult mismatch = run(controller, rejectingLauncher(),
+                "--project-dir", project.toString(),
+                "--resume", id, "--backend", "bob2-native");
+        assertEquals(1, mismatch.exitCode());
+        assertTrue(mismatch.error().contains("execution-mode-mismatch"), 
mismatch.error());
+
+        RunResult mismatchClaude = run(controller, rejectingLauncher(),
+                "--project-dir", project.toString(),
+                "--resume", id, "--backend", "claude-native");
+        assertEquals(1, mismatchClaude.exitCode());
+        assertTrue(mismatchClaude.error().contains("execution-mode-mismatch"), 
mismatchClaude.error());
+
+        Path fakeResult = Files.writeString(tempDir.resolve("fake.json"), 
"{}");
+        RunResult submitted = run(controller, 
RecordingLauncher.passThrough(controller),
+                "--project-dir", project.toString(),
+                "--submit", id, "--result", fakeResult.toString());
+        assertEquals(1, submitted.exitCode());
+        assertTrue(submitted.error().contains("execution-mode-mismatch")
+                || submitted.error().contains("no-pending-task")
+                || submitted.error().contains("requires a native backend"),
+                "Pi run must reject --submit: " + submitted.error());
+    }
+
     @Test
     void continuationCommandsCarryTheProjectDirectoryOfTheProjectLocalStore() 
throws Exception {
         Assumptions.assumeTrue(System.getenv("CAMEL_KIT_SHIP_STATE_HOME") == 
null,
@@ -243,12 +277,15 @@ class ShipCommandTest {
 
         assertEquals(0, result.exitCode(), result.error());
         assertEquals("", result.error());
-        assertEquals(summary(id, "RUNNING", "DISCOVERY", "SMART"), 
result.output());
-        assertEquals(List.of("run:" + id), launcher.invocations);
 
         ShipRun run = controller.status(id);
+        assertEquals(ShipRun.ExecutionMode.PI, run.executionMode(),
+                "A bare start without --backend must default to PI");
         assertEquals(project.toAbsolutePath().normalize().toString(), 
run.projectDirectory());
         assertTrue(run.context().sources().isEmpty());
+
+        assertEquals(summary(id, "RUNNING", "DISCOVERY", "SMART"), 
result.output());
+        assertEquals(List.of("run:" + id), launcher.invocations);
     }
 
     @Test
diff --git 
a/kit/camel-kit-core/src/test/java/org/apache/camel/kit/generator/ShippedAssetStructureTest.java
 
b/kit/camel-kit-core/src/test/java/org/apache/camel/kit/generator/ShippedAssetStructureTest.java
index 003b5f76..facde7a0 100644
--- 
a/kit/camel-kit-core/src/test/java/org/apache/camel/kit/generator/ShippedAssetStructureTest.java
+++ 
b/kit/camel-kit-core/src/test/java/org/apache/camel/kit/generator/ShippedAssetStructureTest.java
@@ -886,6 +886,16 @@ class ShippedAssetStructureTest {
             assertFalse(skillContent.contains(guide),
                     agentName + " Ship skill must not reference retired guide 
" + guide);
         }
+        if ("pi".equals(agentName)) {
+            assertFalse(skillContent.contains("--backend pi-native"),
+                    "Pi Ship skill must not reference a pi-native backend");
+            assertFalse(skillContent.contains("spawn_subagent"),
+                    "Pi Ship skill must not dispatch native subagents");
+            assertFalse(skillContent.contains("invoke_subagent"),
+                    "Pi Ship skill must not dispatch native subagents");
+            assertFalse(skillContent.contains("fork_context"),
+                    "Pi Ship skill must not reference fork_context");
+        }
 
         if (!ctx.agent().generatesCommandStubs()) {
             return;
diff --git 
a/kit/camel-kit-core/src/test/java/org/apache/camel/kit/ship/controller/ShipCoordinatorTest.java
 
b/kit/camel-kit-core/src/test/java/org/apache/camel/kit/ship/controller/ShipCoordinatorTest.java
index 5f8f6a5a..89bb2e93 100644
--- 
a/kit/camel-kit-core/src/test/java/org/apache/camel/kit/ship/controller/ShipCoordinatorTest.java
+++ 
b/kit/camel-kit-core/src/test/java/org/apache/camel/kit/ship/controller/ShipCoordinatorTest.java
@@ -302,6 +302,8 @@ class ShipCoordinatorTest {
 
         ShipRun completed = coordinator.run(run.id());
 
+        assertEquals(ShipRun.ExecutionMode.PI, completed.executionMode(),
+                "Pi coordinator must never switch to a native execution mode");
         assertDesignPaused(completed, 1);
         assertEquals(
                 List.of(sessionId(run)),
@@ -318,6 +320,8 @@ class ShipCoordinatorTest {
 
         ShipRun completed = coordinator.resume(run.id(), List.of());
 
+        assertEquals(ShipRun.ExecutionMode.PI, completed.executionMode(),
+                "Pi coordinator must never switch to a native execution mode");
         assertDesignPaused(completed, 1);
         assertEquals(
                 List.of(sessionId(run)),
@@ -341,6 +345,8 @@ class ShipCoordinatorTest {
 
         ShipRun failed = coordinator.run(run.id());
 
+        assertEquals(ShipRun.ExecutionMode.PI, failed.executionMode(),
+                "Pi coordinator must never switch to a native execution mode");
         assertEquals(RunStatus.FAILED, failed.status());
         assertEquals(StageStatus.FAILED,
                 failed.stage(Stage.DESIGN).status());
@@ -394,6 +400,8 @@ class ShipCoordinatorTest {
         Files.writeString(fixture.resolve("mode"), "nonzero\n");
         ShipRun completed = coordinator.resume(run.id(), List.of());
 
+        assertEquals(ShipRun.ExecutionMode.PI, completed.executionMode(),
+                "Pi coordinator must never switch to a native execution mode");
         assertDesignPaused(completed, 1);
         assertEquals(
                 List.of(sessionId(run)),
@@ -507,6 +515,8 @@ class ShipCoordinatorTest {
         Files.writeString(fixture.resolve("mode"), "success\n");
         ShipRun completed = coordinator.resume(run.id(), List.of());
 
+        assertEquals(ShipRun.ExecutionMode.PI, completed.executionMode(),
+                "Pi coordinator must never switch to a native execution mode");
         assertDesignPaused(completed, 2);
         String sessionId = sessionId(run);
         assertEquals(
@@ -567,6 +577,8 @@ class ShipCoordinatorTest {
             assertFalse(invocation.isAlive());
             assertNull(failure.get());
             assertTrue(interrupted.get());
+            assertEquals(ShipRun.ExecutionMode.PI, aborted.executionMode(),
+                    "Pi coordinator must never switch to a native execution 
mode");
             assertEquals(aborted, controller.status(run.id()));
             assertEquals(RunStatus.ABORTED, aborted.status());
             assertEquals(StageStatus.ABORTED,
@@ -713,6 +725,8 @@ class ShipCoordinatorTest {
 
         ShipRun completed = coordinator.resume(run.id(), List.of());
 
+        assertEquals(ShipRun.ExecutionMode.PI, completed.executionMode(),
+                "Pi coordinator must never switch to a native execution mode");
         assertEquals(RunStatus.PAUSED, completed.status());
         assertEquals(Stage.DESIGN, completed.currentStage());
         assertEquals(StageStatus.COMPLETED,
diff --git 
a/kit/camel-kit-core/src/test/java/org/apache/camel/kit/ship/controller/ShipRunStoreTest.java
 
b/kit/camel-kit-core/src/test/java/org/apache/camel/kit/ship/controller/ShipRunStoreTest.java
index dd1d65a7..b454f537 100644
--- 
a/kit/camel-kit-core/src/test/java/org/apache/camel/kit/ship/controller/ShipRunStoreTest.java
+++ 
b/kit/camel-kit-core/src/test/java/org/apache/camel/kit/ship/controller/ShipRunStoreTest.java
@@ -20,6 +20,7 @@ import org.junit.jupiter.api.io.TempDir;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -51,6 +52,20 @@ class ShipRunStoreTest {
         assertCode("state-corrupt", () -> store.read(RUN_ID));
     }
 
+    @Test
+    void piRunRetainsExecutionModeAfterRoundTrip() throws Exception {
+        ShipRunStore store = store();
+        ShipRun pi = run(RUN_ID);
+        assertEquals(ShipRun.ExecutionMode.PI, pi.executionMode());
+        store.create(pi);
+        ShipRun read = store.read(RUN_ID);
+        assertEquals(ShipRun.ExecutionMode.PI, read.executionMode());
+        for (ShipRun.StageRecord stage : read.stages()) {
+            assertNull(stage.nativeEvidence(),
+                    "PI stage " + stage.stage() + " must not carry native 
evidence");
+        }
+    }
+
     @Test
     void createsReadsAndAtomicallyReplacesRunState() throws Exception {
         ShipRunStore store = store();

Reply via email to