Port PickNik's fork onto upstream BehaviorTree.CPP 4.9.0 - #29
Merged
Merged
Conversation
Signed-off-by: redvinaa <redvinaa@gmail.com>
Signed-off-by: redvinaa <redvinaa@gmail.com>
Co-authored-by: ahuo <ahuo2865189826@gmail.com>
…#1009) 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
dv-picknik
marked this pull request as ready for review
September 28, 2026 15:18
…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)
…numbers (cherry picked from commit 959730c)
(cherry picked from commit 9ab097e)
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
force-pushed
the
port/17640-btcpp-4.9.0
branch
2 times, most recently
from
September 28, 2026 16:23
e006127 to
401f65d
Compare
JWhitleyWork
previously approved these changes
Sep 28, 2026
JWhitleyWork
left a comment
Member
There was a problem hiding this comment.
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
force-pushed
the
port/17640-btcpp-4.9.0
branch
2 times, most recently
from
September 29, 2026 15:25
939b635 to
e9d6040
Compare
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
force-pushed
the
port/17640-btcpp-4.9.0
branch
from
September 29, 2026 15:34
e9d6040 to
c2ab3f4
Compare
JWhitleyWork
approved these changes
Sep 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
[written by AI]
Important
DO NOT SQUASH. Merge commit only.
This branch is upstream tag
4.9.0with our commits replayed on top, not a delta onmain. A squash or rebase merge rewrites every SHA and severs the branch from tag4.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 recordsmain. 525/525 tests pass.Why a replay and not a merge
The fork's history contains duplicate commits. PR #15 rebased
mainonto 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
maingained #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.main13c9fe23generic ReactiveSequence child IDseac4a51bfolded in so the test never lands in the form that crashes the pixi Windows job. The gmock assertions are rewritten withstd::string::find, since this branch does not link gmock7b390305the three red CI jobsci:commit, reapplied rather than cherry-picked, together withba2fa03bc4b23ea5,b61a3989empty subtree defaultsc4b23ea5is itself a backport of this branch's fix, andb61a3989is folded into itCI 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.
foonathan-lexyconan dependency, folded into the packaging commit. 4.9.0 deleted3rdparty/lexyand 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.cppcasting auint64_ttouint, a POSIX typedef MSVC does not have. Folded into the JSON commit.&between two bools in the port type-mismatch guard, folded into thevector<Any>commit. The value is unchanged, since both operands are side-effect-free locals.isVectornever matched on MSVC. It matched^std::vector<.*>$against a demangled name, but MSVC has no demangler, sodemangle()returnstypeid().name()verbatim and that name readsclass std::vector<double,class std::allocator<double> >. The anchor could never match, so thevector<Any>carve-out never applied and bothPortTest.VectorAnyandPortTest.LoopNodeAcceptsVector_Issue969threw. Folded into thevector<Any>commit with a test pinning both toolchains' spellings.The last three are on
maintoo. 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:
NodeConfig::auto_remappedinserted mid-struct, shiftingmanifestand everything after by 8 bytesJsonExporter::from_json_array_converters_SimpleStringmove ctor gainednoexceptin 4.9.0, flippinglinb::anybetween heap and inline storageBreak #3 is why the layout-patch approach on
fix/20928-btcpp-abiwas 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
offsetofagainst a stock 4.9.0 checkout, same toolchain:other_attributesmanifestuidpathpre_conditionspost_conditionsWarning
git cherry-pickauto-mergestree_node.hwith no conflict and silently reinserts the member mid-struct. Resolving conflicts carefully is not enough to avoid reshipping the bug. A one-lineoffsetofassertion 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
validateModelNamerejects. / \ : < > & " ' * ? |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.e11edc21carves out three characters. Each one appears in a config that works today:' 'moveit_proandmoveit_pro_example_ws, every one the space, includingClose GripperandMove 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.2in 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 inscript_tokenizer.cppapplies to script source, which model names never enter. Port names are a separate namespace and still reject.throughIsAllowedPortName. This is ABI-safe, sincevalidateModelNamelives insrc/xml_parsing.cpprather than a header, and upstream already permits all three in instance names for the same readability reason.The carve-out cannot be a comparison
findForbiddenCharreturns only the first offender, so filtering on its result hides every later one.Pick & Placepassed on the space while its&went unseen, andRobot's Home/Lefton 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|betaandv1.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 ofmoveit_pro,moveit_pro_example_wsand 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,
Rootor instance-name findings.Packaging collision found
4.9.0 adds
tools/bt_nodes_model.cpp, installing/opt/ros/*/bin/bt4_nodes_model. Stockros-jazzy-behaviortree-cpp4.9.0 installs that exact path, so shipping it unrenamed is a harddpkg"trying to overwrite" failure. Per the coexistence design's own pitfall section apath-excludedoes not help, because dpkg's ownership check runs before the exclude filter. Renamed tobt4_picknik_nodes_model. This block also auto-merges silently, below the conflict region.The
LEXY_ENABLE_INSTALLguard is now obsolete, because 4.9.0 deleted3rdparty/lexyoutright and every remaining vendored tree is STATIC or INTERFACE with noinstall()rule.Testing
pixi run build && pixi run testunder cmake 4.4.3: 525/525xml_parsing.cpphunk fails 5 of the ReactiveSequence tests, and reverting only theisVectorregex fails the MSVC spellingpre-commit run --all-files: passeslibbehaviortree_cpp_picknik.so.4.9.0with versioned soname.so.4.9NodeConfigoffsets measured against a stock 4.9.0 worktreemoveit_pro_behavior_interface347 tests,moveit_pro_behavior3672,moveit_studio_agent431One thing is left alone.
gtest_subtree.cpptrips 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'ssetOutputstores vectors asvector<Any>, which upstream's newLoopNodedoes not recognise. A real fork-versus-upstream collision, not a merge artifact, fixed in its own commit.Decisions that want a reviewer
{"__type":"double","value":3.14}, stock emits bare3.14. Kept the fork'stoJson, since the Pro UI consumes it, and madefromJsonaccept both. The tagged path is gated on__typenaming a primitive rather than on avaluefield merely existing. Otherwise a registered custom type with its ownvaluemember, or the diagnostic blob the non-registered-type commit emits, imports silently as astd::string.1;2;3;4, stock givesjson:[1,2,3,4], and for an empty vector""againstjson:[]. Both round-trip. The fork form additionally covers types with atoStr<T>specialization but no JSON converter, such asvector<float>, which stock throws on. This is the easiest thing to drop if nothing downstream needs;.'5' .. 3returns an emptyAny. The widenedare_numbersguard steals theconcatbranch. 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
.so.4.7to.so.4.9is customer-visible. A customer shipping a prebuilt Behavior plugin.soin 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.input_portsandoutput_ports.bt_flatbuffer_helper.hiterates onlyinput_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_farmneeds agit_shabump withrevisionreset to 1, then publishes4.9.0-1noble. Staged in apt_build_farm#65.moveit_proDockerfilepin moves from4.7.2-3nobleto4.9.0-1noble, the.so.4.7strings move, and every Pro binary is rebuilt.package.xmladdslibsqlite3-dev,libzmq3-dev,tinyxml2andtinyxml2_vendor, which the build-farm chroot must satisfy.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