Skip to content

Port PickNik's fork onto upstream BehaviorTree.CPP 4.9.0 - #29

Merged
JWhitleyWork merged 166 commits into
mainfrom
port/17640-btcpp-4.9.0
Sep 29, 2026
Merged

JWhitleyWork merged 166 commits into
mainfrom
port/17640-btcpp-4.9.0

Conversation

@dv-picknik

@dv-picknik dv-picknik commented Aug 4, 2026 •

Copy link
Copy Markdown
Member

[written by AI]

Important

DO NOT SQUASH. Merge commit only.
This branch is upstream tag 4.9.0 with our commits replayed on top, not a delta on main. A squash or rebase merge rewrites every SHA and severs the branch from tag 4.9.0, which breaks every future upstream merge, 4.10.0 included.

Advances the fork from the 4.7.2 line to upstream 4.9.0, which is what nav2's Jazzy debs are built against. This is the fix #20928 concluded was necessary. The full commit-by-commit breakdown is on moveit_pro#17640.

15 commits on upstream tag 4.9.0, plus the merge that records main. 525/525 tests pass.

Why a replay and not a merge

The fork's history contains duplicate commits. PR #15 rebased main onto the upstream mirror and then merged the pre-rebase line back in, so most features exist twice. A merge produces one unreviewable blob. Replaying commit by commit also answers the question that actually matters, which is whether each patch is still needed on 4.9.0. For 9 of the 22 the answer was no.

This is intentional and was agreed before the branch was built.

Since this branch was cut

main gained #28, #30, #32, #33 and #34 after 2026-08-04. All of it is carried here. Every fix found along the way is folded into the commit that caused it rather than stacked on top, so each commit still stands on its own.

From main Where it lands here
13c9fe23 generic ReactiveSequence child IDs its own commit, with eac4a51b folded in so the test never lands in the form that crashes the pixi Windows job. The gmock assertions are rewritten with std::string::find, since this branch does not link gmock
7b390305 the three red CI jobs the ci: commit, reapplied rather than cherry-picked, together with ba2fa03b
c4b23ea5, b61a3989 empty subtree defaults already here; c4b23ea5 is itself a backport of this branch's fix, and b61a3989 is folded into it

CI had never run on this branch before now. GitHub cannot build a merge ref for a conflicting PR, so no workflow ever started. Starting it surfaced five things nothing had ever checked. Each one is folded into the commit it came from.

  • Three clang-format violations, one each in the commits that introduced them.
  • A dead foonathan-lexy conan dependency, folded into the packaging commit. 4.9.0 deleted 3rdparty/lexy and nothing in the tree references it, but conan still built it from source on every Windows job, where it dies with "Could not create named generator Visual Studio 18 2026".
  • gtest_json.cpp casting a uint64_t to uint, a POSIX typedef MSVC does not have. Folded into the JSON commit.
  • A bitwise & between two bools in the port type-mismatch guard, folded into the vector<Any> commit. The value is unchanged, since both operands are side-effect-free locals.
  • isVector never matched on MSVC. It matched ^std::vector<.*>$ against a demangled name, but MSVC has no demangler, so demangle() returns typeid().name() verbatim and that name reads class std::vector<double,class std::allocator<double> >. The anchor could never match, so the vector<Any> carve-out never applied and both PortTest.VectorAny and PortTest.LoopNodeAcceptsVector_Issue969 threw. Folded into the vector<Any> commit with a test pinning both toolchains' spellings.

The last three are on main too. They stayed invisible because the fork's Windows job died in conan before it ever reached a compiler or a test.

What this closes

All three ABI breaks from #20928:

# Break How 4.9.0 closes it
1 NodeConfig::auto_remapped inserted mid-struct, shifting manifest and everything after by 8 bytes Ours to place. Appended, offsets verified byte-identical to stock
2 Fork reverted upstream BehaviorTree#965, dropping JsonExporter::from_json_array_converters_ We stop reverting it. The member is upstream's again
3 SimpleString move ctor gained noexcept in 4.9.0, flipping linb::any between heap and inline storage Free, because we are 4.9.0

Break #3 is why the layout-patch approach on fix/20928-btcpp-abi was abandoned. Sizes, offsets and mangled names are all identical, so no layout diff can see it. That branch is retired.

ABI break #1, verified by measurement

offsetof against a stock 4.9.0 checkout, same toolchain:

member stock 4.9.0 original fork this branch
other_attributes 144 152 144
manifest 200 208 200
uid 208 216 208
path 216 224 216
pre_conditions 248 256 248
post_conditions 296 304 296

Warning

git cherry-pick auto-merges tree_node.h with no conflict and silently reinserts the member mid-struct. Resolving conflicts carefully is not enough to avoid reshipping the bug. A one-line offsetof assertion in the suite would have caught #20928 at the commit that introduced it, worth adding separately.

Names that 4.9.0 would refuse

4.9.0's new validateModelName rejects . / \ : < > & " ' * ? | and the space in <BehaviorTree ID> and <SubTree ID>. 4.7.2 did not validate model names at all, so every name an operator ever typed loads today. MoveIt Pro adds no validation of its own, so whatever an operator types becomes a model name.

e11edc21 carves out three characters. Each one appears in a config that works today:

Where it comes from
' ' 1948 violations across 334 files in moveit_pro and moveit_pro_example_ws, every one the space, including Close Gripper and Move to Pose. These IDs are referenced from saved customer configs.
' Robot's Home, pinned by MoveIt Pro's REST suite on the grounds that supported names are XML attribute values rather than interpolated XPath expressions.
. Test Presoak 1.2 in a customer workspace. Version-suffixed names are natural, and renaming is a migration we would be imposing for no benefit we can point at.

None of the three breaks what this validation exists for. All survive a filesystem round-trip, an apostrophe needs no escaping inside the double-quoted attribute value BT.CPP writes, and a dot is not structural here. Node paths are built from / and ::, and the . handling in script_tokenizer.cpp applies to script source, which model names never enter. Port names are a separate namespace and still reject . through IsAllowedPortName. This is ABI-safe, since validateModelName lives in src/xml_parsing.cpp rather than a header, and upstream already permits all three in instance names for the same readability reason.

The carve-out cannot be a comparison

findForbiddenChar returns only the first offender, so filtering on its result hides every later one. Pick & Place passed on the space while its & went unseen, and Robot's Home/Left on the apostrophe while its / did. The carved-out characters are removed first and the remainder is scanned, so the error still names the real offender.

This was live in the original space-only carve-out too, and CI stayed green because every case in the "everything else still throws" test put the offender first. That test now includes Pick & Place, Robot's Home/Left, Robot's <Home>, A B\C, Presoak 1.2|beta and v1.2:final.

Everything else still throws. < > & " break serialization, and / \ : * ? | collide with the path syntax above or with filesystem round-tripping. 4.9.0's ASCII-only script tokenizer and the tightened port-name rule ship as upstream wrote them. A scan of moveit_pro, moveit_pro_example_ws and the 30 most recently pushed PickNikRoboticsServices repos found zero occurrences of either, so they get a migration note rather than a carve-out.

A scan across the 30 most recently pushed PickNikRoboticsServices repos plus both of ours, 1280 BT XML files, reports 0 model-name rejections under the final rule, and no port-name, script, cycle, Root or instance-name findings.

Packaging collision found

4.9.0 adds tools/bt_nodes_model.cpp, installing /opt/ros/*/bin/bt4_nodes_model. Stock ros-jazzy-behaviortree-cpp 4.9.0 installs that exact path, so shipping it unrenamed is a hard dpkg "trying to overwrite" failure. Per the coexistence design's own pitfall section a path-exclude does not help, because dpkg's ownership check runs before the exclude filter. Renamed to bt4_picknik_nodes_model. This block also auto-merges silently, below the conflict region.

The LEXY_ENABLE_INSTALL guard is now obsolete, because 4.9.0 deleted 3rdparty/lexy outright and every remaining vendored tree is STATIC or INTERFACE with no install() rule.

Testing

  • System build, gcc, gtest 1.14: 525/525
  • pixi run build && pixi run test under cmake 4.4.3: 525/525
  • Each commit that absorbed a fix builds and passes on its own. The count climbs 492, 492, 511, 512, 515, 516, 525 across them
  • Red-first checks on both new fixes: reverting only the xml_parsing.cpp hunk fails 5 of the ReactiveSequence tests, and reverting only the isVector regex fails the MSVC spelling
  • pre-commit run --all-files: passes
  • Library builds as libbehaviortree_cpp_picknik.so.4.9.0 with versioned soname .so.4.9
  • NodeConfig offsets measured against a stock 4.9.0 worktree
  • Downstream, per apt_build_farm#65: 75 MoveIt Pro packages build against this branch, moveit_pro_behavior_interface 347 tests, moveit_pro_behavior 3672, moveit_studio_agent 431

One thing is left alone. gtest_subtree.cpp trips MSVC warning C4834 by discarding a [[nodiscard]] return, which upstream fixed in 4.9.1 with (void) casts. It is a warning, not an error, so it can fold in later rather than churn this history now.

One upstream test, PortTest.LoopNodeAcceptsVector_Issue969, failed mid-port. The fork's setOutput stores vectors as vector<Any>, which upstream's new LoopNode does not recognise. A real fork-versus-upstream collision, not a merge artifact, fixed in its own commit.

Decisions that want a reviewer

  1. JSON primitive wire format. Fork emits {"__type":"double","value":3.14}, stock emits bare 3.14. Kept the fork's toJson, since the Pro UI consumes it, and made fromJson accept both. The tagged path is gated on __type naming a primitive rather than on a value field merely existing. Otherwise a registered custom type with its own value member, or the diagnostic blob the non-registered-type commit emits, imports silently as a std::string.
  2. Vector to string format. Fork gives 1;2;3;4, stock gives json:[1,2,3,4], and for an empty vector "" against json:[]. Both round-trip. The fork form additionally covers types with a toStr<T> specialization but no JSON converter, such as vector<float>, which stock throws on. This is the easiest thing to drop if nothing downstream needs ;.
  3. '5' .. 3 returns an empty Any. The widened are_numbers guard steals the concat branch. This is live in the fork today; the port neither introduces nor fixes it. One-token fix (&& op != concat), left alone to keep the port faithful.

Known and accepted

  • Soname .so.4.7 to .so.4.9 is customer-visible. A customer shipping a prebuilt Behavior plugin .so in a derived image gets a load failure until they rebuild. This is the versioned soname working as the coexistence design intends, and it is an accepted risk for this release.
  • Subtree ports now split across input_ports and output_ports. bt_flatbuffer_helper.h iterates only input_ports, so Groot1 serialization omits subtree OUTPUT ports.
  • {=} expansion now applies to every node type, not just subtrees, a deliberate widening.

Follow-on work, separate PRs

  • apt_build_farm needs a git_sha bump with revision reset to 1, then publishes 4.9.0-1noble. Staged in apt_build_farm#65.
  • moveit_pro Dockerfile pin moves from 4.7.2-3noble to 4.9.0-1noble, the .so.4.7 strings move, and every Pro binary is rebuilt.
  • 4.9.0's package.xml adds libsqlite3-dev, libzmq3-dev, tinyxml2 and tinyxml2_vendor, which the build-farm chroot must satisfy.
  • Re-run the zero-file-intersection deb proof against a 4.9.0-derived build. Both sides now derive from the same tree, so any missed scoping is a direct hit.
  • fix: drop SCOPED_TRACE that crashes the pixi Windows job #33 and ci: run push workflows on main #34 are either moot or a trivial rebase once this lands.

Refs PickNikRobotics/moveit_pro#17640, PickNikRobotics/moveit_pro#21174, PickNikRobotics/moveit_pro#20928, PickNikRobotics/moveit_pro#22069, PickNikRobotics/moveit_pro#22500

🤖 Generated with Claude Code

redvinaa and others added 30 commits July 31, 2025 15:18
Signed-off-by: redvinaa <redvinaa@gmail.com>
Signed-off-by: redvinaa <redvinaa@gmail.com>
Signed-off-by: redvinaa <redvinaa@gmail.com>
Co-authored-by: ahuo <ahuo2865189826@gmail.com>
…e#1007)

* fix: use dynamically growing error buffer in ParseScript

* style: format code

* fix: use dynamically growing error buffer in ValidateScript

---------

Co-authored-by: ahuo <ahuo2865189826@gmail.com>
…lite3.h won't be found (BehaviorTree#1002)

Co-authored-by: alejandro.suarez@omron.com <alejandro.suarez@omron.com>
* Refactor VerifyXML to clarify logic

- Reduces duplication in VerifyXML by handling the ID check for built-in
node types up front so they can then be definitively looked up in the
registered nodes.

- Enhances error messaging in VerifyXML by using *either* the node name
  *or* the ID, depending on which is appropriate, instead of leaving
users guessing "which Decorator is wrong"

- Fixes custom Action and Condition nodes using shorthand syntax not
  being properly verified

- Fixes `<Control ID="ReactiveSequence"/>` not being verified with the
  same logic as `<ReactiveSequence/>`

- Fixes `<Action ID="MyAction"/>` not triggering a behavior lookup when
  `<MyAction/>` would.

* fix tests that were failing due to bad assumptions
* Support using minitrace from conan

* Support using tinyxml2 from conan

* Add support for using minicoro from conan

* Add support for using flatbuffers from conan

* Create separate targets for each 3rdparty lib not yet supported by conan so we can avoid exposing the whole 3rdparty folder on target_include_directories
Since this can create some confusion around which headers are actually being included -- the ones from that folder or the ones from conan?
Also fixes the include dirs by using ${CMAKE_CURRENT_SOURCE_DIR} instead of "."

* Fix builds
For whatever reason including zmq.hpp before zmq_addon.hpp (which does include zmq.hpp internally) breaks builds

* Do not include the whole 3rdparty folder, only link in what we need

* Use the regular lexy target

* Do not expose the whole 3rdparty folder as a include_directory

* Add options to opt-out of vendored libraries

* This was shared across both code paths, conan_build.cmake and ament_build.cmake
So it is better to keep this on a single place

* Keep all the find_package calls on the toplevel CMakeLists

* SQLite3 is actually a dependency of cpp-sqlite

* Fix include dirs of the vendored minicoro and flatbuffers

* Define libzmq cmake target on FindZeroMQ to match the conan package

* Improve message. This code path doesn't really mean we're using conan, it just means we're not using ament.

* Use the python version of conanfile.py so we can set the CMake options needed to opt out of vendored dependencies

* Address pre-commit complains

* Use conanfile.py across the board

* Do not look for ZeroMQ directly as it is a dependency of cppzmq
Also only look for cppzmq if BTCPP_GROOT_INTERFACE

* Keep a single copy of zmq.hpp
This header is part of the cppzmq library so it lives on 3rdparty/cppzmq. But for whatever reason there was
another version of this header here. Furthermore it was a differnt version of the library.

* Leave a FIXME for posterity
This target was silently being skiped, not it is explicit

* Leave comment for posterity

* Remove empty line

* Remove unneeded line

* Remove uneeded line

* Remove empty line

* Revert unintentional changes

* Use cmake_layout to support multiconfig

* Update toolchain path on cicd

* Emtpy commit to re-trigger CI

* Use cppzmq from conan

* Use cmake presets on conan builds

* It looks like in windows the preset is called conan-default

* It looks like the preset is only called default for config?

* Fix tests path in windows

* Use lexy from conan

* Force cppstd to 17, conan profile detect uses 14

* Try to fix windows builds

* Remove wildcards cmake options, it has been removed on master

* Update changes after cpp-sqlite removal
this modern approach registers many individual tests instead of a single monolitic test
so if one fails the rest continue running which allows the developer to flag multiple
failing tests on a single run
It also speeds up testing since tests run in parallel
dyackzan and others added 11 commits September 28, 2026 10:02
…ut<T>` to convert `vector<BT::Any>` type to `vector<T>`

* Support vector<Any> -> vector<typename T::value_type> conversion

Don't check port type alignment for vector<Any>

* Convert vector to vector<Any> before placing on the blackboard

Also update checks to allow mismatch when a port was declared as a
vector<T> and we have an input port that takes it in as a vector<Any>

* Update include/behaviortree_cpp/blackboard.h

Co-authored-by: Nathan Brooks <nbbrooks@gmail.com>

* Fix formatting with pre-commit

* Add unit test passing a vector through ports

---------

Co-authored-by: Nathan Brooks <nbbrooks@gmail.com>
(cherry picked from commit 663cfa3)
This aligns with how custom types are represented by the JsonExporter.

* Update both toJson & fromJson functions in the JsonExporter
* Add & update tests

Co-authored by: David Sobek <david.sobek@picknik.ai>

(cherry picked from commit c06e058)
Signed-off-by: Paul Gesel <paul.gesel@picknik.ai>
(cherry picked from commit 79e09ed)
Port-integration fix, no upstream counterpart.

Upstream 4.9.0's LoopNode (issue BehaviorTree#969) accepts SharedQueue<T>, std::vector<T>,
or a string in its 'queue' port. The fork's setOutput<std::vector<T>>() stores
vectors as std::vector<BT::Any>, so none of those casts match and LoopNode
throws "port 'queue' must contain either SharedQueue<T>, std::vector<T>, or a
string". This is a genuine fork-vs-upstream collision, not a merge artifact:
upstream's test PortTest.LoopNodeAcceptsVector_Issue969 fails without this.

Unwrap std::vector<BT::Any> element-by-element back to T, erroring with the
element's own cast failure rather than the generic message when a member does
not fit the loop type.

Refs PickNikRobotics/moveit_pro#17640

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SFjW9VieunSxySXNKSFEmX
Use type_ID instead of element->Name() so the error prints the actual
behavior name (e.g. GetEpickObjectDetectionStatus) rather than the
generic XML tag (e.g. Action). Use element->GetLineNum() instead of
att->GetLineNum() for a correct line number.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
(cherry picked from commit 4d87c12)
Ported from fork commits 2a0f48f + 5596408. Upstream 4.9.0 has not adopted
an equivalent: it still re-parses _autoremap inside recursivelyCreateSubtree,
and still assigns every subtree remapping to config.input_ports regardless of
the direction declared in <TreeNodesModel>.

Deliberate divergence from the original fork commits: NodeConfig::auto_remapped
is APPENDED after post_conditions rather than inserted after output_ports.
The original placement shifted other_attributes/manifest/uid/path/pre_conditions/
post_conditions by 8 bytes, and because TreeNode::getInput<T>() is header-inlined
into callers and dereferences config().manifest, binaries built against stock
headers read that pointer out of the middle of other_attributes and crash. That
was ABI break #1 in moveit_pro#20928. Appending restores every offset:

  member            stock 4.9.0   original fork   this commit
  other_attributes  144           152             144
  manifest          200           208             200
  uid               208           216             208
  path              216           224             216
  pre_conditions    248           256             248
  post_conditions   296           304             296

Verified with offsetof against a stock 4.9.0 checkout, not by inspection.

Dropped from the original commits: the tests/CMakeLists.txt hunk (targeted a
catkin branch and a test-target name 4.9.0 no longer has) and the gmock
dependency it existed to pull in -- the two EXPECT_THAT matchers are rewritten
as plain gtest, so the fork adds no build-time dep upstream lacks.

Refs PickNikRobotics/moveit_pro#17640, PickNikRobotics/moveit_pro#20928

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SFjW9VieunSxySXNKSFEmX
Squash of fork commits be74f60 + 19bfaf2. They must land together: without
19bfaf2's empty() guard, `--value.end()` on an empty vector is UB, so
be74f60 alone is a latent crash rather than a separable feature.

Upstream 4.9.0 has no toStr overload for std::vector<T>, but it does stringify
vectors via the toJsonString fallback plus the BehaviorTree#965 JSON vector converters, so
this is a change of output format, not a new capability, for the four
registered element types:

  vector<int>{1,2,3,4}   stock: json:[1,2,3,4]   fork: 1;2;3;4
  vector<string>{}       stock: json:[]          fork: ""
  vector<float>          stock: throws LogicError  fork: 1.000000;2.000000

Both round-trip -- 4.9.0 taught convertFromString<vector<T>> to strip a "json:"
prefix and it still accepts ';'-delimited input. The fork form additionally
covers element types with a toStr<T> specialization but no JSON converter,
which stock 4.9.0 throws on.

Dropped from the original commits: the throw_unspecialized_error lambda. The
compiler cannot see that a lambda call always throws, so it warns
"control reaches end of non-void function" at both call sites and the
fall-through is UB. Upstream's inline throw is kept instead.

Refs PickNikRobotics/moveit_pro#17640

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SFjW9VieunSxySXNKSFEmX
Ported from fork commits 8972919 + b8d70d6, rebuilt against 4.9.0's build
system rather than cherry-picked -- upstream moved BTCPP_*_DESTINATION into the
top-level CMakeLists.txt and rewrote tools/CMakeLists.txt, so the original
hunks no longer apply.

Keeps the coexistence invariants from
docs_nonpublic/design/behaviortree-cpp-fork-coexistence.md: scoped ament
package / library / include root, versioned soname (now .so.4.9, was .so.4.7),
disjointly-named bin/ tools, and the BTCPP_PICKNIK_FORK compile-time sentinel.

New in 4.9.0 and therefore NOT covered by the original rename commits:
upstream added tools/bt_nodes_model.cpp, installing /opt/ros/*/bin/bt4_nodes_model.
Stock ros-jazzy-behaviortree-cpp 4.9.0 installs that exact path, so shipping it
unrenamed is a hard dpkg "trying to overwrite" failure -- and a path-exclude
does not help, because dpkg's ownership-conflict check runs before the exclude
filter. Renamed to bt4_picknik_nodes_model. Note git cherry-pick auto-merges
this block below the conflict region, so it survives a careless resolution
silently; moveit_pro's Dockerfile assertion would have caught it only after a
full image build.

Dropped as obsolete on 4.9.0:
- the LEXY_ENABLE_INSTALL block -- 4.9.0 deleted 3rdparty/lexy outright,
  replacing it with a hand-written tokenizer/parser. Every remaining vendored
  tree (tinyxml2, minitrace, minicoro, flatbuffers, cppzmq, doxygen-awesome-css)
  is STATIC or INTERFACE with no install() rule, so none leaks into the export
  set and the collision class does not recur.
- the conanfile.py foonathan-lexy requirement and its USE_VENDORED_LEXY cache
  variable. Nothing under src/, include/ or any CMakeLists reads either, but
  conan kept building lexy from source on every Windows job, where it dies with
  "Could not create named generator Visual Studio 18 2026".

The tests/CMakeLists.txt test-target rename IS carried, unlike an earlier
revision of this commit that dropped it. The test binary is not collision
surface, so the rename is not required for coexistence, but #26 and #28
established behaviortree_cpp_picknik_test on main and 4.9.0 hardcodes
behaviortree_cpp_test. Without this the name would regress when the 4.9.0 line
becomes main.

Refs PickNikRobotics/moveit_pro#17640, PickNikRobotics/moveit_pro#20928

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SFjW9VieunSxySXNKSFEmX
Ported from fork commit f4c4ce8 (test only).

The refactor that shipped with it (cc77236) and its test fixups (ceadf1a)
are dropped: upstream 4.9.0 already contains that refactor essentially
verbatim -- same is_builtin precondition block, same lookup_name dispatch,
same reordered BehaviorTree/SubTree branches -- and goes further with
validateModelName, a TryCatch arity check and a kMaxNestingDepth guard. The
fork's version is a strict subset, so cherry-picking it would regress upstream.

The assertions are rewritten with std::string::find instead of gmock's
HasSubstr, so the fork does not add a gmock build dependency upstream lacks.
That is also why ceadf1a's package.xml and tests/CMakeLists.txt hunks are
dropped -- they existed only to pull gmock in for these two matchers.

Note both fixtures trip on an unregistered node (TriggerServer, IsDoorLocked)
rather than on a structural rule, so this guards recursive line-number
reporting, not decorator arity.

Refs PickNikRobotics/moveit_pro#17640

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SFjW9VieunSxySXNKSFEmX
@dv-picknik
dv-picknik force-pushed the port/17640-btcpp-4.9.0 branch 2 times, most recently from e006127 to 401f65d Compare September 28, 2026 16:23
JWhitleyWork
JWhitleyWork previously approved these changes Sep 28, 2026

@JWhitleyWork JWhitleyWork left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Logic makes sense. Checked new dependencies for license compliance - seems good there too. We'll probably need to add a release note in bold like MUST REBUILD WORKSPACE AFTER THIS RELEASE but I doubt people will read it anyway.

@dv-picknik
dv-picknik force-pushed the port/17640-btcpp-4.9.0 branch 2 times, most recently from 939b635 to e9d6040 Compare September 29, 2026 15:25
dv-picknik and others added 5 commits September 29, 2026 09:33
4.9.0's new validateModelName rejects `. / \ : < > & " ' * ? |` and the
space in <BehaviorTree ID>, <SubTree ID> and node type names. 4.7.2 did not
validate model names at all, so every name an operator ever typed loads
today.

MoveIt Pro names every Objective and SubTree in human-readable form and puts
no validation of its own on those names, so whatever an operator types
becomes a model name. Three characters therefore appear in configs that work
now, and enforcing the upstream rule would refuse them:

  ' '   1926 IDs across 334 files in moveit_pro and moveit_pro_example_ws,
        276 distinct, including `Close Gripper` and `Move to Pose`. These IDs
        are referenced from saved customer configs.
  '\''  pinned by MoveIt Pro's REST suite as `Robot's Home`, on the grounds
        that supported names are XML attribute values rather than
        interpolated XPath expressions.
  '.'   found in a customer workspace as `Test Presoak 1.2`. Version-suffixed
        names are natural, and renaming is a migration we would be imposing
        for no benefit we can point at.

None of the three breaks what this validation exists for. All survive a
filesystem round-trip, an apostrophe needs no escaping inside the
double-quoted attribute value BT.CPP writes, and a dot is not structural
here: node paths are built from '/' and "::", and the '.' handling in
script_tokenizer.cpp applies to script source, which model names never enter.
Port names are a separate namespace and still reject '.' through
IsAllowedPortName.

The carve-out cannot be expressed as a comparison against findForbiddenChar's
result. That function returns only the FIRST offender, so a name whose first
offender is carved out hides every later one: `Pick & Place` would pass on the
space while its `&` went unseen. The carved-out characters are removed first
and the remainder is scanned, so the error still names the real offender.

Everything else still throws, pinned by
ForkStillRejectsOtherForbiddenCharsInModelName, which includes cases whose
offender follows a carved-out character. `< > & "` break serialization, and
`/ \ : * ? |` collide with the path syntax above or with filesystem
round-tripping. Upstream permits all three carved-out characters in instance
names for the same human-readability reason, so this narrows the
model/instance gap rather than inventing a new rule. The upstream tests
asserting a space or a period is rejected now assert the fork's behaviour and
are renamed to say so.

Refs PickNikRobotics/moveit_pro#17640

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A SubTree model port declared with an empty default, default="", was
treated as mandatory, so a parent that did not remap it failed to build.
The instantiation check tested the default's string form rather than
whether a default was declared at all, which collapses "declared, defaults
to empty" and "no default, caller must supply one" into the same case.
Gate on the Any instead, as the model writer and the XSD generator already
do.

An empty string is a legitimate default for a real port: an id assigned by
an external system, optional metadata, an optional filter. Without this
there is no way to declare one, and adding such a port to an existing
subtree silently breaks every parent that does not already remap it,
including parents outside this repo.

Fixes PickNikRobotics/moveit_pro#22069. Backported to the 4.7.2 line as
c4b23ea so it could ship in MoveIt Pro 10.1.1 ahead of this branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
VerifyXML's ReactiveSequence async-child scan read the child's tag name,
so a generic <Action ID="Foo"/> looked up "Action" and was rejected as an
unknown node type. Resolve the ID the way the recursive validation below
already does.

Port of 13c9fe2 (#30) onto 4.9.0. The gmock assertions are rewritten with
std::string::find because this branch does not link gmock, matching
fbed232. SCOPED_TRACE is omitted per #33, which crashes the pixi Windows
job; the case is streamed into the assertion instead.

Co-Authored-By: Noah Wardlow <noah.wardlow.0@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

(cherry picked from commit 13c9fe2)
Reapplies 7b39030 (#28) onto 4.9.0, whose CI files differ enough that the
hunks do not cherry-pick, and folds in ba2fa03 (#34).

conan install gains -o "zeromq/*:encryption=tweetnacl". libsodium is the
only package in the graph built by msbuild, and its hand-written .vcxproj
takes PlatformToolset v145 from compiler.version=195 while vcvars lands on
VCToolsVersion 14.44, which msbuild rejects (MSB8052). The copy of
tweetnacl bundled in libzmq drops the dependency. CURVE support is
unchanged.

pixi needs the "Visual Studio 18 2026" generator named explicitly, which
exists only in cmake >= 4.2, so the floor moves and the lock is
regenerated at cmake 4.4.3. That makes pixi.lock format v7, which v0.40.3
cannot read, so setup-pixi and pixi move up with it.

ros2-rolling pins OS_CODE_NAME to noble: rolling now targets Ubuntu 26.04
(resolute), and packages.ros.org publishes no rolling debs for it yet.
Verified 2026-09-28, the resolute dist carries 0 ros-rolling-* packages
against 2150 on noble.

fail-fast is off on both matrices. On pixi a failing windows job was
cancelling ubuntu mid-build; on cmake_windows a failing Debug job was
cancelling Release before it could report.

Push triggers move from master to main, which is #34's change. It covers
one workflow #34 could not, cmake_ubuntu_sanitizers.yml, which exists only
on the 4.9.0 line.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Records main as merged without taking its tree. This branch is not a delta
on main: it is upstream tag 4.9.0 with each of the fork's divergent commits
re-evaluated and reapplied, so main's 4.7.2-based tree is the wrong side of
every file. That replay was agreed before the branch was built.

Everything main carries is accounted for in the commits below:

  663cfa3..b8d70d6  the fork's own 22 commits, evaluated one by one when
                      this branch was built: 13 carried, 9 dropped as already
                      upstream in 4.9.0 or net no-op
  7b39030 (#28)      reapplied in the ci commit, together with
  ba2fa03 (#34)
  13c9fe2 (#30)      reapplied as the ReactiveSequence commit
  eac4a51 (#33)      folded into that same commit, so the test never lands
                      in the form that crashes the pixi Windows job
  c4b23ea, b61a398  c4b23ea is itself a backport of this branch's empty
                      subtree default fix; b61a398's follow-up is folded
                      into it here

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dv-picknik
dv-picknik force-pushed the port/17640-btcpp-4.9.0 branch from e9d6040 to c2ab3f4 Compare September 29, 2026 15:34
@JWhitleyWork
JWhitleyWork added this pull request to the merge queue Sep 29, 2026
Merged via the queue into main with commit 90577d5 Sep 29, 2026
14 checks passed
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.