Skip to content

Fix: Report a declared port whose name cannot be used - #35

Closed
dv-picknik wants to merge 1 commit into
mainfrom
fix/subtree-port-remap-silently-dropped
Closed

dv-picknik wants to merge 1 commit into
mainfrom
fix/subtree-port-remap-silently-dropped

Conversation

@dv-picknik

Copy link
Copy Markdown
Member

[written by AI]

A SubTree can declare a port that nothing is able to bind, and nothing reports it.

BT.CPP has two port-name rules that disagree. validatePortName in xml_parsing.cpp runs on <TreeNodesModel> port declarations and rejects only a leading digit. IsAllowedPortName in basic_types.cpp requires an alphabetic first character, and createNodeFromXML uses it to classify every instance attribute. The C++ InputPort() API uses it too, and says so plainly:

C++ InputPort("_myPort"): REJECTED (must start with an alphabetic character. Underscore is reserved.)
XML  <input_port name="_myPort">: ACCEPTED

So the declaration parses, and then _myPort="{outer}" on the instance fails IsAllowedPortName, drops through to other_attributes, and the remapping is gone. The SubTree reads its declared default instead of the parent's value, with no exception and no log. Measured against this branch's parent:

Parent sets outer='FROM_PARENT'. Declared default is DEFAULT_NOT_WIRED.
  myPort  inside SubTree -> FROM_PARENT
  _myPort inside SubTree -> DEFAULT_NOT_WIRED

What this does

Throws when an instance attribute names a port the node declares but cannot bind, naming the port and the rename. Declared ports live in two places, so the check reads both: manifest->ports for a registered node, subtree_models for a SubTree.

Why not reject the declaration

That is the one-line change and it is the wrong one. A port declared and never remapped is inert, and Objectives in the wild carry those. moveit_pro_example_ws has one: an editor wrote _collapsed into a <TreeNodesModel> port list in kinova_gen3_base_config/objectives/get_imarker_pose_from_mesh_visualization.xml. Refusing it at parse time would stop that Objective loading, to fix a bug it does not have.

The same workspace carries 148 _collapsed attributes on SubTree instances whose models do not declare it. This change fires only where a remapping is actually being lost, so all of those keep loading untouched.

Testing

pixi run build && pixi run test, 529/529. Four new cases in gtest_name_validation.cpp:

Case Expected
_myPort declared and remapped on a SubTree throws, message carries the port name and the rename
_foo declared and remapped on a registered node, via the PortsList overload throws
_collapsed on an instance whose model does not declare it loads
_collapsed declared in a model and not remapped loads

The registered-node case exists because CreatePort screens names through IsAllowedPortName, but the PortsList overload of registerNodeType takes the map as given, so a hand-built list can carry a key no attribute will bind.

Provenance

validatePortName's digit-only check is upstream, 8f86eb99 by Davide Faconti, present at tag 4.9.0. Not something the fork introduced. Worth raising upstream separately. This carries the fix for our 10.2 line in the meantime.

Refs PickNikRobotics/moveit_pro#17640

@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 949a117a-fdd9-46fd-a497-363d257c8b06

📥 Commits

Reviewing files that changed from the base of the PR and between 90577d5 and a5ad6c0.

📒 Files selected for processing (2)
  • src/xml_parsing.cpp
  • tests/gtest_name_validation.cpp

Included review availability: This review used your included allowance. 2 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 8 reviews per hour.


📝 Summary

Summary by CodeRabbit

  • Bug Fixes
    • XML loading now reports a clear error when an attribute attempts to remap a declared port with an unusable name. The error identifies the port, node type, and XML line, and suggests renaming the port.
    • Undeclared attributes and declared ports that are not remapped continue to load as before.

Walkthrough

The XML parser now checks disallowed, non-reserved attributes against declared node ports and SubTree ports. It throws a line-specific RuntimeError when such an attribute remaps a declared port. Tests cover rejected remappings and accepted attributes that do not remap a declared port.

Changes

XML port validation

Layer / File(s) Summary
Reject remapping of declared unusable ports
src/xml_parsing.cpp, tests/gtest_name_validation.cpp
The parser checks disallowed attributes against the node manifest or SubTree model and throws when an attribute remaps a declared port. Tests cover SubTree and registered-node rejection, plus accepted undeclared attributes and declared ports that are not remapped.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to a5ad6

No actionable issue remains identified; the change is mergeable after normal checks.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description is complete and relevant. It explains the bug, affected code paths, intended behavior, compatibility rationale, tests, and test results. It also addresses the requested unit-test cover…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Human Review Check ✅ Passed PASS. The PR changes only src/xml_parsing.cpp and tests/gtest_name_validation.cpp. It adds narrow XML port-validation behavior and tests. The diff contains no authentication, permissions, secrets,…

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

A SubTree model can declare a port that nothing is able to bind.
`validatePortName` only rejects a leading digit, so `<input_port
name="_myPort"/>` parses, while `IsAllowedPortName` requires an alphabetic
first character and therefore refuses the same name. The C++ `InputPort()`
API refuses it too, with "Underscore is reserved".

The cost is silent. `createNodeFromXML` classifies each instance attribute
with `IsAllowedPortName`, so `_myPort="{outer}"` falls through to
`other_attributes`, the remapping is dropped, and the SubTree reads the
declared default instead of the parent's value. No exception and no log:

  Parent sets outer='FROM_PARENT'. Declared default is DEFAULT_NOT_WIRED.
    myPort  inside SubTree -> FROM_PARENT
    _myPort inside SubTree -> DEFAULT_NOT_WIRED

Throw when an attribute names a port the node declares but cannot bind,
naming the port and suggesting the rename.

Rejecting the declaration itself would be the smaller change and the wrong
one. A port declared and never remapped is inert, and Objectives in the wild
carry those: an editor wrote `_collapsed` into a `<TreeNodesModel>` port list
in moveit_pro_example_ws. Refusing it at parse time would stop that Objective
loading to fix a bug it does not have. This fires only where the remapping is
actually being lost, so the 148 `_collapsed` instance attributes in that same
workspace keep loading untouched.

Refs PickNikRobotics/moveit_pro#17640
@dv-picknik
dv-picknik force-pushed the fix/subtree-port-remap-silently-dropped branch from 6a4e20e to a5ad6c0 Compare September 29, 2026 20:43
@dv-picknik

Copy link
Copy Markdown
Member Author

[written by AI]

Closing. The premise does not hold up.

A declared port BT.CPP cannot bind does lose its remapping in silence. What that argument rests on is 4.9.0 having taken something away, and it did not.

The deb we ship today, 4.7.2-noble4, comes from b61a3989. Its IsAllowedPortName is:

const char first_char = str.data()[0];
if(!std::isalpha(first_char))
{
  return false;
}

and it has the same else if(!IsReservedAttribute(port_name)) { other_attributes[...] } branch that drops the attribute. Diffing IsAllowedPortName and IsReservedAttribute between b61a3989 and stock tag 4.7.2 gives no difference at all, and that fork does not contain validatePortName, which is new in 4.9.0. So _my_port has never worked, in stock or fork, at 4.7.2 or 4.9.0, and the fork has never diverged on port names. The only port-name-adjacent thing we carry is the validateModelName carve-out for the space, the apostrophe and the dot, which is a different namespace.

That leaves the question of who declares a port named _foo at all. In practice, only the MoveIt Pro editor, by writing a UI attribute into a <TreeNodesModel> port list. moveit_pro_example_ws has exactly one, _collapsed in kinova_gen3_base_config/objectives/get_imarker_pose_from_mesh_visualization.xml. That is our bug, not the library's, and moveit_pro#23165 now refuses to save a port declaration whose name starts with an underscore while still letting that file open so the junk port can be deleted.

So this would add a permanent throw to a vendored fork, to be carried across every upstream rebase, for a case our own product can no longer produce and that nobody has reported. Not worth it.

The inconsistency itself is real and upstream. validatePortName rejects a leading digit where IsAllowedPortName requires a leading alpha, so a declaration is accepted that the C++ InputPort() API refuses with "Underscore is reserved." That is 8f86eb99 by Davide Faconti, present at tag 4.9.0. Worth raising with upstream rather than patching here.

apt_build_farm#69 closes with this, and #23165 goes back to the published 4.9.0-1noble.

@dv-picknik dv-picknik closed this Sep 29, 2026
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