BE-3482 pymando Branch Review
Read-only review (2026-07-13) of feature/BE-3482-pymando, 13 commits off develop@3024e20c in worktree .worktrees/BE-3482-pymando, judged against the mando AGENTS.md conventions guide. Verdict: NEEDS REWORK BEFORE MR. The conformance work itself is solid and the quality gates are genuinely green, but three blockers are things reviewers in this repo reject on sight, and all three are cheap to fix (about 30 minutes). Update, same day: the rework was EXECUTED as a from-scratch rebuild on feature/BE-3482-pymando-v2; see the “Rework executed” section below.
For Agents
FINAL OUTCOME 2026-07-22: v2 was pushed, opened as MR !570 and MERGED into develop (squash
fde2425e“feat: pymando logging conformance”, merge commit14dfb3ad). The conformant wheelpy-mando==1.16.1+dev.2691658359.14dfb3adis published to Nexus dev and consumed by BE-3613. This note is now historical record of the review + rework. No mutations were made during the review (no commits, amends, rebase, push, or file edits; only build/test/check commands in the target worktree). UPDATE 2026-07-13: the rework was executed the same day. The fingerprint branch went first as planned and is now open as MR !569 (pushed to origin, awaiting review; see BE-3482 Datadog Logs and APM Conformance). This branch was rebuilt from scratch asfeature/BE-3482-pymando-v2(worktree.worktrees/BE-3482-pymando-v2, 15 title-only commits, all gates green incl. pytest 120 passed / 1 skipped, which satisfies the re-run requirement below). v2 is NOT pushed and has no MR; the old v1 branch and worktree are untouched as fallback. Full outcome in the “Rework executed” section.
What the branch contains
Two workstreams bundled into one branch, net +470/-196 across 13 files (2 new: mando-lib/src/tracing/fmt_util.rs, py-mando/test/test_dd_conformance.py).
On-ticket (9 commits): py-mando DataDog log/APM conformance. ECS team-label extraction; logger.init(service, team) building an identity dict and exporting DD_SERVICE/DD_ENV/DD_VERSION; TraceFilter emitting flat flow.step.path and unified ddtags; span-tag rename flow.context to flow.step.path; conformant logging.conf (logger.* fields, status, drops the phantom %(status)s); a log_error helper emitting error.{type,kind,code,message,stack,fingerprint} with root-cause unwinding; the Rust JsonFormatter renamed to status/logger.file/logger.line; a set_log_tags pyfunction bridging ddtags into the Rust stream via LOG_DDTAGS: OnceLock<String>; a 177-line conformance test file (8 tests).
Off-ticket (4 commits): 86d7ffd8 version-fallback panic fix (byte-identical duplicate of bc3727ba on the fingerprint branch, same 4 files, same +39/-8), 34e749d5 inline-format tweak on the exact line 86d7ffd8 rewrote, 9b0c4e70 logger-init split with focused tests, d0986230 extraction of collect_span_fields/MapVisitor/WriteAdaptor/level_to_status into a new ungated mando_lib::tracing::fmt_util.
Gate results (genuinely green)
rtk proxy cargo clippy --release --all-features: exit 0, ZERO findings on branch-touched files (all remaining warnings are pre-existing, in untouched crates).rtk proxy cargo test --all-features --release -- --test-threads=1: 366 passed / 0 failed / 35 ignored. Nonzero totals confirm the RTK false green did not occur.- Feature-flag correctness proven by BUILD, not assumed:
cargo check -p mando_lib --no-default-featurespasses AND--no-default-features --features pythonpasses, sofmt_utildoes NOT accidentally require theappfeature (tracing-subscriberis an unconditional dep). - All 4 new Rust tests confirmed executing in the gate run.
- Python pytest suite NOT re-run this session; last verified 2026-07-09 (114 passed / 1 skipped). Since
logging.conf,logger.py, andtracing.pyall changed, this MUST be re-run before MR.
Blockers (3)
- Scoped commit titles, 13/13. Every title carries
(mando)or(py-mando);AGENTS.md2.7 bans scopes outright (reviewer-mandated in MR !322). All 13 need retitling. - 7 orphaned ContextVars in
py-mando/python/py_mando/tracing.py(declarations at lines 13-19, dead.set()/.reset()at 39-45 and 52-58). Commitd528b71fdeleted their only reader (thetagsblock inTraceFilter.filter); grep confirms zero readers repo-wide. Knock-on:trace()’smetadataparam now feeds nothing but dead vars, andrun_with_tracestill passes_logger.metadatainto it. This is the exact theme-2 rejection pattern (“remove the unused code”, MR !345). - Off-ticket duplicated commits.
86d7ffd8+34e749d5are unrelated to the ticket, AND86d7ffd8is a byte-identical duplicate ofbc3727baon the fingerprint branch. Whichever MR lands second gets conflicts in all 4 files on identical content.
Notable should-fixes
error.fingerprintserializescode|Nonewhen no step context is set (tracing.py:109,f"{code}|{context_var.get()}"). Section 6.3 defines the fingerprint as error code plus step path; a missing path should be omitted or empty, not stringified as"None". Pollutes DD fingerprint aggregation for everylog_errorcall made outside atrace()/run_with_tracescope. No test covers that path.- Dead
tracing-serdedep inpy-mando/Cargo.toml:78;fe78dc10removed the last use. (py-mando-simulation still uses it legitimately; leave the workspace entry alone.) - fmt_util extraction only 2/3 done.
py-mando-simulation/src/log_formatter.rsstill duplicatescollect_span_fields/MapVisitor/WriteAdaptorverbatim, and itsCargo.tomlalready depends onmando_libwithfeatures = ["python"], somando_lib::tracing::fmt_utilis directly reachable today. Theme 5 (finish the pass before requesting review) plus 13.4 (duplicate logic is debt). - 2 branch-introduced rustfmt hunks:
fmt_util.rs:47(NEW file, theme 7: rustfmt every new file) anddd_formatter.rs:373(new test line exceedingstruct_lit_width). Hand-fix those two lines only; a full rustfmt ofdd_formatter.rswould also rewrite its 6 pre-existing hunks (unrelated churn, theme 1). super::version_or(...)called by full path 3 times in thedd_formatter.rstest module (lines 370, 372, 374) despiteuse super::*;at line 164. The single most repeated review nit in this repo.- Test hygiene: hand-rolled
_Capturehandler where pytest’s built-incaplogfixture does the job; the 3log_errortests duplicate the same 4-line setup verbatim and leak handlers onto a module-level logger;test_init_populates_identity_and_dd_envnever restores_initialized, the root handlers, oridentity/metadata, even though commit491a293b’s title claims “restore mutated globals” (the restore is not present in the final file). - Minor: two
use crate::log_formatter::...lines to merge (py-mando/src/lib.rs:65-66); inert# noqa: F401comment (logger.py:73, py-mando has no linter configured); cross-module use of the private_logger._build_ddtags()sets up a fragile circular import.
Rebase risk and landing order
- develop tip
a01892f9is 14 commits ahead of the branch base (79 files changed on develop); the intersection with branch-touched files is exactly ONE file:mando-lib/src/app/dd_formatter.rs. - The conflict is semantic and guaranteed: develop’s
ae6d1098(the BE-3482 core merge) rewrotedd_formatter.rsinline with the same plumbingd0986230extracts (collect_span_fields,MapVisitor,WriteAdaptor, its own level-to-status match,logger.file/logger.line). The branch deletes hunks develop rewrote and relocated; git cannot usefully auto-merge. Must be re-done by hand if kept.
Recommended landing order:
- Fingerprint MR first (
feature/BE-3482-fingerprint-activity): already based on the develop tip, correctly titled, and it unblocks develop’s live mando-bess panic. - Then rebase this branch onto develop, dropping
86d7ffd8(already on develop viabc3727baby then; verify the rebase auto-drops it as an identical patch) and34e749d5(empty or one-char churn once86d7ffd8is gone). - Sharpest option (recommended): drop
d0986230too. It is the entire conflict surface, off-ticket, and only 2/3 finished. Result: a zero-conflict 9-commit on-ticket MR. Refile the fmt_util extraction as its own ticket that unifies all THREE formatters (dd_formatter + py-mando + py-mando-simulation) against develop’s conformantdd_formatter. Cost:fe78dc10currently takeslevel_to_statusfrom fmt_util, so py-mando temporarily needs its own local level-to-status match (about 7 lines), which the follow-up ticket then de-dupes. Alternative: keepd0986230, but FINISH it (fold in py-mando-simulation) and accept the manual rebase rework.
No refinery migrations on the branch, so the migration-superset check is N/A.
Shortest path to MR-ready (9 items)
- Strip scopes from all 13 commit titles (BLOCKER, ~10 min).
- Delete the 7 orphaned ContextVars and their set/reset in
trace(); collapse the now-unusedmetadataparam and itsrun_with_tracecall site (BLOCKER, ~10 min). - Drop
86d7ffd8+34e749d5; the version fix lands via the fingerprint MR (BLOCKER, ~10 min). - Remove the dead
tracing-serdedep (~2 min). - Fix the
code|Nonefingerprint and add the missing no-step-context test (~10 min). - Hand-fix the 2 rustfmt hunks; drop
super::from the 3version_orcalls; merge the twouse crate::log_formatter::...lines; delete the# noqa(~10 min). - Decide on
d0986230: drop it (recommended, zero-conflict MR) or finish it and hand-rework on rebase. - Re-run
maturin develop --release+poetry run pytest --nbval(GATE, ~15 min). - Swap
_Captureforcaplog; parametrize the 3 duplicatedlog_errortests; restore mutated globals (~20 min).
Blockers (items 1-3) are roughly 30 minutes total.
New tooling landmine: rustfmt —skip-children false green
rustfmt --check --skip-children prints nothing on stable
--skip-childrenis UNRECOGNIZED on the pinned stable rustfmt (1.89.0 toolchain):rustfmt --edition 2021 --check --skip-children <file>errors out withUnrecognized option: 'skip-children'and emits NOTHING on stdout, which reads as “0 fmt diffs, all clean”. An earlier gate attempt was false-green this way before detection.
- Check WITHOUT the flag:
rustfmt --edition 2021 --check <file>. - rustfmt FOLLOWS
moddeclarations into child files, so attribute reported hunks by the filename in eachDiff in <path>:<line>:header, not by the file you passed. - To separate pre-existing churn from branch-introduced churn, run the same check against the base revision’s copy of the file:
git show <base>:<path> > /tmp/x.rs. - Recorded in mando
AGENTS.mdsection 10 plus the landmines table, alongside the RTK and cargo-fmt landmines; Mando AGENTS Guide (mirror) resynced.
Rework executed (2026-07-13): feature/BE-3482-pymando-v2
The rework above was executed the same day, as a from-scratch rebuild rather than an in-place retitle. The old v1 branch and worktree were left untouched as fallback.
- Branch:
feature/BE-3482-pymando-v2in worktree.worktrees/BE-3482-pymando-v2. 15 title-only, no-scope commits (author preserved,andras.lederer@alpiq.com) offorigin/developtip51d2b516. develop advanced twice during the session (dff18d5f, then the BE-1595 Arrow Flight merge51d2b516, which touchespy-mando/src/lib.rs+py-mando/Cargo.toml); the rebuild targets the real tip. Zero cherry-pick conflicts. Diff vs develop: 10 files, +374/-55. - Commit selection: the 9 on-ticket commits replayed in original order, scopes stripped.
9b0c4e70KEPT (on-ticket logger-init split with focused tests; touches only py-mando files; independent of all three dropped commits). DROPPED:86d7ffd8+34e749d5(the version fix rides MR !569) andd0986230(fmt_util extraction, to be refiled as its own ticket). - Surprise:
fe78dc10needed NO level_to_status workaround. The predicted ~7-line temporary duplication never materialized: the helper (and its test) were already inline inpy-mando/src/log_formatter.rspre-extraction, sinced0986230is what moved them out. Droppingd0986230therefore cost zero extra code. - All blockers and should-fixes executed: 7 orphaned ContextVars deleted (declarations, all set/reset lines in
trace(), the deadmetadataparameter, and therun_with_tracecall site; zero readers remain); deadtracing-serdedep removed frompy-mando/Cargo.toml(py-mando-simulation + workspace entry untouched, genuinely used there); the fingerprint outside a trace scope now emits the bare code instead ofcode|None(new testtest_log_error_without_step_context_omits_pathasserts no"None");build_ddtagsmade public (tracing.py is a legitimate cross-module caller; no alias needed, no test referenced the private name); test hygiene done for real: pytest’scaplogreplaces the hand-rolled_Capture, anerror_logfixture collapses the three duplicated setups, alogger_statefixture genuinely restoreslg.metadata/lg.identity/lg._initialized/ root handlers / root level, andDD_ENV+DD_VERSIONare monkeypatch-tracked. Tests now use the realtr.trace(...)API instead of pokingcontext_vardirectly. - Deliberately NOT actioned (all flagged as judgement calls in the review): the
_push_ddtags_to_rustImportError swallow,set_ddtagsdiscarding itsResult(first-write-wins, has a test), andset_log_tagsleft without a doc comment per AGENTS.md 2.11 (no comments in new code). Thedd_formatter.rs/api_docs.rsstyle nits are N/A: those commits were dropped.
New field: flow.step.system
flow.step.connection does not exist in the py-mando surface at all. It is produced in exactly one place, the step info_span! in mando-lib/src/workflow/mod.rs, built from structured data: StepMetadata { flow: DataFlow, system: &'static str }. The new field is therefore ONE line at the production site, flow.step.system = metadata.system, taken from the structured source with no string parsing of the connection value.
- Verified against the generated flows: all 14
DataFlow::Wrapper(internal) steps declaresystem: "Mando", no exceptions; external steps declare the real counterparty (Volue EMSx19,Metisx14,Position Managerx8, …). - Semantics, all four required cases: Receive
Volue EMS(connectionVolue EMS => Mando) →Volue EMS; ReceiveMetis→Metis; SendVolue EMS(connectionMando => Volue EMS) →Volue EMS; Wrapper (connectionMando) →Mando. - Tests: new
step_span_system_is_the_non_mando_counterparty, a#[test_case]matrix over exactly those four rows asserting bothflow.step.connectionandflow.step.system; the pre-existingstep_span_carries_step_name_and_connectionextended to emit the new field. All 4 cases pass.
Scope tension (flagged, unresolved)
This puts one mando-lib commit (
52b4d270) on an otherwise py-mando-only branch. It is a single self-contained commit and trivially splittable onto its own branch if the MR should stay pure py-mando.
Gates (all green)
- clippy (
rtk proxy cargo clippy --release --all-features): exit 0, zero findings in branch-touched files (remaining warnings are pre-existing, in untouched crates). - cargo tests (
rtk proxy cargo test --all-features --release -- --test-threads=1): 434 passed / 0 failed / 35 ignored; nonzero totals confirm the RTK false green did not occur; all new tests confirmed executing in the run. - rustfmt: zero branch-introduced hunks, measured in place at base vs branch (the 3+1 pre-existing hunks in
py-mando/src/lib.rs/log_formatter.rsare byte-identical onorigin/develop; left alone per “hand-fix only your own lines”). - pytest (
poetry run pytest --nbval): 120 passed / 1 skipped (handover baseline was 114 / 1), including the new conformance tests and a runtime JSON conformance proof captured from the Rust stream (status,logger.file,logger.line,ddtagsall present in the emitted line). Cargo.lockcarries one extra hunk (mandarrow-client1.16.0 → 1.16.1) beside thetracing-serderemoval because develop’s committed lock is stale; see the landmine below. Kept rather than hand-editing the lock to a state cargo immediately overwrites.
Two more tooling landmines (recorded in AGENTS.md section 10 + landmines table; mirror resynced)
rustfmt base-file checks in a scratch dir are FALSE GREEN, and rustfmt writes into child files
Copying a base-revision file to a scratch dir and running
rustfmt --checkthere reports 0 hunks no matter what: rustfmt followsmoddeclarations, cannot resolve the children from the new location, and silently passes. Worse, runningrustfmt(non-check) on a module-declaring file writes into its resolved child files: during this rework it swept develop’s pre-existing flight-import churn into the diff and touched 6 unrelated child files (algo.rs,algo_runner.rs,model/cache_mode.rs,polars.rs,test.rs,validation.rs), all reverted. The only sound method: check out the base revision of the file AT ITS REAL PATH and run rustfmt there.
develop's committed Cargo.lock can be stale
mandarrow-clientisversion.workspace = true(= 1.16.1) but develop’s committed lock says 1.16.0, so any plaincargo buildon clean develop regenerates the lock. ACargo.lockhunk in a branch diff may therefore be legitimate, not churn.
Environment notes
~/.local/bin/python3.12is broken (its venvs crash inensurepip); use/opt/homebrew/bin/python3.12instead.- A transient SIGBUS hit
/Volumes/bandiduringmaturin develop --release(I/O fault on the external volume, not a code error); the retry succeeded.
State
Nothing pushed for v2, no MR. The old feature/BE-3482-pymando branch + .worktrees/BE-3482-pymando worktree are untouched as fallback; the main checkout is untouched (still on bugfix/BE-3333). feature/BE-3482-fingerprint-activity is now pushed and open as MR !569 (“feat: add activity component to error fingerprint”, 2 commits incl. the salvaged panic fix), awaiting review.
Final outcome (2026-07-22)
Both MRs merged: !569 (merge 53912a70, squash b3f39605, by 2026-07-15) and !570 = this rework (merge 14dfb3ad, squash fde2425e “feat: pymando logging conformance”). Wheel py-mando==1.16.1+dev.2691658359.14dfb3ad published to Nexus dev; the v1 fallback branch/worktree are archivable. Merge-train detail: Merge train completed (verified 2026-07-22). Consumer adoption: BE-3613 Algo Services py-mando Conformance.
Related
- BE-3482 Datadog Logs and APM Conformance - canonical BE-3482 status note (core merge, fingerprint branch, deferrals, sibling landmines)
- Mando MR Review Patterns - the nine mined review themes this review was judged against
- Mando AGENTS.md Master Guide - guide-level record of the conventions and landmines
- Mando AGENTS Guide (mirror) - full guide text including the skip-children landmine
- py-mando - crate overview
- Agent Context