Add per-command metadata propagation to interfaces - #3777
Conversation
06f7730 to
c30dd3f
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Binary Python metadata can fail after transmission, and caller metadata can forge command-validation result fields.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds per-command metadata propagation from Ruby/Python command APIs through queues and interfaces.
Changes:
- Adds optional
extrametadata to command APIs and scripting wrappers. - Serializes metadata across topics, queues, and retries while protecting audit fields.
- Adds tests and scripting documentation.
File summaries
| File | Description |
|---|---|
openc3/spec/script/commands_spec.rb |
Tests Ruby wrapper propagation. |
openc3/spec/models/queue_model_spec.rb |
Tests queued metadata serialization. |
openc3/spec/microservices/queue_microservice_spec.rb |
Tests queued metadata processing. |
openc3/spec/microservices/interface_microservice_spec.rb |
Tests interface metadata merging. |
openc3/spec/api/cmd_api_spec.rb |
Tests Ruby API validation. |
openc3/python/test/script/test_commands.py |
Tests Python wrapper propagation. |
openc3/python/test/models/test_queue_model.py |
Tests binary queue metadata. |
openc3/python/test/microservices/test_interface_microservice.py |
Tests Python interface merging. |
openc3/python/test/api/test_cmd_api.py |
Tests Python API validation. |
openc3/python/openc3/topics/command_topic.py |
Serializes topic metadata. |
openc3/python/openc3/script/commands.py |
Forwards metadata from scripts. |
openc3/python/openc3/models/queue_model.py |
Stores queued metadata. |
openc3/python/openc3/microservices/interface_microservice.py |
Applies metadata before writes. |
openc3/python/openc3/api/cmd_api.py |
Adds Python extra support. |
openc3/lib/openc3/topics/command_topic.rb |
Serializes Ruby topic metadata. |
openc3/lib/openc3/script/commands.rb |
Forwards Ruby script metadata. |
openc3/lib/openc3/models/queue_model.rb |
Persists queue metadata. |
openc3/lib/openc3/microservices/queue_microservice.rb |
Restores queued metadata. |
openc3/lib/openc3/microservices/interface_microservice.rb |
Applies Ruby interface metadata. |
openc3/lib/openc3/api/cmd_api.rb |
Adds Ruby extra support. |
docs.openc3.com/docs/guides/scripting-api.md |
Documents metadata usage. |
Review details
- Files reviewed: 21/21 changed files
- Comments generated: 4
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
c30dd3f to
55faa65
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3777 +/- ##
==========================================
+ Coverage 79.30% 79.51% +0.20%
==========================================
Files 896 899 +3
Lines 67371 67827 +456
Branches 2608 2667 +59
==========================================
+ Hits 53426 53930 +504
+ Misses 13276 13228 -48
Partials 669 669
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Caller extra was merged over command.extra, so it overwrote the keys the packet's accessor had just written during build_cmd. For an HTTP target that meant HTTP_PATH, HTTP_METHOD, HTTP_HEADERS and HTTP_QUERIES became caller controlled, letting anyone with cmd permission on the target redirect the request the interface makes on their behalf. Merge the other way so packet derived values win. Keys the accessor did not set still come through, so the feature is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
QueueModel gained an extra keyword but the controller never passed one, so queueing or editing a command through the API dropped the metadata attached by cmd(extra: ...). Pass extra from params on insert_command and update_command, rejecting a non-Hash with 400. update_command carries the existing entry's extra forward when the request omits it, since the queue edit APIs have no way to express it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
cmd(extra: ...) puts caller controlled values into packet.extra, which exposed two problems on the write path. JSON.generate returns UTF-8. Appending that to the binary log entry or preidentified frame raises Encoding::CompatibilityError once the buffer holds a byte >= 0x80, which the packet time nearly always does, so the packet was silently dropped from the log. Force the JSON to binary. The extra length field also counted characters instead of bytes, which would declare a short length and desync every entry after it. On the Python side PreidentifiedProtocol used plain json.dumps, raising "Object of type bytes is not JSON serializable" for the binary extra values COSMOS supports elsewhere. Use JsonEncoder like the rest of the codebase. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ruby's Float#as_json writes non-finite floats as
{"json_class": "Float", "raw": "NaN"|"Infinity"|"-Infinity"} since bare
NaN/Infinity literals are not valid JSON. JsonDecoder only understood
the String form, so those values reached Python interfaces as dicts
rather than floats. Commands released by the Ruby queue microservice
carry their extra in exactly this encoding.
Decode both forms. The encoder is unchanged: Python writes bare literals
that Ruby already parses with allow_nan, and switching it would break
readers using plain json.loads.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
jmthomas
left a comment
There was a problem hiding this comment.
I made some additional improvements to the PR. Approved.



Replaces #3666, whose original head fork was deleted.
Problem
Command callers sometimes need to attach correlation and workflow metadata to a single command and make that metadata available to the interface that writes the packet. Previously, the
cmdAPI did not provide a supported way to carry caller metadata through command topics, queues, and command retries.Changes
extraHash/dict to the Ruby and PythoncmdAPI families and scripting wrappers.extraon the command topic and merge it intocommand.extrabefore the interface writes the packet.cmd_string,username,interface_name,queue_username, orapprover.extrausage for Ruby and Python scripts.Commands.build_cmdcontinues to clear template packet metadata. Caller metadata is applied only to the newly built command insideInterfaceMicroservice, preventing stale metadata from leaking between commands.Compatibility
extrais optional.Testing
flow_uuidandhv_idmetadata;queue_usernameandapprovervalues were removed.git diff --check, Ruby syntax checks, Python compilation, and the repository security/code-analysis checks passed.