Skip to content

feat(executor): stop_plan_at — cancel an RTDL plan at a given op#110

Merged
enkerewpo merged 9 commits into
devfrom
feat/executor-stop-at-op
Jul 3, 2026
Merged

feat(executor): stop_plan_at — cancel an RTDL plan at a given op#110
enkerewpo merged 9 commits into
devfrom
feat/executor-stop-at-op

Conversation

@enkerewpo

@enkerewpo enkerewpo commented Jun 28, 2026

Copy link
Copy Markdown
Member

Summary

Lets the planner/LLM stop a running RTDL plan at a specific op ("after this op finishes" or "the moment it is reached"), plus the read-side tools to inspect what is running first. "Stop" = cancel the whole plan (same teardown as cancel_plan).

Adds three executor builtin MCP capabilities (auto-registered via the BUILTINS table, discovered by pilot like cancel_plan), an executor-logging fix, and a pilot prompt update so the LLM knows the inspect-then-act workflow. No proto/contract change.

LLM-facing tool names: executor.builtin_get_all_plans, executor.builtin_get_plan_status, executor.builtin_stop_plan_at.


New interfaces (inputs / outputs)

1. get_all_plans — list in-flight plans

robonix/system/executor/builtin/get_all_plans

Input: none.

{}

Output (JSON string):

{
  "count": 2,
  "plans": [
    { "plan_id": "1", "description": "run the three shell commands in strict sequence",
      "op_count": 4, "cancelled": false, "stop_points": 0 }
  ]
}
  • Only in-flight plans are returned. A plan is removed from the active table the moment it finishes (success or cancel), so completed/cancelled plans do not appear here (and count excludes them). Use this to act on what is currently running.
  • description is the plan's root-node description (carries the model's rtdl_description), so the LLM can tell which plan is which.
  • op_count includes operator nodes (sequence/parallel) + leaf do nodes. stop_points = number of armed stop points.

2. get_plan_status — inspect one plan's ops + live state

robonix/system/executor/builtin/get_plan_status

Input: plan_id (string, required).

{ "plan_id": "1" }

Output (running plan):

{
  "plan_id": "1",
  "running": true,
  "cancelled": false,
  "ops": [
    { "op_id": "4", "kind": "sequence", "description": "run the three shell commands…",
      "state": "running", "stop_point": null },
    { "op_id": "7", "kind": "do", "description": "echo STEP_THREE_DONE",
      "state": "pending", "stop_point": "on_enter" }
  ]
}
  • kindsequence | parallel | do.
  • statepending | running | succeeded | failed | canceled | timeout | paused (pending = op not started yet).
  • stop_pointon_enter | on_complete | null.
  • Unknown / finished plan → error (it is a query; "not found" fails): get_plan_status: no active RTDL plan '<id>' (it never existed or already finished/cancelled); call get_all_plans to list running plans. Use get_all_plans to check whether a plan is still running; get_plan_status is only for inspecting a plan you know is live.

3. stop_plan_at — arm a stop point on a plan

robonix/system/executor/builtin/stop_plan_at

Input: plan_id (string, required), op_id (string, required), when (string, optional — on_enter | on_complete, default on_complete).

{ "plan_id": "1", "op_id": "7", "when": "on_enter" }

Output (text):

  • success → Stop point set: RTDL plan '1' will be cancelled on_enter op_id=7.
  • plan not active → RTDL plan '1' is not running (already finished or cancelled); no stop point needed. (success no-op — avoids planner retry loops)
  • bad input → error, e.g. stop_plan_at: invalid when 'halt' (expected on_enter or on_complete) / stop_plan_at: op_id must not be empty

Semantics: when execution reaches the node with op_id, the whole plan is cancelled (marked cancelled + running async calls get <contract>/cancel, like cancel_plan; the loop's is_cancelled checks halt the rest).

  • on_enter — cancel before that op's call runs (the op never executes).
  • on_complete — cancel right after that op's call finishes.
  • An op_id that never executes simply never fires; setting on an already-finished plan is a success no-op.

Other changes

  • pilot prompt (planner.rs build_forest_block): the in-flight-trees block now tells the LLM it can get_plan_status to read op_ids/state and stop_plan_at a chosen op — not only cancel_plan the whole plan.
  • executor logging (service.rs): the dispatch log line now includes a bounded args= preview (256 chars) and the success-result preview is widened 120→512 chars, so plan-control calls are readable in rbnx logs -t executor.

Implementation

  • plan_runtime.rs: StopWhen enum; PlanRun.{stop_points, ops, op_state, description}; PlanRuntime::{stop_plan_at_builtin, should_stop_at, trigger_stop, get_plan_status_builtin, get_all_plans_builtin, record_plan_ops, record_op_state}.
  • service.rs: op-id breakpoint checks in the do-node arm of execute_node; op-state recorded at every node_state emit site; op list + description snapshotted at register.
  • dispatch/builtin.rs: dispatch + BuiltinSpec (description + JSON schema) for the three ops.
  • dispatch/async_poll.rs: record live op state per poll tick.
  • README + unit tests.

Validation

  • cargo fmt --all -- --check clean · cargo clippy --workspace --all-targets -- -D warnings clean · cargo test -p robonix-executor31 passed (8 new).
  • End-to-end on the Webots deployment (pilot = gpt-5.5): the LLM autonomously ran get_all_plansget_plan_statusstop_plan_at(on_enter) on a live plan and cancelled it before the target op — full boot/exec/verify logs in the PR comment below.

New builtin MCP capability robonix/system/executor/builtin/stop_plan_at lets
the planner/LLM arm a breakpoint on an in-flight RTDL plan: when execution
reaches the node with a given op_id, the whole plan is cancelled (same teardown
as cancel_plan). 'when' selects the phase — on_enter cancels before the op's
call runs (op never executes); on_complete (default) cancels right after it
finishes. Stop points are stored per-plan and checked at each do node in the
execution loop; firing reuses begin_cancel + async-cancel drain so the existing
is_cancelled checks halt the rest of the plan. Unknown plan = success no-op;
unreferenced op_id never fires.

Adds StopWhen, PlanRun.stop_points, PlanRuntime::{stop_plan_at_builtin,
should_stop_at,trigger_stop}, the builtin dispatch + spec, the do-node
breakpoint checks, README docs, and unit tests.
@github-actions github-actions Bot added type:feature New feature (feat:) comp:executor system/executor comp:docs docs/ and READMEs and removed type:feature New feature (feat:) labels Jun 28, 2026
…el stop in pilot prompt

Companion read-side builtins so the LLM can inspect a running plan before
stopping it (inspect first, then act):

- get_all_plans: list active plan_ids with op_count / cancelled / stop_point
  count (no args).
- get_plan_status(plan_id): per-op {op_id, kind, description, live state,
  armed stop_point}. Executor snapshots the op list at register and records
  each op's state at every node_state emit site (operator terminals, do-node
  result, on_enter cancel, async poll tick) so status reflects live progress.

Pilot's in-flight-trees prompt block now tells the LLM it can get_plan_status
to read op_ids/state and stop_plan_at a chosen op, not just cancel the whole
plan. Adds unit tests for both builtins.

cargo fmt --all / clippy --workspace -D warnings / test --workspace all green.
@github-actions github-actions Bot added type:feature New feature (feat:) comp:pilot system/pilot labels Jun 28, 2026
get_all_plans listed only plan_id + counts, leaving the LLM unable to tell
which running plan is which. Snapshot the root node's description (which carries
the model's rtdl_description, via pick_description's root fallback) into PlanRun
at register time and surface it as each plan's `description`. No proto change —
the executor Plan has no plan-level description field, so the root node's
description is the in-executor equivalent.
@enkerewpo

enkerewpo commented Jun 28, 2026

Copy link
Copy Markdown
Member Author

End-to-end test on the Webots deployment

The LLM agent autonomously drives the new get_all_plansget_plan_statusstop_plan_at flow against a live running plan, and the stop point actually cancels the plan before the targeted op runs.

Test environment

Item Value
Host OS Debian 13 (Linux 6.12, x86_64)
Simulator Webots Tiago, docker container robonix_tiago_sim
Deploy examples/webots (robonix_manifest.yaml), brought up with rbnx boot
System stack atlas / executor / pilot / liaison / scene + primitives (chassis, camera, lidar, audio) + services (memory, mapping, nav2) + skill (explore)
Executor build this branch (feat/executor-stop-at-op), rebuilt + installed before the run
LLM (pilot VLM) gpt-5.5 via https://api.ofox.io/v1 (OpenAI-compatible) — shown in boot as pilot :50071 vlm=gpt-5.5@api.ofox.io
Agent driver rbnx chat (interactive TUI, mid-task steer); logs read with rbnx logs -t executor

The model matters: the whole point is that the LLM itself chooses to call get_all_plans/get_plan_status/stop_plan_at and picks the right plan_id/op_id. gpt-5.5 is the project's pilot model (smaller models mis-emit RTDL JSON); this run used it unchanged.

Local CI (pre-test)

cargo fmt --all -- --check clean · cargo clippy --workspace --all-targets -- -D warnings clean · cargo test -p robonix-executor31 passed (8 new for the runtime methods).

Test flow

  1. rbnx boot brings up the whole webots deploy — section A.
  2. In one rbnx chat session, turn 1 dispatches a 3-step plan: echo STEP_ONE_DONEsleep 180echo STEP_THREE_DONE (strict sequence). Step 1 runs; step 2 starts the 180s sleep.
  3. turn 2 (mid-task steer) asks the agent to stop the plan at step 3. On its own, the LLM calls (full args + results in section B):
    • get_all_plans (args={}) → finds the plan by description (plan_id=1, "run the three shell commands in strict sequence");
    • get_plan_status (args={"plan_id":"1"}) → reads the third step's op_id = 7;
    • stop_plan_at (args={"op_id":"7","plan_id":"1","when":"on_enter"}) → Stop point set: RTDL plan '1' will be cancelled on_enter op_id=7.
  4. When step 2's sleep ends and execution reaches op_id 7, the breakpoint fires: the whole plan is cancelled, STEP_THREE_DONE is never echoed, and plan 1 leaves the active list.

Result

STEP_ONE_DONE ran, STEP_THREE_DONE did not (section C) — the plan was cancelled exactly at the targeted op. The live RTDL forest in the TUI showed step 1 , step 2 (canceled), step 3 · (never executed).

Note: plan_id/op_id are pilot-assigned and surfaced at runtime, so the agent resolves the target via get_all_plans (by description) rather than guessing — exactly the inspect-then-act loop this PR adds.


A. Stack boot (rbnx boot, webots deploy) — all components ACTIVE

[ OK ]  explore             up to date
:: system ::
[ OK ]  atlas               :50051
[ OK ]  executor            :50061
[ OK ]  pilot               :50071  vlm=gpt-5.5@api.ofox.io
[ OK ]  liaison             :50081
[ OK ]  scene               ACTIVE  (no driver)
:: primitive ::
[ OK ]  tiago_chassis       ACTIVE
[ OK ]  tiago_lidar         ACTIVE
[ OK ]  audio_driver        ACTIVE
:: service ::
[ OK ]  memory              ACTIVE  (no driver)
[ OK ]  mapping             ACTIVE
[ OK ]  nav2                ACTIVE
:: skill ::
[ OK ]  explore             INACTIVE  (skill — awaits executor activate)
    Ctrl-C to tear down (or run `rbnx shutdown` from another shell).

B. The agent's plan-control calls (rbnx logs -t executor) — args + results

06-28 16:00:39.599  I executor                 [executor] dispatching call_id=2:0 provider='executor' contract='robonix/system/executor/builtin/get_all_plans' args={}
06-28 16:00:39.599  I executor                 [executor] 'robonix/system/executor/builtin/get_all_plans' ok: {"count":2,"plans":[{"cancelled":false,"description":"run the three shell commands in strict sequence","op_count":4,"plan_id":"1","stop_points":0},{"c
06-28 16:00:39.599  I executor                 [executor] dispatching call_id=2:1 provider='executor' contract='robonix/system/executor/builtin/get_plan_status' args={"plan_id":"1"}
06-28 16:00:39.599  I executor                 [executor] 'robonix/system/executor/builtin/get_plan_status' ok: {"cancelled":false,"ops":[{"description":"run the three shell commands in strict sequence","kind":"sequence","op_id":"4","state":"pending","stop_poi
06-28 16:00:44.801  I executor                 [executor] dispatching call_id=3:0 provider='executor' contract='robonix/system/executor/builtin/stop_plan_at' args={"op_id":"7","plan_id":"1","when":"on_enter"}
06-28 16:00:44.801  I executor                 [executor] 'robonix/system/executor/builtin/stop_plan_at' ok: Stop point set: RTDL plan '1' will be cancelled on_enter op_id=7.

C. Verification — step 3 was prevented

# count ACTUAL run_command executions (not op descriptions in JSON):
$ rbnx logs -t executor | grep -c "run_command' ok: STEP_ONE_DONE"     # step 1 ran
1
$ rbnx logs -t executor | grep -c "run_command' ok: STEP_THREE_DONE"   # step 3 never executed
0

Agent's final report (from rbnx chat): "I set the stop point on the strict three-command plan (plan_id=1) at the third command's op_id=7 with when=on_enter. The plan is now no longer active, and no STEP_THREE_DONE output was observed, so it was stopped before the third command executed."

The dispatch log line printed only call_id/provider/contract, and the
result preview was capped at 120 chars — so plan-control calls
(stop_plan_at/cancel_plan/get_plan_status) were unreadable in the log
(no args, truncated result). Log a bounded args preview (256 chars) on
dispatch and widen the success-result preview to 512, so the executor
log is self-contained for debugging these calls.
@enkerewpo enkerewpo marked this pull request as draft June 28, 2026 08:56
get_plan_status is a query; a stale or already-finished plan_id should be
an error (not found), not a success with running:false. The cancel/stop
commands stay idempotent no-ops, but a read must fail clearly so the LLM
knows the id is wrong — get_all_plans is the way to check liveness.
@enkerewpo enkerewpo marked this pull request as ready for review July 2, 2026 07:51
@syswonder syswonder deleted a comment from github-actions Bot Jul 2, 2026
@syswonder syswonder deleted a comment from github-actions Bot Jul 2, 2026
@enkerewpo

Copy link
Copy Markdown
Member Author

@robonix-ci test

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

✅ robonix CI — 14/14 passed (100%)

suite scenario result rounds
builtin fault_recovery_builtin 3
builtin file_roundtrip 1
builtin run_command 1
cap camera_snapshot 1
cap explore_smoke 2
cap lidar_snapshot 1
cap mapping_save 1
cap memory_roundtrip 1
cap scene_object_fixture 1
cap speech_speak 1
cap voiceprint_list 1
flow fault_recovery 2
flow object_navigation 4
flow patrol_observe 3

Report: open HTML report.
Raw logs: scenario JSONL, provider logs, simulator logs, final caps, and boot logs are in the same artifact.

@KouweiLee KouweiLee left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. I found two issues that look worth fixing before merge:

  1. stop_plan_at never fires for operator-node op_ids.

get_plan_status exposes every plan node, including sequence and parallel nodes, but the executor only checks stop points in the RTDL_DO branch. If the planner chooses the root sequence or a parallel group from the returned status, stop_plan_at succeeds and stores the stop point, but execution never consults it for that node kind. Either check stop points for all node kinds at node entry/completion, or only expose/accept DO-node op_ids.

Relevant code: system/executor/src/service.rs in the RTDL_DO branch, and system/executor/src/plan_runtime.rs where get_plan_status returns all run.ops.

  1. The new plan-control builtins are not treated as control-only trees.

is_control_only currently hides trees that consist only of cancel_plan / cancel_all_plans, so the LLM does not try to cancel its own control action. With this PR, a tree containing only get_plan_status or stop_plan_at can still be advertised as in-flight task work, which reopens the self-control-loop risk this guard was added to prevent. Please include the new control builtins in that classification.

Relevant code: system/pilot/src/planner.rs in is_control_only.

Validation: I attempted cargo test -p robonix-executor --all-targets and cargo test -p robonix-pilot --all-targets, but both fail before reaching tests because build.rs cannot resolve common_interfaces/sensor_msgs/msg/Image.msg from the capability lib roots.

@enkerewpo

Copy link
Copy Markdown
Member Author

Thanks for the PR. I found two issues that look worth fixing before merge:

  1. stop_plan_at never fires for operator-node op_ids.

get_plan_status exposes every plan node, including sequence and parallel nodes, but the executor only checks stop points in the RTDL_DO branch. If the planner chooses the root sequence or a parallel group from the returned status, stop_plan_at succeeds and stores the stop point, but execution never consults it for that node kind. Either check stop points for all node kinds at node entry/completion, or only expose/accept DO-node op_ids.

Relevant code: system/executor/src/service.rs in the RTDL_DO branch, and system/executor/src/plan_runtime.rs where get_plan_status returns all run.ops.

  1. The new plan-control builtins are not treated as control-only trees.

is_control_only currently hides trees that consist only of cancel_plan / cancel_all_plans, so the LLM does not try to cancel its own control action. With this PR, a tree containing only get_plan_status or stop_plan_at can still be advertised as in-flight task work, which reopens the self-control-loop risk this guard was added to prevent. Please include the new control builtins in that classification.

Relevant code: system/pilot/src/planner.rs in is_control_only.

Validation: I attempted cargo test -p robonix-executor --all-targets and cargo test -p robonix-pilot --all-targets, but both fail before reaching tests because build.rs cannot resolve common_interfaces/sensor_msgs/msg/Image.msg from the capability lib roots.

OK

for the common_interfaces not found, please init submodules (git submodule update --init --recursive --force) to fix it

@KouweiLee

Copy link
Copy Markdown
Contributor

Correction on validation: my earlier note about tests being blocked by missing common_interfaces/sensor_msgs/msg/Image.msg was due to my temporary review worktree not having submodules initialized. After initializing submodules (using the configured proxy), both targeted test commands pass:

  • cargo test -p robonix-executor --all-targets: 31 passed
  • cargo test -p robonix-pilot --all-targets: 24 passed

The two review findings above still stand; this only corrects the validation note.

@KouweiLee

Copy link
Copy Markdown
Contributor

I fixed these 2 problems. When CI is ok, we can test it through CI.

Thanks for the PR. I found two issues that look worth fixing before merge:

  1. stop_plan_at never fires for operator-node op_ids.

get_plan_status exposes every plan node, including sequence and parallel nodes, but the executor only checks stop points in the RTDL_DO branch. If the planner chooses the root sequence or a parallel group from the returned status, stop_plan_at succeeds and stores the stop point, but execution never consults it for that node kind. Either check stop points for all node kinds at node entry/completion, or only expose/accept DO-node op_ids.

Relevant code: system/executor/src/service.rs in the RTDL_DO branch, and system/executor/src/plan_runtime.rs where get_plan_status returns all run.ops.

  1. The new plan-control builtins are not treated as control-only trees.

is_control_only currently hides trees that consist only of cancel_plan / cancel_all_plans, so the LLM does not try to cancel its own control action. With this PR, a tree containing only get_plan_status or stop_plan_at can still be advertised as in-flight task work, which reopens the self-control-loop risk this guard was added to prevent. Please include the new control builtins in that classification.

Relevant code: system/pilot/src/planner.rs in is_control_only.

Validation: I attempted cargo test -p robonix-executor --all-targets and cargo test -p robonix-pilot --all-targets, but both fail before reaching tests because build.rs cannot resolve common_interfaces/sensor_msgs/msg/Image.msg from the capability lib roots.

@KouweiLee KouweiLee self-requested a review July 3, 2026 03:00
@KouweiLee

Copy link
Copy Markdown
Contributor

@robonix-ci test

1 similar comment
@enkerewpo

Copy link
Copy Markdown
Member Author

@robonix-ci test

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

❌ robonix CI — no scenario summary

No machine-readable scenario summary was produced. Treat this as an incomplete infrastructure run, not as a verified Webots pass.

Report: no testing report artifact was available for this run.
Action run: open GitHub Actions run.
Raw logs: no testing-logs artifact was uploaded.

@KouweiLee

Copy link
Copy Markdown
Contributor

@robonix-ci test

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

❌ robonix CI — 14/15 passed (93%)

suite scenario result rounds
builtin fault_recovery_builtin 3
builtin file_roundtrip 1
builtin plan_control_builtins 1
builtin run_command 1
cap camera_snapshot 1
cap explore_smoke 2
cap lidar_snapshot 1
cap mapping_save 1
cap memory_roundtrip 1
cap scene_object_fixture 1
cap speech_speak 1
cap voiceprint_list 1
flow fault_recovery 2
flow object_navigation 4
flow patrol_observe 3
Failure details
  • builtin/plan_control_builtins (builtin.plan_control_builtins.jsonl)
    • steps[0] did not match any plan round from 0; expected contracts=['robonix/system/executor/builtin/get_all_plans', 'robonix/system/executor/builtin/get_plan_status', 'robonix/system/executor/builtin/stop_plan_at']; observed round 0: calls=[('robonix/system/executor/builtin/get_all_plans', '1:0'), ('robonix/system/executor/builtin/get_plan_status', '1:1'), ('robonix/system/executor/builtin/stop_plan_at', '1:2')]; rtdl.children[1]: expected leaf 'robonix/system/executor/builtin/get_plan_status' di

LLM analysis
The CI run tested 15 scenarios; 14 passed and 1 failed. The failing scenario is 'plan_control_builtins', which exercises the new executor builtins get_all_plans, get_plan_status, and stop_plan_at. The test expected the plan's ops to have op_id '1', but the actual plan assigned op_ids '16', '17', '18', '19'. This indicates a mismatch between the test fixture's assumption about op_id numbering and the executor's actual op_id assignment logic.

Change summary: Added three new executor builtins: get_all_plans, get_plan_status, stop_plan_at; Added plan_control_builtins test scenario in testing/scenarios/builtin/plan_control.yaml; Modified plan_runtime.rs to support stop points, op state recording, and plan inspection.
Test result: Webots CI ran 15 scenarios (builtin, cap, flow). 14 passed. The 'plan_control_builtins' scenario failed because the test expected op_id '1' in the plan status output, but the actual plan used op_ids '16', '17', '18', '19'. All other scenarios (including camera, lidar, explore, navigation, memory, speech, voiceprint, scene, and fault recovery) passed successfully.
Likely root cause: The test scenario 'plan_control_builtins' assumes that the first op in a plan has op_id '1', but the executor assigns op_ids starting from a higher number (likely based on a global counter or arena index). The observed op_ids '16', '17', '18', '19' suggest the plan was not the first plan created in the session, or the op_id numbering does not start at 1.
Suggested fix / watchout: Update the test scenario 'plan_control_builtins' to not assume a specific op_id value. Instead, use a more flexible assertion, such as checking that the plan has ops with op_ids that are non-empty strings, or capture the actual op_id from get_all_plans and use it in subsequent steps. Alternatively, if the executor should always start op_ids at 1 for each plan, fix the op_id assignment logic in plan_runtime.rs.
Watchouts: The op_id numbering scheme may change in future versions, causing similar test failures.; Other tests that rely on specific op_id values may also break.; The test scenario may need to be made more robust to handle varying op_id assignments..

Report: open HTML report.
Action run: open GitHub Actions run.
Raw logs: scenario JSONL, provider logs, simulator logs, final caps, and boot logs are in the same artifact.

@KouweiLee

Copy link
Copy Markdown
Contributor

@robonix-ci test

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

✅ robonix CI — 15/15 passed (100%)

suite scenario result rounds
builtin fault_recovery_builtin 3
builtin file_roundtrip 1
builtin plan_control_builtins 1
builtin run_command 1
cap camera_snapshot 1
cap explore_smoke 2
cap lidar_snapshot 1
cap mapping_save 1
cap memory_roundtrip 1
cap scene_object_fixture 1
cap speech_speak 1
cap voiceprint_list 1
flow fault_recovery 4
flow object_navigation 4
flow patrol_observe 3

LLM analysis
This CI run tested the new stop_plan_at, get_plan_status, and get_all_plans executor builtins, along with existing capabilities and flows. All 15 scenarios passed (100% rate). The new plan-control builtins were exercised in the plan_control_builtins scenario, which successfully listed active plans, inspected a plan's status, armed a stop point, and verified the stop point was recorded. No regressions were observed in existing functionality.

Change summary: Added get_all_plans, get_plan_status, and stop_plan_at builtin ops to the executor, including their MCP capability declarations and dispatch logic.; Implemented stop_plan_at_builtin, get_plan_status_builtin, and get_all_plans_builtin methods in plan_runtime.rs, with stop-point recording, op-state tracking, and plan metadata snapshotting.; Integrated stop-point checks (should_stop_at) and stop-point triggering (trigger_stop) into the execution loop in service.rs, supporting both on_enter and on_complete phases..
Test result: Webots CI ran 15 scenarios across builtin, cap, and flow families. All passed. The new plan_control_builtins scenario verified: get_all_plans returned 1 plan with plan_id '1', get_plan_status returned the plan's ops with live states, stop_plan_at armed a stop point on op_id '1' with on_complete, and a subsequent get_all_plans confirmed stop_points incremented to 1. Existing scenarios (fault recovery, file roundtrip, run_command, camera/lidar snapshots, explore, mapping, memory, sce
Suggested fix / watchout: No fix needed. Monitor the fake-VLM scene-graph-LLM failures (Expecting value errors) in future runs — they are non-fatal but indicate the fake VLM returns empty responses for relation extraction, which may affect scene graph quality.
Watchouts: Scene graph LLM requests to fake-vlm consistently return empty responses (Expecting value errors), which may degrade scene relation extraction if a real VLM is used later.; The stop_plan_at builtin was tested only on a plan that arms a stop point on itself — testing on plans with external async calls would provide more coverage.; The voiceprint_list scenario returned 'db not initialised' — this is expected in CI but should be monitored for regressions..

Report: open HTML report.
Action run: open GitHub Actions run.
Raw logs: scenario JSONL, provider logs, simulator logs, final caps, and boot logs are in the same artifact.

@enkerewpo enkerewpo merged commit 4146e47 into dev Jul 3, 2026
12 checks passed
@enkerewpo enkerewpo deleted the feat/executor-stop-at-op branch July 3, 2026 13:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp:docs docs/ and READMEs comp:executor system/executor comp:pilot system/pilot type:feature New feature (feat:)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants