Skip to content

feat: Add Finally control node for guaranteed cleanup - #36

Draft
dv-picknik wants to merge 2 commits into
mainfrom
feat/20169-finally-node
Draft

dv-picknik wants to merge 2 commits into
mainfrom
feat/20169-finally-node

Conversation

@dv-picknik

Copy link
Copy Markdown
Member

[written by AI]

Adds a Finally control node with two children, main and cleanup. It ticks main, then always ticks cleanup, which gives Objectives try/finally semantics for resetting scene state such as collision rules.

<Finally>
  <Sequence>
    <SetCollisionRule .../>
    <ComplexOperation/>
  </Sequence>
  <SetCollisionRule .../>
</Finally>

Cleanup runs in each of these cases:

  • main returns SUCCESS, FAILURE, or SKIPPED. The node returns main's status.
  • main throws. The node prints what() to stderr, halts main, runs cleanup, and returns FAILURE.
  • the node is halted while main is RUNNING. It halts main and ticks cleanup once, synchronously. If cleanup returns RUNNING, it gets halted.

If cleanup fails, the node returns FAILURE even when main succeeded, because a failed cleanup can leave the scene dirty. An exception thrown by cleanup propagates from tick(). During halt() it is printed instead, because ~Tree() calls haltTree() and a throw there would call std::terminate. The XML loader rejects a Finally without exactly 2 children.

Related: PickNikRobotics/moveit_pro#20169, milestone 10.2.0.

TryCatch, which arrived with the 4.9.0 port, doesn't cover this. It skips the catch child on SUCCESS and does not catch exceptions.

Open for review:

  • Converting a main exception to FAILURE follows the issue text, but it drops the NodeExecutionError backtrace everywhere except stderr. The other option is to store a std::exception_ptr and rethrow once cleanup finishes. That keeps the diagnostics, but an outer Fallback could no longer recover.
  • I skipped the single-child decorator variant the issue mentions. It can come later if someone needs it.
  • Finally exists only in our fork. It should go upstream as part of the BT.CPP unfork work.

Validation: behaviortree_cpp_picknik_test passes all 542 tests locally (Release, GCC). That includes 17 new FinallyTest cases covering async main and cleanup, a halt in each phase, exceptions from main and from cleanup on both the tick and halt paths, SKIPPED main, and child-count validation. pre-commit passed on the changed files. picknik:code-reviewer and the CodeRabbit CLI both flagged cleanup throwing during halt, and the second commit fixes it. Writing that SKIPPED test turned up a double cleanup: executeTick() leaves the node RUNNING after a SKIPPED return, so a later halt ran cleanup again. The node now resets itself before returning SKIPPED.

🤖 Generated with Claude Code

dv-picknik and others added 2 commits September 30, 2026 16:01
Finally ticks its main child, then always ticks its cleanup child: after SUCCESS, FAILURE, or SKIPPED, after main throws (printed to stderr, surfaced as FAILURE), and synchronously when halted while main is RUNNING. It returns main's status, or FAILURE if cleanup fails.

Refs PickNikRobotics/moveit_pro#20169

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
halt() also runs from ~Tree(), so a cleanup exception there is printed instead of propagated. A SKIPPED result leaves the node RUNNING in executeTick(), so the node resets itself, keeping a later halt from rerunning cleanup. Adds tests for async main and cleanup, halts in each phase, and SKIPPED main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 30, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant