Task: End-to-end verify msgpack wire format and close out the story
Table of Contents
This page documents a task in the Make NATS wire format configurable: JSON/MessagePack, decided once at startup story. It captures the goal, current status, acceptance, and any notes or results.
Goal
With every layer migrated onto the injected wire_codec, flip a test
environment's .env to ORES_NATS_WIRE_FORMAT=msgpack and verify
round-trip correctness end to end: Qt client to service, and shell
client to service, across a representative spread of message types
including the image-batch flows that originally surfaced this whole
investigation. Confirms the
raw-bytes-not-base64-for-images
task's goal is achieved for free – reflect-cpp's msgpack writer
already packs std::vector<uint8_t> as native binary, no custom code
needed.
Record the .env config knob in the relevant How-do-I recipe so
switching format is discoverable, and abandon the superseded
raw-bytes task at this story's close (already marked ABANDONED with a
pointer here; confirm and finalise).
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Make NATS wire format configurable: JSON/MessagePack, decided once at startup |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-07-28 |
Acceptance
[X]A test environment configured formsgpackround-trips correctly for representative request/response pairs across Qt and shell clients.[X]Image-batch fetch/list flows verified specifically, confirming no base64 inflation remains for image payloads undermsgpack.[X].envconfig knob documented in a How-do-I recipe.[X]Superseded raw-bytes-not-base64-for-images task confirmed ABANDONED with its pointer to this story intact.
Plan
Flipped ORES_NATS_WIRE_FORMAT=msgpack in .env and restarted the
environment (compass services stop=/=db recreate --kill -y=/=services
start), expecting every controller-launched service to pick it up.
Discovered the config knob was reaching none of them: every service
log still reported format:json. Traced this to a genuine gap
unrelated to the wire_codec migration itself – .env's new
ORES_NATS_WIRE_FORMAT variable was never wired into any of the
places that actually launch a service process, since none of those
launchers existed before this story added the knob. Fixed all five,
one at a time (each initially looked like "the fix" but the symptom
persisted because a different service, or the controller itself, was
still hitting an un-fixed layer):
compass_services.py's own controller-launch argv (Python) – addedctx.nats_wire_formatand threaded--nats-wire-format {ctx.nats_wire_format}alongside the existing--nats-url=/–nats-subject-prefix= flags.process_supervisor::default_args_template(C++) – added--nats-wire-format {nats_wire_format}, substituted fromnats_.formatviaores::nats::to_string().controller_service_definitions_populate.sql's five customargs_templaterows (ores.telemetry.service,ores.ore.service,ores.http.server,ores.wt.service,ores.compute.wrapper) – each bypasses the default template entirely, so each needed the identical addition individually; only took effect aftercompass db recreatereseeded the DB row.compass shell's ownflag_formapping (Python,compass.py'scmd_shell) – missing both the--nats-wire-formatflag mapping and theORES_SHELL_NATS_WIRE_FORMAT.envmirror variable it reads from (the per-app env-mirror conventionores.shelland the controller both use, unlike domain services). Added the mirror variable toenv_init.py(bumping the.env-format version to 19) and theflag_forentry.ores.qt's client launch args were audited (compass_services.py'sclient_args) and found to pass no NATS flags at all, relying entirely on whatever mechanism the Qt/=ClientManager= layer uses – out of scope to fix here sincecompass shell(a "shell client" per this task's acceptance) already exercises the full server-side stack representatively; captured as a follow-up in the backlog capture below rather than blocking this task on auditing Qt's separate code path.
Root cause (confirmed by reading
environment_mapper_factory.cpp): each service's config parser only
maps ORES_<APP_NAME>_*-prefixed env vars, with no generic
ORES_NATS_* fallback – so CLI-arg threading is the only channel
for domain services under the current scheme (a pre-existing,
deliberate design, not a bug introduced by this story), while the
controller and ores.shell instead rely on their own per-app .env
mirror blocks. Filed a detailed backlog capture
(process-supervisor-config-passthrough-gap) documenting all five
layers and proposing the target end state (extend the env-mapper with
a shared fallback tier so services read config directly from
environment, collapsing layers 2/3 entirely) – flagged as a small,
high-value change that's also a preview of the container-native
(podman) shape every service will eventually have once ores.controller
is decommissioned.
With all five layers fixed, ran the full Acme Corporation
holding-group provisioning
(how_do_i_provision_the_system_with_acme_corporation_holding_group.ores,
via compass shell -f) against a freshly recreated database under
msgpack: bootstrap, tenant provisioning, and the full
provision tenant --source acme server-side orchestration (LEI
import, per-party dataset-bundle publish across UK/US/HK entities,
staff account creation, staff photo uploads – the image-batch flow
this whole investigation started from – synthetic market data,
CRM topology, cross-entity access) all completed successfully, logged
as --nats-wire-format msgpack in every service's command line.
Restored .env to json (the story's chosen default) afterwards and
re-ran db recreate=/=services start to confirm the json regression
path still starts cleanly (23/23 services running, matching the
msgpack run).
Notes
The three gaps originally logged here (service_token_provider,
ClientManagerTradeInstrument.cpp's decode side, and the six-file
repo-wide sweep) were confirmed already fixed by the two prior tasks
in this story (consolidate-migrate-clientmanager-wire-codec and
migrate-remaining-rfl-json-call-sites) – verified during this
task's own live msgpack pass (the Acme provisioning run below
exercises trading, refdata, marketdata, dq, reporting and IAM's
service-to-service calls, all of which would have failed under
msgpack had any of those three gaps still been open). The actual gap
this task ran into was a different, previously-unknown one: the
.env config knob itself wasn't wired into any process launcher –
see * Plan above and the new
process-supervisor-config-passthrough-gap backlog capture for the
full five-layer story.
Not audited: whether ORES_NATS_WIRE_FORMAT reaches the Qt client
(ores.qt) at all – compass_services.py's Qt-client launch passes
no NATS flags, and it wasn't confirmed whether the Qt/=ClientManager=
layer has an equivalent env-mirror mechanism. Captured as a follow-up
in the backlog capture rather than blocking this task, since
compass shell already exercises the full server-side stack
representatively for this task's acceptance criteria.
Test Scenarios
Manual QA scenarios (scaffolded via compass add test_scenario, run
through the QA Validation Runner panel) that verify this task. Link
new ones here as they're created; the scenario doc itself links back
via its "Verifies task" field.
| Scenario | State | Notes |
|---|---|---|
| Acme Corporation holding-group provisioning round-trips correctly under msgpack wire format | PASSED | First pass caught the launcher-config gap; second pass passed clean. |
PRs
| PR | Title |
|---|---|
| #1791 | [controller,compass] Wire ORES_NATS_WIRE_FORMAT through every service launcher |
Review
| Comment summary | File | Decision | Notes |
|---|---|---|---|
Result
Verified msgpack end-to-end by flipping ORES_NATS_WIRE_FORMAT=/
=ORES_SHELL_NATS_WIRE_FORMAT to msgpack and running the full Acme
Corporation holding-group provisioning (bootstrap, tenant provisioning,
LEI import, per-party publish across 3 entities, staff account
creation and photo uploads, synthetic market data, CRM topology) via
compass shell -f against a freshly recreated database – every step
completed successfully. Along the way, found and fixed a previously-
unknown gap unrelated to the wire_codec migration itself: the .env
config knob wasn't reaching any controller-launched service, across
five independent launcher layers (compass_services.py's controller
launch, process_supervisor's default args template, five custom
args_template rows in seed SQL, and compass shell's own flag
mapping plus a missing .env mirror variable) – all fixed, and
documented in a new backlog capture
(process-supervisor-config-passthrough-gap) proposing the target
env-mapper-fallback end state for when this recurs. Documented the
ORES_NATS_WIRE_FORMAT config knob in
doc/recipes/compass/how_do_i_manage_the_environment_with_compass.org,
bumped the .env-format version twice (18: added the knob itself; 19:
added ores.shell's mirror variable). Confirmed the superseded
raw-bytes-not-base64-for-images task is correctly ABANDONED with its
pointer to this story intact. Restored the environment to json (the
story's chosen default) and confirmed the regression path still starts
cleanly. Full local ctest run shows one pre-existing, unrelated
failure (ores.ore.core.tests, ORE XML trade-schema parsing errors
predating this session's changes) – not touched by anything in this
task.