For Agents
Living index of themes for the Alpiq BESS / Mando project. Each H2 is a topic; bullets are wikilinks to related notes. Updated by
obsidian-documenterwhen documenting work. Read byhistorianat bootstrap. Topics kept alphabetical.
Arrow Flight / Deployment
- be-1595-arrow-flight-dev-deploy-runbook-2026-06-24 — Dev deploy runbook (recurring facts). Three artifacts move together (mando image + py-mando wheel + optimization/forecast consumer images; same build on a wire change). Migration drift → mando crash-loop (
V…__… is missing from the filesystem), fix by merging develop so branch migrations ⊇ DB’s. IaC =optimization-universe-iac.worktrees/mando-arrowterraformterraform.auto.tfvars.json(mando/optimization-algo/forecast-algo image pins).terraform_apply:devalways red on a pre-existing customer-portal S3 403 HeadObject despiteApply complete!(harmless, not ours). Runtime switchMANDO_FETCH_STRATEGY=flight+MANDO_FLIGHT_HOST/PORTon algo task defs. Verify in Datadog EUenv:dev:bess-os-service-mando/bess-os-algo-optimization/bess-os-algo-forecast. - be-1595-flight-rest-graceful-degradation-2026-06-24 — F4 capability-gap fallback. Flight/DuckDB passthrough only streams within its cache window; a range predating the cache (Permanent storage) returns
RepoError::MethodNotSupported(repo_passthrough.rs:335). Was mapped to gRPCFailedPrecondition+ client only fell back onis_retryable()→ propagatedFatal, crashed forecast. Fix (commit7926278c): mapMethodNotSupported→ gRPCUnimplemented(mando-bess/src/flight.rs repo_error_to_status) +ClientError::is_unimplemented()(mandarrow-client/src/error.rs); client falls back onis_retryable() || is_unimplemented()(py-mando/src/polars.rs). KEEPCyclicDependencyasFailedPrecondition(real error). Net: optimization fully on Flight, forecast logsfalling back to RESTthen completes. Resolves the “Known limit” in be-1595-flight-execution-id-parity-2026-06-17. - be-1595-flight-wire-type-version-skew-2026-06-24 — #1 deploy hazard: wheel ↔ server skew. Wheel (mandarrow-client Flight wire types + bess client
send(update_id=...)) and server deploy independently; a develop merge bumped sharedDataPointUpdateInfo(fetch_time→update_time, addedupdate_id) → old-wheel↔new-server broke. Fix (commit73cbdcf3): tolerant wire types —#[serde(default)]on every optional/added field,#[serde(alias="old")]on renames, NEVER#[serde(deny_unknown_fields)]on boundary structs, + compat tests. Can’t retroactively fix released versions (deploy mando + wheel from the same build on wire changes); send-side signature mismatch only resolves by rebuilding consumers on the matching wheel. - be-1595-arrow-consumer-lock-script-2026-06-24 — Consumer wheel-lock script (
deploy-arrow-consumers.ps1/ base64iexone-liner). Windows on corp net, Nexus must resolve. Clone via token URL (https://oauth2:$Token@…) +GIT_TERMINAL_PROMPT=0/GCM_INTERACTIVE=Neverto stop the GitLab credential popup hanging. The base64 one-liner is a frozen snapshot → a stale clipboard silently re-locks the WRONG wheel; re-copy fresh + verify the echoed wheel. Dev images gated behind thepython-docker-publishcomponent (Publish Docker Devonly on develop/rc) → script appends a local rules-override forfeature/*. - be-1595-flight-execution-id-parity-2026-06-17 — Brings the Flight streaming path to parity with REST for execution-bound manual overrides; fixes the dev regression where
MANDO_FETCH_STRATEGY=flighthard-broke the optimization/forecast algo runners (they always fetch withexecution_id). Sharedmando_lib::app::execution_override::apply_execution_overrideshelper (DRY across RESTget_data+ Flightdo_get);execution_id: Option<Uuid>added to bothQueryTicket+FlightTicket(#[serde(default)]); overrides injected intoDataPointFilters at the HANDLER layer beforeretrieve_stream(no trait change);MandoFlightServicegained anArc<FlowRepository>; client guard relaxed to error only onaccess_token(headers + access_token-fallback deferred). Deploy needs mando image + wheel + consumer images. - be-1595-enabling-arrow-flight-consumers-2026-06-16 — How to turn ON the Flight client path in BESS consumers. Runtime switch: py-mando env
MANDO_FETCH_STRATEGY(defaultrest; Flight only when ==flight;flight.rs:16, read per-fetchpolars.rs:224-236) +MANDO_FLIGHT_HOST/PORT/PROTOCOL. Dev wheel ships-F flightso it’s a pure env flip, no rebuild. Gotcha: Flight-fetch error → SILENT REST fallback (polars.rs:234); confirm via logs/metrics. Per-service topology: algo-optimization + algo-forecast share the bess-os ECS task (bess_os_ecs.tf, localhost) → add 3 env vars (done onfeature/mando-arrow,d3db63e); trader-dashboard is a separate task (trader_dashboard_ecs.tf, overlocal.mando_domain) with no network path to the gRPC port → needs NLB/ALB-HTTP2 plumbing, left on REST. Server side:var.mando_flight_port(50051) exposed via port mapping + SG rule. - Arrow Flight Streaming Design — Flight streaming server/client design.
- Arrow Flight Gap Analysis 2026-04-30 — gap analysis.
- be-1595-publish-docker-dev-feature-branch-test-need-2026-06-16 — CI gotcha enabling dev-image builds on
feature/*for the Flight consumer repos.
BESS AM / Real-time Telemetry
- BESS AM (BE-2262) - mando-bess-am — BESS Asset Management (ticket BE-2262, in flight, authored by Gergely Vászon). Headless Kinesis stream processor (new crate
mando-bess-am) for per-second WAGO battery telemetry (real-time SoC/SoH) on the FI/Valkeakoski/Beskar asset. PipelineWAGO Box → AWS IoT Core → Kinesis (bess-am-events) → mando-bess-am → Kinesis (bess-os-events) → mando-bess: consumes raw per-second events, persists to Postgres schemabess_am, computes 1-min aggregates (Mean/Max/Min/Last/StdDev/Count/Sum) via windowing + grace-period closure, forwards tobess-os-events. Introduces a NEWEvent-typed data-point class distinct from the time-series flows (NOT one of Trading/Manual Schedule/Auction/Intraday/Data Update/AFRR). Kinesis adapter atmando-lib/src/adapter/kinesis/; Event modelmando-core/src/model/{event,wago}.rs; Event datapoints inbattery_online.yaml. Builds on BE-2132 (interval/Event groundwork), depends on BE-2341 (generic Kinesis producer, folded into the MR). Core MR !512 OPEN/requested_changes; nothing merged todevelopyet. - Dev Deploy 2026-07-14 (error telemetry) - the May feat deploy of the BE-2262 POC left
U202605131400__create_event_table+U202605141900__create_kinesis_checkpoint_tablein dev’srefinery_schema_history; the files exist only on the unmerged branches, so EVERY develop-based image crash-loops on dev at boot (MigrationError::PatchApplicationFailed). Worked around by restoring both files byte-exact (single canonical blobf45b6dd4across all source commits). OPEN ACTION: land the two files on develop (small MR, recommended) or clean dev’s history rows.
Bugs / Investigations
- fatal-step-double-logging-ancestor-boundary-2026-09-02 — Fatal steps emit the SAME error at the leaf boundary and again at every ancestor wrapper node (
run_steplogs unconditionally atmod.rs:499;status_or_error()returnsErronly for Fatal,?re-surfaces it at the wrapper). Fingerprints diverge by step path so copies don’t aggregate. Not the BE-3541 axis; still live on developb5770766. Trigger was a DuckDB duplicate PK already fixed by MR !610 (dab6cba6) but absent from prod7c1ef44f. - pymando-rust-log-trace-correlation-gap-2026-09-02 — Rust
tracingevents inside the Python algo containers carry nodd.trace_id/dd.span_id/logger.name/thread_name:py-mando/src/log_formatter.rsis a second, thinner formatter, anddd_formatter’sTraceInfo/lookup_trace_infoare private AND behind theappfeature py-mando never enables.TraceFiltercontextvars (tracing.py) also never reach Rust events. Reusingdd_formatter’s helper is RULED OUT (it needsOtelDataand py-mando installs no OTel layer); fix is B1 metadata parity + B2 bridging the ddtrace span context Python→Rust per-call. - be-1595-flow-repository-sqlite-bugs-2026-06-17 — Two latent
FlowRepositorySqlitebugs (local Sqlite dev/test path only, NOT Postgres prod; worth a ticket), found writing the test seed for the Flight parity work. (1)create_flow(flow_repository_sqlite.rs:111) uses lazystmt.query()for an INSERT + dropsRowswithout iterating → SQL may never run, row never persists (use.execute()/.insert()). (2)started_atschema defaultcurrent_timestampstores TEXT butget_executionreads column 2 asi64→ row created without explicit integerstarted_atfails to read back (InvalidColumnTypeTEXT vs i64). - calculated-virtual-dp-range-cutoff-bug-2026-05-18 — Calculated & Virtual DPs leak rows past
to(all 4 retrieve methods); root cause inservice_base.rs:94-159, mechanisms inconvert_resolution.rsupsampling +evaluation.rsFull-join/concat. Diagnosed, fix pending. - flow-step-log-message-dropped-2026-05-26 —
ErrorWithStepStatus::log(status, message)dropsmessageat parent log site;StepResult::log()(mod.rs:155-159) destructures with..,Displayis"status: {status}"only (mod.rs:443),status_or_errorcollapsesLogtoOk(mod.rs:231-239), top-level catch inmando-bess/.../flow.rs:289sees"status: Error"only.Error(anyhow)arm OK; only::Logbroken. Tests atmod.rs:548-605only assert log level + wrapper string. Affects all envs. Cross-check follow-up commits onfeature/BE-2272. - mando-cli-v0.4.0-compose-bugs-triage-2026-05-26 — Two confirmed compose-runtime bugs in v0.4.0:
context: .resolves torunconfig/not project root (build.yml:11,build_dev.yml:18); bare service slug passed for mocked runconfig activates malformed override stub viarender_override(templates.rs:298-325) —profiles: ["{run_tag}"]line attemplates.rs:317is a load-bearing invariant keeping mocked stubs dormant. Fixes identified, not yet committed. - mando-cli-v0.4.0-mando-bess-binary-missing-2026-06-25 — The NEXT failure after the above triage’s fixes (commit
2233959). On a fresh clone, default profilemando-mocked-algosabortsdocker compose buildatCOPY target/release/mando_bess→failed to compute cache key … not found. Design gap:mando/Dockerfileis a thin runtime image copying a PRE-built binary (no Rust stage),bess-service.yaml:23-32context_includesit (CI assumption), and the default profile mapsmando→buildrunconfig (runprofile.rs:225) while nothing inupcompiles it (--cargoisartifact-only,cli.rs:55). Workaround:mando up --mando=pull(pulls the registry image). Durable fix pending decision: flip default →pull(open risk: confirm defaultMANDO_IMAGE→ registry notmando:dev) + add anuppreflight guard for missing binary. Diagnosed, not implemented. Reported by gabi on WSL. - mando-cli-v0.4.0-piped-output-invisible-failures-2026-06-25 — Observability bug found while validating the above’s
--mando=pullworkaround. Whenupoutput is piped/redirected (2>&1 | tee log), failures are INVISIBLE: captured.mando/compose-up.logempty, only a baredocker compose up … failed (exit 1)shown. Two stacking root causes (both required): (A)docker composewrites progress+errors to/dev/tty, bypassing mando-cli’s pipe (compose.rsrun_streaming) → fix: top-level--progress plain(compose.rs~L231); (B)CliBuffer::add_log_line(opts.rs:57) usesindicatif::MultiProgress::println, a silent no-op on non-TTY, dropping all streamed output + the failure-tail replay (compose.rs:263-272) → fix:eprintln!fallback when!stderr().is_terminal(). Validated macOS: redirected run 35→138 lines, realpull access denied+ tail + log pointer now visible. Reusable: pipe-captureddocker composeneeds--progress plain/--ansi never;indicatifprintlnneedseprintln!fallback on non-TTY. Tester’s now-visible failure = registrypull access denied(needsdocker login, distinct from mando-cli’s GitLab-API auth). Both fixes built+validated, NOT committed; linux/amd64 build pending. - BESS AM (BE-2262) - mando-bess-am (Update 2026-06-24) — FIXED 2026-06-25.
mando-codegencasing bug:expand_variantsmis-casedpython_fieldfor multi-segment Named variants —Min1/Mean→market.da.price.min1.Meaninstead of all-lowercase…min1.mean(lowercased first path segment but not the value segment). Affected BESS AM 1-minute aggregate datapoint names (Min1/Mean, Min1/Max, Min1/StdDev…). Originated in the branch’s ownfeat: separate out bess-amcommit5a739baa, NOT the develop merge (mando-codegenbyte-identical before/after). Fix: lowercase the value segment atmando-codegen/src/util.rs:41→format!("{pf}.{name_lower}.{}", v.to_lowercase()); commitcc4720b2onfeature/BE-2262-bess-am-poc, pushed to MR !512. Verifiedcargo test -p mando_codegennow 51/51 (was 50/1). Residual (unrelated): two pre-existing rustfmt drifts remain in that same file (~L50, ~L390, author’s code), left untouched. (Same update also recordsorigin/developmerged into the !512 branch as80a17e78— compiles clean, 428 lib tests pass.) - Dev Deploy 2026-07-14 (error telemetry) - dev crash-loop at boot:
MigrationError::PatchApplicationFailed: migration V202605131400__create_event_table is missing from the filesystem. Root cause: dev’srefinery_schema_historycarries two migrations from the UNMERGED BE-2262 bess-am branches (May feat deploy), so refinery validation fails for any image built without those files; the INVERSE of the BE-1595 drift case, merging develop cannot fix it. Fix: byte-exact restore of both files (blobf45b6dd4, checksums pass) as6bc418cd. STANDING WARNING until develop carries the files or the rows are cleaned. - fr-region-missing-datadog-logs-2026-07-21 - FR region services silently absent from Datadog logs:
modules/bess_os/cloudwatch.tfcreates the-frlog groups but thedd-logs-forwardersubscription-filter modules were dropped at module extraction (they live only in top-levelcloudwatch.tf). Diagnosed with live AWS evidence, fix not applied. - mando-ci-lib-only-test-gate-2026-07-22 -
mando-bess/tests/flight_end_to_end.rsis compile-broken on develop itself (E0603:DataPointUpdateInfoimported viamando_lib::servicewhere it is only a privateusesince theb3ce27a8repository crate split), undetected because the CI test gate is--lib-only; fix is a pending one-liner (import frommando_core::model::datapoint), its own change on Andras’s yes. - mando-cli-full-audit-2026-07-22 — full ~18.7k-line mando-cli audit: 37 verified bugs (2 HIGH — unguarded
kill -TERM -{pid}mass-kill inhost_process.rs:108-133with nopid<=1guard; orphaned untracked host procs onrecordfailure inup.rs:262-270— 18 MEDIUM, 17 LOW). Themes: exit-0-on-failure (migrate/get/pull), no reqwest timeouts anywhere,wiremock.enable()silent no-op. Also a −280-line/−5-dep over-engineering cut list, 9 duplication clusters (compose-context prologue copy-pasted across 8 command files), and the simulator runtime spec unimplemented onmain. No fixes applied. - mando-e2e-rebase-2026-08-05 - rebasing mando
poc/e2e-testsonto develop after ~135 commits of drift surfaced an upstream rename (crate::service::MandoServiceConfig→mando_repository::model::DataPointRepositoryConfig) that a defaultcargo testCANNOT see, because theappmodule is feature-gated and gets silently skipped — the feature-flag twin of the--lib-only CI gate lesson. Also two live env-var races (MANDO_TEST_ENDPOINTSin the new CSV route tests, fixed;MANDO_DEBUG_MOCK_ERRORupstream indebug_error, reported). - pymando-logger-init-race-flaky-2026-08-10 -
test_dd_conformance.py::test_init_populates_identity_and_dd_envintermittently seesidentity["service"] == None. Root cause spans the Rust/Python boundary: constructingAlgoRunner(algo_runner.rs:~113)std::thread::spawns a permanent Axum server thread thattest_algo_runner.pyLEAKS; each thread re-runslogger.init()with no service arg, andlogger.pyinit()setsidentitythen_initializedseveral slow lines apart (gap includesimport ddtrace.auto), so a backgroundinit(service=None)overwritesidentitymid-window. Athreading.Lockmitigation shipped (MR !601,feature/BE-4067) but a residual microsecond window remains — the real fix (stop leaking the server thread / isolate the test) needs its own ticket. Tell: interleavedStarting Algo runner service, listening on 127.0.0.1:3003/3002log lines before the failing test.
CI/CD
- bess-os-ci-components — the org’s shared GitLab CI component library (
alpiq_cicd/sales-and-origination/flexible-assets/bess/poc/bess-os-ci-components, tagsv1.0.0..v1.4.0, semantic-release;templates/<name>/template.yml:mr-jira-check,python-setup,python-test,python-docker-publish,release-notes,simulator-pipeline). Consumer.gitlab-ci.yml= ~40 lines ofinclude: component:+spec: inputs:, pinned@v1.x.0. Two patterns worth stealing:python-setup’s dotenv broadcast (one job derivesPROJECT_VERSION/ECR/release metadata from branch +pyproject.toml, exports viaartifacts: reports: dotenv: build.env, downstream inherits throughneeds:) andpython-test’s service-less + prebuilt-wheel shape (noservices:,DB_DISABLED=true, poetry pulls a prebuiltpy-mandowheel from Nexus so there is ZERO Rust compilation — that is why the 54/68-case suites cost 7.4/13.8 min;pre_test_scripthook,--junitxml+ junitparser merge +artifacts: when: always). Branch rules: MR events, develop,rc/*, main,release/*. The Rust mando pipeline does NOT consume it. - BESS AM (BE-2262) - mando-bess-am — adds a dedicated CI job “Publish BESS-AM Docker Dev” building the
mando-bess-amimage fromcontainer.bess-am.Dockerfile(ECR tag prefixbess-am-<version>). Crate-split branchfeature/BE-2262-bess-am-poc-build-test(commit5a739baa) separates it into its own image. - be-1595-publish-docker-dev-feature-branch-test-need-2026-06-16 — GitLab gotcha: enabling
Publish Docker Devonfeature/*in the BESS Python consumer repos needsoptional: trueon the.Publish→Testdependency, or the pipeline won’t start. - be-1595-arrow-consumer-lock-script-2026-06-24 — consumer dev images gated behind the
python-docker-publishcomponent (Publish Docker Devonly on develop/rc); the wheel-lock script appends a local rules-override sofeature/*builds. Also the GitLab credential-popup hang fix (GIT_TERMINAL_PROMPT=0/GCM_INTERACTIVE=Never+ token-URL clone). - be-1595-arrow-flight-dev-deploy-runbook-2026-06-24 —
terraform_apply:devalways red on a pre-existing customer-portal S3 403 HeadObject despiteApply complete!(harmless); IaC image pins inoptimization-universe-iac.worktrees/mando-arrowterraform.auto.tfvars.json. - clippy-disallowed-macros-allow-placement-2026-06-22 — keeping clippy green across versions:
disallowed_macrosallow must be a module-root#![allow]at the macro’s definition (mando-core/src/error.rs), not at the call site (ineffective on clippy 1.95). Workspace pins1.88.0; verify clippy-clean under the pinned toolchain since the call-site/block form is version-fragile. - mando-cli-github-build-mirror — GitHub-Actions build pipeline, GitLab Package Registry publishing, parallel-release flow (init → matrix-publish → finalize).
- mando-cli-gitlab-release-flow-2026-08-05 — mando-cli’s FIRST-EVER GitLab pipelines, unblocked by clearing the project’s stale
ci_config_path(canonical diagnosis for “committed.gitlab-ci.yml, no pipeline created, no error shown”). Windows builds removed entirely. Single long-livedreleasebranch flow:releasemints the tag + GitLab release + moveslatest;mainonly builds/publishes versioned packages. First release blocked solely on MR !5 approval — a group-level approval rule the author cannot self-serve from the CLI. UPDATE 2026-08-05 evening: v0.4.0 SHIPPED (tagv0.4.0+2734905610, full pipeline green Version → musl builds → Publish+latest→ Release). Two reusable GitLab gotchas from the one failed Release run:dependencies:overridesneeds:for artifact download (both were declared, so the Version job’sreports: dotenvPACKAGE_VERSIONnever arrived andtag=was empty — an empty dotenv var with no error is the signature), and markdown backticks in release descriptions get shell-evaluated byrelease-cli’s busybox wrapper (keep release notes plain text). - mando-cli-wsl-linux-build — local macOS (Apple Silicon) cross-compile of a Linux x86_64 / WSL
mandobinary;--platform linux/amd64+poc/-parent-mount gotchas. The manual counterpart to the CI mirror. - Mando AGENTS.md Master Guide — the verified mando gate set: NO
cargo fmtgate in CI (never barecargo fmt— ~64 legacy files reformat; scope to touched files); lint gatecargo clippy --release --all-features; canonical testcargo test --all-features --release -- --test-threads=1(single-threaded MANDATORY — shared in-memory DB pools). Runtime image now ca-certificates/libc6/libssl3 only (samba dropped); build image base stillrust:1.88.0-bookworm+ samba (toolchain 1.89.0 viarust-toolchain.toml). Pipeline etiquette: title-only Conventional Commits,feature/BE-xxxxbranches, py-mando publish is manual,release/*flows back to main+develop. Round 2 (2026-07-13):rustfmt.toml’s import-grouping/comment options are nightly-only + silently ignored on stable 1.89 (tip tree NOT fmt-clean — import grouping maintained by hand); CI gained a “PyMando Simulation Linux Dev” job (pytest without--nbval) + a simulator docker publish child pipeline; no MR/issue templates, no CODEOWNERS. - Mando Deployment Ceremony - the two-repo deploy ceremony (AGENTS.md section 14): mando CI only publishes images (
Publish Service Docker Dev,only:develop + rc/, tag{version}-dev.{pipeline_id}.{short_sha}, APP_VERSION+becomes-); optimization-universe-iac pins versions with ALLterraform_applyjobswhen: manual. CORRECTED 2026-08-18: pins are NO LONGER interraform/terraform.auto.tfvars.jsonunder adev_testgroup - since IaC3f50c77(2026-07-14) that file is gone and each env has its ownterraform/environments/{dev,test,int,prod}.tfvarswith a flatcomponents.mando.version, selected byterraform plan -var-file=environments/${CI_ENVIRONMENT_NAME}.tfvars;local.environment_groupno longer exists anywhere interraform/, andecr_reposmoved into the per-env files too. dev and test are independently pinnable and have diverged in practice. Verified branch rules onorigin/develop@9600b34c: dev plandevelop|feature/*|bugfix/*, dev applydevelop, test plan+applydevelop|rc/*(rc/ DOES still drive test), intrelease/*, prodmain. Feature-branch deploy = temporaryonly:edit that MUST be reverted before the MR merges. Never deploy from mando alone. - Test Env Deployment State 2026-08-18 - worked example of reading the new per-env pin end to end, plus the apply-failure pattern: three
terraform_apply:testruns on 2026-08-11, the first two (pipelines 2750514125 @ 12:37 UTC, 2750720634 @ 13:24 UTC) failed and the third (2750845368 @ 14:12 UTC, IaC9600b34c) went green - the failures track the two intervening “application asset tag” fix commits (cb07b0b,9600b34c), not the mando bump. Recipe for “is MR X live on env Y”:git merge-base --is-ancestor <merge-sha> <deployed-sha>. - Dev Deploy 2026-07-14 (error telemetry) - worked example of the feature-branch deploy on BOTH repos: throwaway mando branch
feature/BE-3482-dev-deploy(develop tip51d2b516+ !569 cherry-picks +only:edit + the PyMando Win Devneeds/dependenciesentry commented out; that job is absent on feature branches and a dangling need kills pipeline creation) + IaC branchfeature/error-hdl(version bump + “ci: enable dev apply from error-hdl branch” adding the branch toterraform_apply:devonly:, which normally exists ONLY on IaC develop). All CI edits temporary; strip on merge. - mando-ci-lib-only-test-gate-2026-07-22 - the CI test gate (
.gitlab/scripts/test.sh) runscargo test --all-features --release --lib -- --test-threads=1: ONLY lib unit tests are gated;*/tests/integration targets are never compiled by the pipeline and rot silently (the separate “Integration Test Linux Dev” job covers only the mando-lib archiver Postgres suite). Local gates need--libfor CI parity.--libarrived with the archiver split commit7278f36a. Companion lesson 2026-07-23 (BE-3657 phase 3): per-crate task gates (-p mando_lib) likewise miss CONSUMER crates - the final whole-branch review caught 2 tuple-syntax construction sites inmando-simulatorthat no task gate ever compiled (a Critical); workspace-widecargo clippy --release --all-features(no-p) is the only gate that catches cross-crate fallout. - mando-e2e-ci-green-2026-08-05 — “E2E Data Suite Linux Dev” GREEN in real CI (mando pipeline
2735150017, 72s): cross-project download of the released mando-cli binary viaCI_JOB_TOKEN+ job-token allowlist,postgres:17+wiremock/wiremock:3xasservices:(shared netns → WireMock onlocalhost:8081),mando_bessas a background host process from the build artifact, refinery migrations on boot, junit uploaded. mando’s.branch_rules:devgainedpoc/*(one line). THREE REUSABLE CI LESSONS: deleting a branch mid-pipeline kills its not-yet-started jobs atgit fetch(missing ref); cargo-chef must cook the SAME feature sets the jobs build (container.linux.chef.build.Dockerfilecooked bare--releasewhilebuild.shuses--features flightandtest.sh--all-features, so every job recompiledarrow-flight/tonic— now cooks both, but the rebake trigger is develop-only so the win lands only after merge); and GitLabcache:cannot hold/init/chef/cook/targetbecause it is outsideCI_PROJECT_DIR(escalations: weekly scheduled rebake,sccache+S3). PyMando Win Dev at ~2656s is the real pipeline whale. - mando-cli-junit-per-assertion-2026-08-06 — junit granularity in the e2e job raised from one case per test set to one case per assertion, so the MR widget names the failing assertion. Carries the reusable GitLab reporting rule: testcase names must be stable across pass and fail because GitLab keys a test’s history on
(classname, name)— derive the name from the expectation, never from the result. Plus the false-green-by-omission guard for tests that never executed. - mando-cli-flow-coverage-2026-08-06 — the E2E job’s GitLab
coverage:value is flows exercised / flows the service declares, parsed from the runner’s own last line via'/flow coverage: \d+\/\d+ flows \((\d+\.\d+)%\)/'(mando96b5b89c); the regex needs a decimal digit, so summary format and regex are one cross-repo contract and a mismatch shows as no coverage, no error. An interimcargo-llvm-covjob (green at 40.36%) was fully reverted — do not reintroduce line coverage here. First live value: pipeline2736559170, 20.0% = 1/5 flows. Badge/badges/poc%2Fe2e-tests/coverage.svg?job=E2E+Data+Suite+Linux+Dev.
Clippy / Lints
- clippy-disallowed-macros-allow-placement-2026-06-22 — clippy
disallowed_macros(mando banstracing::error!/log::error!inclippy.tomlto force logging through themando_core::error!wrapper): a per-invocation/per-block/per-fn#[allow(clippy::disallowed_macros)]at the call site is silently ineffective on a newer clippy (1.95) and is flagged “unused, applied to a macro invocation”. The lint resolves at the lexical site where the banned tokens live (the macro definition), so the only robust fix is a module-root#![allow(clippy::disallowed_macros)]in the defining file (mando-core/src/error.rs) — one allow covers all cross-crate callers. Caveat: workspace pins1.88.0; the block form ondeveloppresumably passes CI on 1.88 but is fragile across clippy versions; module-root#![allow]can’t be worse on 1.88 and is confirmed on newer clippy. General rule: for lints that fire on macro expansions,#[allow]at the call site no-ops — put#![allow]at the macro’s definition.
Conventions / Agent Guides
- Mando AGENTS.md Master Guide — master conventions doc for AI agents at
/Volumes/bandi/coding/poc/mando/AGENTS.md(untracked, deliberately never committed; synthesized 2026-07-10 from 5 parallel researchers ondevelop). 12 sections + 8 recipes: crate map/dependency direction, 10 non-negotiables, Rust style,mando_core::error!system, mando-bess API + py-mando PyO3 conventions, testing/git/CI ceremonies, verified-landmines table. Headline corrections:mando-bess/build/generated/**is generated BUT tracked in git (edit YAML/template → build → commit both, never hand-edit); NOErrorCodederive / NOmando-lib-macroon develop (worktree experiments only; parentpoc/CLAUDE.mdstale); JWTtoken_layersignature validation DISABLED (audit metadata, not authz); flow versions immutable once released. Rebuilt 2026-07-13 (round 2) against the true develop tip92bfe1c8(Jul-9, v1.16.0) via 5 re-run researchers on a.worktrees/developworktree — the stale-merge-base caveat is obsolete; localdevelopfast-forwarded + now tracksorigin/develop. Round-2 facts:FlowStepService= construction trait (type Params: ParamMeta; type Response;+ asyncfrom_config(config, &StepProviders)),manifest.yamldeployment catalog (schedule_env/semaphore_group, semaphores capacity 1), 36type_entry!registrations, error redesign partially landed (ae6d1098), py-mando-simulation split (BREAKING), second async bridge (future_into_py), rustfmt opts nightly-only/ignored, zero tests in the new crates, no MR templates/CODEOWNERS. Maintenance rule: code wins, then update the file. Rebuild process gotcha:agentops:researchersubagents can’t SendMessage — collect reports via scratchpad files or transcript extraction (~/.claude/projects/<project>/<session>.jsonl, longest assistant text). - Mando MR Review Patterns - evidence base behind the guide’s “Recurring review feedback” section: all 21 of Andras’s mando MRs (!322 through !558; 17 merged / 2 closed / 2 open) mined 2026-07-13 via read-only glab API; 108 reviewer notes (krisztian.fekete1 87 / gabor.nagy6 13 / balazs.mracsko.alpiq 7). Nine themes with source MRs: zero unrelated diff changes; delete ALL unused code before review (most repeated complaint); failures are errors never warnings (
mando_core::error!+ enum variant, no custom message strings); placement (route helpers next to routes, util inutil); completeness before review (mirror sibling configs); green pipeline + no conflicts before assigning review; rustfmt every new file; Jira ticket in sync; idioms (errors imported frommando_core, nounwrap, bind service once). Plus config direction (new config:config::Configover Envconfig) and the dictated rules: commits title-only NO scopes/descriptions (reviewer-mandated !322), commit every ~3 tasks, never em dashes anywhere, always subagent-driven development. - mando-repos-history-rewrite-2026-08-05 - the standing attribution-free history policy applied across both repos (mando-cli
main/release/4 re-pointed tags, mandopoc/e2e-tests), and the verification discipline it forced: rtk-wrapped grep/log pipelines produced fabricated zeros, so an rtk-mediated negative is never evidence — re-prove absences withrtk proxy gitredirected to a file. Third member of the rtk hazard family alongside the--test-threads=1false-green and the piped-exit-code trap (all three now in Agent Context). - BE-3482 Datadog Logs and APM Conformance - source of the 2026-07-13 fmt correction:
cargo fmt -- <file>does NOT scope (field-verified, it touchedapi_docs.rswhich was never named); format single files withrustfmt --edition 2021 <file>, every form ofcargo fmtbanned. Propagated toAGENTS.mdsections 2.3, 10, 13.5 + the landmines table, Mando AGENTS Guide (mirror) resynced, fmt warnings in Mando AGENTS.md Master Guide / Agent Context amended. - BE-3482 Datadog Logs and APM Conformance - second 2026-07-13 tooling landmine: the RTK shell hook rewrites
cargo test --all-features --release -- --test-threads=1so the threads flag becomes a test-name filter (every test filtered out, exit 0 in 0.00s, signature0 passed, N filtered out; two gate runs false-green before detection). On RTK machines runrtk proxy cargo test --all-features --release -- --test-threads=1and confirm the summed passed totals are nonzero; exit 0 alone proves nothing. Adjacent pitfall:cargo test | grep | tail; echo $?reports tail’s exit, not cargo’s. Recorded inAGENTS.mdsection 10 + the landmines table; mirror resynced. - BE-3482 pymando Branch Review - third 2026-07-13 tooling landmine:
rustfmt --check --skip-childrenis UNRECOGNIZED on the pinned stable rustfmt and prints nothing (false green); check without the flag, attribute reported hunks by theDiff in <path>:headers (rustfmt followsmoddeclarations into child files), and baseline againstgit show <base>:<path>to separate pre-existing churn from branch-introduced churn. Recorded inAGENTS.mdsection 10 + the landmines table; Mando AGENTS Guide (mirror) resynced. - BE-3482 pymando Branch Review - fourth and fifth 2026-07-13 tooling landmines (from the rework session): (a) rustfmt base-file checks that COPY the file to a scratch dir are FALSE GREEN (rustfmt cannot resolve the
modchildren from the new location and reports 0 hunks), and running rustfmt on a module-declaring file WRITES INTO its child files (6 unrelated py-mando files were touched and reverted); the only sound method is checking out the base revision at its real path; (b) develop’s committedCargo.lockcan be STALE (mandarrow-client1.16.0 vs workspace 1.16.1); plaincargo buildregenerates it, so a lock hunk in a branch diff may be legitimate, not churn. Recorded inAGENTS.mdsection 10 + the landmines table; Mando AGENTS Guide (mirror) resynced.
Data Point Retrieval
- BESS AM (BE-2262) - mando-bess-am — introduces a NEW
Event-typed data-point class (real-time telemetry) alongside the existingTimeSeriesDouble/TimeSeriesDoubleMatrix/StaticData. Event datapoints (BATTERY_SOC_ONLINE/BATTERY_SOH_ONLINE) defined inmando-bess/config/parts/battery_online.yamlunderAsset/FI/Valkeakoski/Beskar/Battery/.../Online, keyed to Kinesis external IDs[bess-am-events, OnlineSOC/OnlineSOH]. Model atmando-core/src/model/event.rs; built on BE-2132 interval/Event groundwork. - calculated-virtual-dp-range-cutoff-bug-2026-05-18 —
[from, to)range not enforced for Virtual/Calculated DPs inhandle_data_point_types;EvaluationMetaData.rangeplumbed but unused by final output. - mando-data-insert-wire-format-2026-08-06 — the insert side of the contract, pinned:
POST /data/insert(mando-lib/src/app/route/save_data_route.rs) takes{"data_points": {"<dp id>": [<bytes>]}}where the bytes are an Arrow IPC STREAM encoded as a JSON array of decimal u8 (not base64 — polars 0.49.1’sdeserialize_map_bytesonly reaches bytes viavisit_sequnder serde_json); reader ispolars-arrowStreamReader,update_infooptional, 204 on success, Content-Type never enforced. Required schema:value_time/generation_time/fetch_timeTimestamp(Microsecond, None)naive-UTC,value/value_x/value_yFloat64,idUtf8for matrix/trade; aflagcolumn and any null cell are rejected;get_column_date_timesignores tz and normalises ns/us/ms. Fixture fingerprint: 936 IPC bytes → ~9KB JSON. - mando-cli-e2e-live-green-2026-08-05 — two breaking
DataPointIdchanges landed upstream (found live 2026-08-05): (1) itsDeserialize/Serializewere rewritten from a{id, id_fragments}struct to a bare JSON string, so any hand-mirrored query body sending the object shape now gets a 400 fromPOST /data/query/v2; (2)DataPointId::newnow rejects path fragments that are not purely ASCII alphanumeric — ids with an underscore inside a segment (bess/fi_north/soc_state, thein_/out_corpus naming) 400 on query. The second is UNCONFIRMED as intentional and needs raising with the mando team; mando-cli’sdocs/e2e-guide.mdstill documents underscore ids as valid.
Docker Compose / Runtime
-
mando-cli-simulator-runtime-2026-05-30 — adds second compose project
mando-simas a peer to devmando; two-stack ergonomics via sharedservice_stack::select_profileresolver (single source of truth forlogs/exec/volume); env merged viafill_build_args(NOT--env-file, which hard-fails on missing.env); Python healthcheck; Gurobi license as file mount, not multiline env. (§A env contract SUPERSEDED 2026-06-02 — see mando-cli-simulator-env-contract-2026-06-02) -
mando-cli-simulator-env-contract-2026-06-02 — realigned simulator env to real images: orchestrator
SIMULATOR_DATABASE_*(replacesPOSTGRES_*), per-runnerSIMULATOR_<NAME>_HOST(replacesSIMULATOR_RUNNERSCSV), repo URL/branch/commit_hash moved orchestrator → runners. Runners read unprefixedSIMULATOR_REPO_URL/BRANCH/COMMIT_HASH(assumption — verify when image lands). Healthcheck Python →curl; Gurobi mount →GUROBI_LICenv. Schemabess_simulation→simulator. -
mando-cli-v0.4.0-compose-bugs-triage-2026-05-26 — v0.4.0 baseline compose-runtime triage;
context: .relative-path bug; load-bearingprofiles: ["{run_tag}"]invariant inrender_override. -
mando-cli-v0.4.0-mando-bess-binary-missing-2026-06-25 — next compose-build failure once the above fixes (commit
2233959) landed: defaultmando-mocked-algosresolvesmandoto thebuildrunconfig (runprofile.rs:225), butmando/Dockerfileis a thin runtime image that COPYs a pre-builttarget/release/mando_bessand theupflow never compiles it →docker compose buildaborts on a fresh clone (failed to compute cache key: "/target/release/mando_bess": not found). Workaroundmando up --mando=pull; durable fix (flip default →pull+ preflight guard) pending decision. -
mando-cli-v0.4.0-piped-output-invisible-failures-2026-06-25 — the streaming compose runner (
compose.rsrun_streaming) loses ALL output under pipe/redirect:docker composewrites progress+errors to/dev/tty(bypassing the pipe) → add top-level--progress plain(compose.rs~L231); andCliBuffer::add_log_line(opts.rs:57) routes lines throughindicatif::MultiProgress::println, a non-TTY silent no-op → fall back toeprintln!on!stderr().is_terminal(). Together they restore the captured.mando/compose-up.log, the streamed lines, and the--- last 40 log lines ---failure tail (compose.rs:263-272) for piped/CI/teeruns. Both fixes required. -
mando-cli-full-audit-2026-07-22 — 2026-07-22 audit of the compose runtime and command layer: 2 HIGH bugs in host-process signalling (
host_process.rs), MEDIUM compose/volume/down flag-drop + name-mapping bugs (down.rs,volume.rs,exec.rs/logs.rs), a 9-cluster duplication list headed by the compose-context prologue duplicated across 8 command files (→compose::prepare), and the simulator runtime spec still unimplemented onmain. -
mando-cli-datadog-local-logs-2026-07-22 —
mando up --datadogrenders an optional infra template (src/runtime/templates/infra/datadog.yml→.infra/datadog.builtin.yaml) with a local Datadog agent (container_name local-dd-agent,name:^mando-.*include filter), included existence-driven bycompose::assemble(NOT inINFRA_BUILTIN_FILES) so a flag-offupcan delete it. Compose interpolates env on EVERY verb → the template MUST use${DD_API_KEY:-}not:?(a:?hard-failsdown/logswhen the var is absent while the file exists). Extractedbuild_args::atomic_write_with_dirunifying 3 write sites. UPDATE 2026-07-23:docs/datadog-guide.md(tipc2becf1) verified end to end, every claim holds: 4 fail-fast paths, mando-only tailing, EU shipping (103 logs), teardown at both levels (flag-offup--remove-orphans+down). UPDATE 2026-07-23 (ddtags override): the embeddedddtagsenv WINS when mando emits one - Datadog JSON parsing promotes the bodyddtags(MANDO_ENVIRONMENT=local→env:local, unindexed by the org → 202-accepted lines VANISH); the “no env in ddtags” claim holds only whileMANDO_ENVIRONMENTputs none there; setMANDO_ENVIRONMENT=dev-local-<user>for ANY local mando (containerized included); the per-developer env tag is the only wall from the shared org’s REALenv:prodtelemetry under the same service name. -
mando-local-host-run-recipe-2026-07-23 - running a locally built
mando_bessas a HOST process against the mando-cli infra stack (mando up --datadog -p infra: postgres + wiremock + local-dd-agent). Env from.cargo/config.toml.example+ overrides (file is STALE, see Env / Configuration); infra pg credsbess_transactional_db/postgres/postgres @ localhost:5432;MANDO_PORT=8081(wiremock owns 8080), adapter hosts at localhost:8080 for hermetic 404s. DANGER: mando’s refinery races the mando-cli migrations container in DuckDbPostgres mode (idle-in-transaction boot wedge,move_schema_objectsblocked; recovery =pg_terminate_backend+ drop public/data/bess_os cascade + single-writer relaunch; DuckDb mode skips pg migrations but the stale container set lacks the setting table). Host processes are invisible to the local dd agent (mando-*filter): ship to Datadog via direct intake. Teardown:mando down+pkill mando_bess. UPDATE 2026-07-23 (epilogue): the first direct-intake forward VANISHED - Datadog promotes the embeddedddtagsfrom the JSON line, so mando’senv:local(fromMANDO_ENVIRONMENT=local) overrode the envelope’senv:dev-local-levanderand the org indexes noenv:local; fix = rewrite embedded ddtags before forwarding; durable = setMANDO_ENVIRONMENT=dev-local-<user>in the run env.
DuckDB
- BE-1597 DuckDB UI Exposure Research — BE-1597: exposing the DuckDB UI of running mando-bess + remote client access to its in-memory DuckDB. Root cause both prior attempts failed: the UI JS bundle hardcodes
"localhost:4213" === window.location.host ? "duckdb_ui" : "web", so any non-localhost:4213origin (path prefix, custom hostname, ALB) silently degrades — killing Attempt A (socat 4214 + ALB TG +/duckdb*listener rule; died on the 8080/healthcheck) and Attempt B (reqwest reverse proxymando-bess/src/route/duckdb_proxy.rs+<base href>/JS string-patch). Fix = SSMAWS-StartPortForwardingSessionToRemoteHostforwarding the LOCAL port 4213 → browsehttp://localhost:4213(literallocalhost, not127.0.0.1); no ALB (alreadyinternal=true); bastion hop required since ECS Exec is interactive-only. UI ext: onlyui_local_port(4213)/ui_remote_url/ui_polling_interval(284ms) settings, NO bind-address (binds::1→ socat), no auth, proxies assets from ui.duckdb.org per session (internet required), not statically linked (verifiednm -Don vendored 1.4.2.so→ only Icu/Json). Remote client → in-mem DB: only via Quack (DuckDB 1.5.3+, beta until 2.0). Includes a full 1.4.2→1.5.4 upgrade assessment (low-risk, in-memory-only sidesteps storage churn;=1.4.2pinCargo.toml:60vs the new1.MAJOR_MINOR_PATCH.xcrate scheme, 1.5.4→1.10504.0; C API additive-only; date_trunc/lambda SQL breaks clear). - BE-1597 DuckDB UI — Design — the approved decision/spec built on the research above (status: design approved, NOT implemented; owner Geri Vászon; repos mando + optimization-universe-iac). Phased: Approach 1 dev-only NOW via SSM
AWS-StartPortForwardingSessionToRemoteHostforwarding the LOCAL port 4213 →http://localhost:4213(literallocalhost, not127.0.0.1; no ALB / target group / health check //duckdb*prefix / reverse proxy / bundle patching — the bastion hop is only needed because ECS Exec is interactive-only); Approach 2 = Quack for a remote client → in-memory DB, deferred to DuckDB 2.0 (Sept 2026) since Quack is Beta in 1.5.x. Container change: addsocat+ adocker-entrypoint.shbridgingTCP4:4214 → TCP6:[::1]:4213whenDUCK_DB_UI_SERVER=true(NO Rust change; env already read atlib.rs:181/:301). Never in int/prod (proprietary MotherDuck frontend, no auth). Blocking open Q: Fargate egress to duckdb.org UNVERIFIED.
Env / Configuration
- BESS AM (BE-2262) - mando-bess-am — BESS AM service config uses env prefix
BESS_AM_*andconfig/bess-am.yaml; reads thebess-am-eventsKinesis stream and writesbess-os-events; Postgres schemabess_am. Local-dev Kinesis viadoc/kinesis-local-setup.md+ MiniStack (ministackorg/ministack). - be-1595-enabling-arrow-flight-consumers-2026-06-16 — Flight client toggled by
MANDO_FETCH_STRATEGY(defaultrest, Flight only when ==flight, case-insensitive) +MANDO_FLIGHT_HOST/PORT/PROTOCOL. Pure runtime env flip on the-F flightdev wheel; co-located consumers reach mando overlocalhost, separate tasks can’t (no network path to the gRPC port). Silent REST fallback on error. - be-1595-arrow-flight-dev-deploy-runbook-2026-06-24 — deploy-side view of the same switch:
MANDO_FETCH_STRATEGY=flight+MANDO_FLIGHT_HOST/PORTset on the algo task defs; verify in Datadog EUenv:dev(mando/optimization/forecast) — expect forecastfalling back to REST, optimization fully on Flight. - mando-cli-simulator-env-contract-2026-06-02 — three reusable patterns: (1) arbitrary extras passthrough — filter workspace
.envkeys against a managed-set const (MANAGED_SIM_ENV_KEYS), auto-inject survivors asKEY: "${KEY}", sorted alphabetically, withtracing::info!per injection; (2) sim-postgres aliasing — when two services share a default credential, route the dependent service’s native env (POSTGRES_USER) through the orchestrator’s contract (SIMULATOR_DATABASE_USERNAME) so one.envoverride controls both; (3) derive UI summary strings from the actual invocation-args function — never hardcode the verb+flags in summary text or it goes stale (pre-fix bug: literal"up -d --build --remove-orphans"in success row whileup_compose_argshad dropped--buildfor sim). - BE-1597 DuckDB UI — Design — the DuckDB-UI dev gate is
DUCK_DB_UI_SERVER(already read in mando-bess atlib.rs:181/:301; NO Rust change), set in terraform asDUCK_DB_UI_SERVER = tostring(var.environment == "dev")on the mando container and mirrored by thedocker-entrypoint.shif [ "$DUCK_DB_UI_SERVER" = "true" ]socat guard. Usevar.environment, NOTlocal.environment_group— the group collapses int+prod and dev+test, so it’s the wrong knob for a dev-only gate. - Mando MR Review Patterns - config direction from review (two reviewers, MR !556): NEW config uses
config::Config, notEnvconfig; existing envconfig adapters stay as-is (no mass migration). - Mando Deployment Ceremony - per-env mando runtime config WITHOUT rebuild. CORRECTED 2026-08-18: this is NO LONGER in the
.environment-vars:{env}blocks of the IaC.gitlab-ci.yml- since IaC3f50c77those anchors carry ONLYASSUME_ROLE_ARN/ACCOUNT_ID/ACCOUNT_NAME/AWS_REGION/environment:name, andSIMULATION_MODE,MANDO_FLOW_SCHEDULE_*,EBS_SMB_HOST,MANDO_FLOW_ACTIVE_VERSIONgrep to NOTHING there. Runtime config now lives interraform/environments/<env>.tfvarsas lower-case terraform vars:simulation_mode,ebs_smb_host/ebs_smb_share,mando_flow_schedule_{trading,idc_trading,intraday,auction,data_update}; sizing and active version moved per region underbess_os_regions.{fi,fr}(mando_flow_active_version,ecs_resources.mando_cpu/mando_memory) - dev has fi+fr, test has fi only. Constraint unchanged:mando_flow_active_version(dev fiV1_4, dev frV1_4_SIMULATION) must name a setup version existing in mandoconfig/flows/manifest.yaml; bump the two together. dev and test both runsimulation_mode = "true". - fr-region-missing-datadog-logs-2026-07-21 - mando reads the
DD_SERVICEenv since commitab8cfd11onfeature/BE-3657(resolve_dd_service()+ OnceLock-cacheddd_service()inmando-lib/src/app/mod.rs, hardcodedbess-os-service-mandoas fallback also on empty string, all 6 call sites routed incl. 4 rawglobal::meterliterals); deployment-neutral on the main stack because IaCbess_os_ecs.tf:218already sets the var on the mando container, and it unblocks the FR-frsuffix. - mando-cli-json-output-agent-skill-2026-07-23 -
MANDO_OUTPUT=json(exact-match"json"only; the--jsonflag beats it) is the env knob that flips mando-cli output to terse NDJSON on stdout. Any other value / unset = human mode (byte-identical to pre-feature). - mando-local-host-run-recipe-2026-07-23 -
.cargo/config.toml.exampleis STALE (field-verified 2026-07-23):MANDO_FLOW_ACTIVE_VERSIONis DEAD, the config now wantsMANDO_SETUP_ACTIVE_VERSION(V1_4) +MANDO_SETUP_SCHEDULER_DISABLED(prefixMANDO_SETUP, config-crate style);FINGRID_API_KEYmust be NON-EMPTY (init panics via unwrap: “must contain at least one key”); the postgres cert loader reads<cwd>/certs/eu-central-1-bundle.pem, so running from a workspace root needs acertssymlink tomando-bess/certs. - mando-e2e-rebase-2026-08-05 -
.cargo/config.tomlis gitignored local config (it holds secrets), so a fresh mando checkout has none andpy_mando/py_mando_simulationfail to link on Apple Silicon with unresolved_Py_*symbols. Fix:cp .cargo/config.toml.example .cargo/config.toml— the example carries the[target.aarch64-apple-darwin]rustflags-undefined dynamic_lookupthat PyO3cdylibtargets need. Nothing bootstraps this for you. - mando-e2e-ci-green-2026-08-05 -
/Volumes/bandi/coding/poc/compose.override.ymlis the canonical known-good env set formando_bess— diff against it rather than deriving requirements from code (deriving from code turned CI boot into one 15-minute pipeline per missing var). Two boot-order warts to raise upstream:DataPlatformConfig::init_from_env().unwrap()runs BEFORE its ownDATA_PLATFORM_DISABLEDflag is read, soFINGRID_DATABASE/OUTPUT_LOCATIONmust exist even when disabled; andFINGRID_API_KEYhard-panics atfingrid.rs:29. Final missing set for a CI boot:FINGRID_API_KEY,AFRR_AUCTION_RESULT_DEADLINE,FCR_AUCTION_RESULT_DEADLINE,BATTERY_STATIC_DATA_MDR_PATH. - mando-cli-e2e-live-green-2026-08-05 - the
MANDO_FLOW_*→MANDO_SETUP_*migration reaching the e2e suite, found only by a live run:MANDO_SETUP_ACTIVE_VERSION=V1_4is now REQUIRED (the service crashloops at boot without it), andMANDO_FLOW_SCHEDULER_DISABLEDwas renamedMANDO_SETUP_SCHEDULER_DISABLED— nothing rejects the obsolete name, the scheduler just stays on, and the0 5 * * * *data-update cron then fires mid-suite and breaksunmatched_max: 0at a nondeterministic point. General rule: a renamed env var that is merely ignored fails as an unrelated-looking downstream symptom.
Error Handling
- fatal-step-double-logging-ancestor-boundary-2026-09-02 — Fatal steps emit the SAME error at the leaf boundary and again at every ancestor wrapper node (
run_steplogs unconditionally atmod.rs:499;status_or_error()returnsErronly for Fatal,?re-surfaces it at the wrapper). Fingerprints diverge by step path so copies don’t aggregate. Not the BE-3541 axis; still live on developb5770766. Trigger was a DuckDB duplicate PK already fixed by MR !610 (dab6cba6) but absent from prod7c1ef44f. - BE-3117 Per-Flow Error Context Store — “OEF v2”: one aggregate business
ERRORper flow run with full fidelity.ErrorRecord+FLOW_ERROR_CONTEXTtask-localVec+record_error!macro (TT-muncher, in mando-lib not mando-core) +flush_flow_errors/FlowCompletionGuard. Invariant: record exactly once per flow, gated bylogged_at_site. Hand-written redactingSerialize(drops bodies whenhttp_context.sensitive). Removes OEF v1’s dd_formatter ERROR→WARN demotion. Fixed pre-existingMarketNotFounddouble-log viamark_logged(). - BE-2272 — DD log JSON flattening to root (
flow.*,step.*,error.*siblings). - flow-step-log-message-dropped-2026-05-26 —
ErrorWithStepStatus::Log { message }payload silently dropped byStepResult::log()destructure +Displayimpl +status_or_errorcollapse. Only::Logarm broken;::Error(anyhow)correctly logs. - clippy-disallowed-macros-allow-placement-2026-06-22 — tooling gotcha behind the error-logging path: mando bans
tracing::error!/log::error!(clippy.toml) to force logging throughmando_core::error!; that wrapper’s own#![allow(clippy::disallowed_macros)]must be a module-root attribute inmando-core/src/error.rs, not at the call site (call-site/block allow is ineffective on clippy 1.95). Found during the BE-3117 error-handling redesign. - Mando AGENTS.md Master Guide — correction: NO
ErrorCodederive and NOmando-lib-macrocrate ondevelop(verifiedgit grep ErrorCode origin/developempty) — worktree experiments only (poc-error-extractor, BE-2023, BE-1595/BE-3482); mando-lib-macro marked outdated.error.kind(fully-qualified path) /error.code(Enum::Variant) /error.sourceare extracted at runtime bymando_core::error!from the Debug repr, no derive. Canonical pattern: every component defines athiserrorenum…Error;anyhowonly in init/flow-step glue; HTTP mapping via deterministicmap_*_error_to_response(same variant ⇒ same status); py-mando maps everything to the singlePyMandoErrorviamap_*_errorfns (mando-lib/src/python/error.rs).doc/errors.json= untracked generated catalog (generator not in CI — likely poc-error-extractor worktree). Direction of travel: log-once-at-step-boundary (docs/error-handling-redesign-plan.md). Round 2 (2026-07-13): the redesign PARTIALLY landed in commitae6d1098—error.message= root cause,error.stack= source chain,error.fingerprint={code}|{step_path},ErrorWithStepStatus.logged_at_sitedouble-log guard withnew()/logged()constructors; NOT landed:error.trace,.step_context(); the plan doc +doc/errors.jsonremain untracked on develop. - Mando MR Review Patterns - review-mandated error policy (15+ “should be error” comments + policy statement from balazs, MR !481): failures are ERRORS, never warnings; log via
mando_core::error!with an error-code enum variant;Fatalfails the execution; NO custom error message strings (the error’sto_string()IS the message, human context goes in themessagefield). - BE-3657 error_stack Adoption - LOCKED decision (2026-07-16, planned/awaiting go): full
error_stack=0.8.0adoption over the ErrorCode-derive direction. Principle “leave what we can to error_stack”: mando adds only theErrorCodefull-path attachment (captured statically inCodedExtatchange_context/newsites) + extraction fns + anerror!(report=...)arm. Field contract:error.code==kind==type==outermost full-path code;error.errors= codes array;error.details=[{code,file,line,message}]; fingerprint full-path. Crate’s own tree rendering with codes as attachment sub-lines. Boundary message template"{outermost}: {static}: {chain}". Phase-2 direction: per-variant#[error_meta(event_type, resolution step, business_message, description)]→ details enrichment + resolution-doc codegen (phase 1 must not preclude; attachments are open-ended). Branchfeature/BE-3657not yet created, off BE-3656 heada0735ebb; spec/plan underdocs/superpowers/{specs,plans}/2026-07-15-*error-stack*. UPDATE 2026-07-22: phases 1+2 BUILT + PUSHED @2baea68a(13 commits atop the rebased BE-3656 head8f50ef2a); boundary template FLIPPED static-first"{static}: {outermost}[: {chain}]"(2026-07-16); MR only after !578 merges + explicit yes, thenrebase --onto develop; standalonecargo build -p mando_corebroken by a pre-existing BE-3643 uuid/serde issue (workspace graph fine). UPDATE 2026-07-22 afternoon: !578 MERGED (merge4a0d297f= develop tip); rebased onto it zero-conflict (2baea68a→22ccae93, patch-id byte-identical, pipeline2696324182green) + DD_SERVICE env fix appended (ab8cfd11, branch now 14 commits); MR pending ONLY on Andras’s yes. UPDATE 2026-07-22 evening: phases 1+2 ride MR !580; phase 3 PLANNED + approved: scope “plumbing + adapters” (rejected: plumbing-only, full workspace breadth), mechanics = seam replication per the Volue pilot (rejected: native Report signatures, boundary-only wrapping), same ticket (branchfeature/BE-3657-phase3off develop AFTER !580 merges, one phase = one MR); conformance-tightened against Balazs’slogs-and-apm.md(line 157 absolute: ALL tuple/transparent variants restructured;error.details.attributesvia new mando-coreErrorAttrlanding before any adapter; line 159 http roll-up = outermost-winsHttpContextpick,with_reportfillshttp_contextwhen unset; auth providers: no bodies/tokens, method/url/status only); adapter order auth providers → volue/ems completion → volue/atp → alpiq/ebs (SMB, share/path ErrorAttr) → mdr/opl/data_platform/et_3000 → fingrid → metis → mando group → position_manager; vetoable defaults: archiver deferred to the repos phase, startup mock ported to mainline behindMANDO_DEBUG_MOCK_ERRORdefault-off; spec/plandocs/superpowers/{specs,plans}/2026-07-22-*phase3*(15 tasks); execution NOT started, gated on !580. UPDATE 2026-07-23: !580 MERGED (squashde7cd4c6, merge4c3442df, zero review comments); phase 3 EXECUTED subagent-driven onfeature/BE-3657-phase3(14 tasks + final whole-branch review; 20 commits, HEADe0184bc3, pushed + ls-remote verified, CI running, NO MR yet): mando-coreErrorAttr+ per-levelerror.detailsattributes +error_attr_value+report_first_attachment+with_reporthttp roll-up; ALL in-scope adapter groups migrated (*_reportedseams, exhaustive no-wildcard*_error_from_report, zeroerror(transparent)left inmando-lib/src/adapterincl. archiver); auth providers first (no bodies/tokens, sentinel leak tests); PyO3map_report_errorforward plumbing (grep-proven unreachable this phase); sharederror_json_field_valueacross all three log formatters;MANDO_DEBUG_MOCK_ERRORenv-gated report-backed mock. KEY FINDING (formal spec exception): onlyvolue/emscarries Reports to the step boundary in production (Error = Report<VolueEmsError>); the other seven groups are WALLED (flat-typed service towers +pub(crate)seams make cross-crate wiring structurally impossible), chains flatten at legacy wrappers until a services-phase tower restructure - every wall independently verified genuine. Gates ate0184bc3: workspace clippy 0, 574 lib tests / 0 failed, no lock churn, 61 files +4390/-1482 vs develop; follow-up bundle triaged (auth-provider pre-existing body logs, ErrorAttr ids on non-success terminals only, py delegation tests CI-only, data_platform test panic-safety, oplclear_strategiesportfolio attr,map_errclosure-form drift). develop since moved (6fae64a5); pre-MR rebase possible on go. UPDATE 2026-07-23 (local e2e): phase 3 telemetry VERIFIED LOCALLY end to end from the real runtime (branch binaryb394e0d4, rebase-descendant ofe0184bc3, run as a host process against the mando-cli infra stack): boot mock 4-level report (error.errors4 full-path codes,error.detailsreal file:line +attributesincl.data_group,error.stackbox tree with theErrorAttrsub-line + the opaqueHttpContextattachment, codekindtype, root-cause message, trace correlation); Volue EMS spot single-level fingerprintcode|step_path(spot_data.rs:79); Volue EMS retrieve TWO-LEVEL auth chain live (ems.rs:217+ems_auth_provider.rs:155, the final-review auth level confirmed); Metis full-path code with NOerror.errors(walled-tower flattening as designed); dd_formatter real nested JSON; 12 error lines to Datadog EU via direct intake (env:dev-local-levander). Run recipe + friction: mando-local-host-run-recipe-2026-07-23. UPDATE 2026-07-23 evening (MR + short-code decision): phase 3 IN REVIEW as MR !585 “feat: adopt error stack across all adapters” (feature/BE-3657-phase3→ develop, heada58df341, 22 commits, no conflicts, branch pipeline green on the same sha; reviewers krisztian.fekete1 / gabor.nagy6 / balint.budavoelgyi / jozsef.nagy1). Same head implements the NEW DECISION (Andras, 2026-07-23) amending the field contract:error.codegoes SHORT (Enum::Variant, bare struct name when variantless, per the team spec’s line-100 mobile-readability intent) whileerror.kind/error.type/error.errors/error.detailscodes/error.fingerprintALL stay full module path - mando-coreshort_error_coderesolver (PascalCase-segment heuristic, valid due to house naming), applied at the 4 macro emission sites only (span/boundary layers passthrough), pinned by testkind_type_fingerprint_stay_full_while_code_goes_short, 577/0 workspace lib tests, verified LIVE locally + forwarded to Datadog (env:dev-local-levander). Consequences: the “mobile readability rationale is dead” pending team sentence WITHDRAWN (conformance restored); DD cutover impact for@error.code-keyed assets shrinks (short form resembles legacy codes);@error.sourceRETIRED (renamed toerror.messagein the redesign, pinned by a mando-core regression test) - delete or rekey any facet/monitor still on it to@error.message/@error.errors(an emptyerror.sourceconsumer was hit and fixed by Andras 2026-07-23). UPDATE 2026-07-24 (Caused-by stack renderer): NEW DECISION (Andras) REVISING the locked “crate’s own tree, codes as sub-lines” trade-off:error.stackrendering switches from the error-stack crate’s Debug tree to a custom prefix-style “Caused by” chain renderer, because Datadog collapses newlines in several views and the crate’s ASCII connectors (|-, lone|,|->) turned into unreadable pipe soup in the real DD UI. Per-level format:{short code}: {message}head (first level bare, subsequent prefixedCaused by:) +at file:line:column+with key = valueperErrorAttr; opaque attachments no longer mentioned. Commit1f9cd611(new MR !585 head, 23 commits):render_stack_treewalks frames likereport_details; crate debug-hook plumbing (install_debug_hook,Charset/ColorMode,HOOK_INIT) deleted as dead; 579/0 workspace lib tests incl. an exact multiline golden pin; verified live + forwarded to Datadog (2026-07-24env:dev-local-levanderevents carry the new stacks AND the shorterror.codetogether). Datadog research recorded: Error Tracking for logs needs error status + service + (error.kindOR a validerror.stack) and mando always emitserror.kind(stack format never gates tracking/grouping); DD’s valid-stack bar wants a frame with a FUNCTION NAME + filename, which error_stackLocations never carry (mando stacks never frame-parseable regardless of format); thesourcetag auto-parses only conventional language stacks; remappers relocate but do not parse - soerror.stackis purely presentational, optimized for human readability and graceful newline-collapse (which theCaused by:separators survive). UPDATE 2026-07-24 (error-authoring DX overhaul): decision series (Andras), north star: error reporting must not break Rust’s natural flow,?is the API; 3 commits (branch now headf771805e, ~27 commits, pushed, riding MR !585):f5beabd4report vocabulary (new_coded/CodedExt/change_context_codedDELETED workspace-wide →ToReport::into_report(crate already exportsIntoReport, hence the name) /Result::reported()/.wrap()/.attr(k,v)/flatten_reportfor Clone enums collapsing 6 conversion fns to one-liners; 307 sites, 37 files; bonus find: 20map_err(new_coded)fn-pointer sites - the exact#[track_caller]-losing form - had degraded locations until swept);4e519269shorterror.details[].code(the details panel is the human panel,file:linecarries precision;errors/kind/type/fingerprintstay full path; split pinned by a test in ONE emission);f771805enative report signatures REVISING the phase-3 seam-replication decision (dual*_reportedseams “not fit for a codebase this big”: ~35 flat twins deleted, bareMandoReport<C>(Report<C>)newtype in mando-core with Deref /as_report/into_inner/ delegating Display+Debug /std::error::Errorsource-None / plainFrom<Report<C>>?auto-lift +MandoResult<T,E>alias; adapters ONE fn per op returningMandoResult; ~50 consumer edits flattening at tower edges; ems towers keepReport<VolueEmsError>internally withinto_innerat 5 sites; 5 dead conversion fns deleted; the walls stand, the seam mechanics are gone). Gates 580/0 workspace lib tests + clippy clean. NEXT MR spec’d (docs/superpowers/specs/2026-07-24-be3657-report-derive-design.md):#[derive(MandoReport)]From-impl generation (#[report(from(Src))]display-mapping default,#[report(wraps(Inner, message = "..."))]chain levels,MandoErrormarker bounds at boundaries + clippy ban onReport::new); spike (scratchpad/spike-trackcaller, rustc 1.89.0, error-stack 0.8.0) proved directFromonReport<LocalEnum>orphan-blocked (E0117, naive design dead) while#[track_caller]through the local newtype propagates?-site locations EXACTLY (no ergonomics-for-locations sacrifice needed, despite Andras offering it); grounding pass caught 2 design regressions pre-implementation (from(Src)must display-map or source fields kill Clone/flatten;wraps()needs an explicit static message for struct variants). UPDATE 2026-07-27:error.stackREVERTED to error-stack’s default Debug tree (commite813a84c, MR !585 head; RE-REVISES the 2026-07-24 Caused-by renderer back toward the original locked decision 3 - ASCII, no color, full-path codes per frame via the ErrorCode debug hook, the DD newline-collapse quirk accepted as display-only) whileerror.details[].codestays SHORT (deliberate asymmetry); revert conflicts hand-resolved, 578 lib tests + clippy clean.MandoReportFINALIZED as the name for both the newtype and the derive (overTrace/ErrChain, zero churn).derive(MandoReport)MR STARTED on stacked branchfeature/BE-3657-deriveoff the phase-3 tipe813a84c(transplant at merge:git rebase --onto origin/develop e813a84c feature/BE-3657-derive); MR !585 still open, zero review comments,not_approvedthe only gate. UPDATE 2026-07-27 (short codes REVERSED): Andras reversed his own 2026-07-23/24 short-code direction - commit4df2562a“refactor: restore full path error codes” (the NEW MR !585 head) revertsa58df341(shorterror.code) +4e519269(shorterror.details[].code), soerror.codeANDerror.details[].codeare FULL module path again, uniform withkind/type/errors/fingerprint; the short/full split is gone.short_error_codehelper DELETED with its tests (zero refs workspace-wide); clean revert + one import fix; clippy clean + 575 lib tests / 0 filtered. DD-cutover consequence: full-path break is back to FULL impact (nothing resembles the legacy short codes),@error.details.codefacet is full-path, and the “full-path-kills-mobile-readability” team sentence (logs-and-apm.mdline 100) is UN-WITHDRAWN (needs re-raising). Derive branchfeature/BE-3657-derive(zero own commits) fast-forwardede813a84c→4df2562a; transplant nowgit rebase --onto origin/develop 4df2562a feature/BE-3657-derive; plandocs/superpowers/plans/2026-07-27-be3657-report-derive.md. Error-postfix question answered:*_reportedtwins GONE (~35 deleted byf771805e), 13*_error_from_reportconverters remain BY DESIGN (the walls; removal deferred to the services phase). UPDATE 2026-07-28 (DX overhaul v2, tipb7b04b71, 4 commits/21 total): SECOND Andras-driven DX pass onfeature/BE-3657-derivereaching bare?for EVERY source + a single#[mando_error]enum annotation. NativeMandoReportbirth -into_report()/reported()returnMandoReport/MandoResultat CONSTRUCTION and the newtype’s stdErrorimpl is REMOVED (the anyhow trick), making the blanketFrom<S: MandoFrom>coherent (5acd801b);#[cause(Src1, Src2)]bare source lists replace the 07-27from/mappedbridge grammar with.mapped()/MappedExtDELETED (8ebd6ad9;wraps/.wrapped()?kept); the#[mando_error]attribute macro (Error + Debug + marker + cause routes in one annotation, sharedgenerate_routesengine) replaces#[derive(MandoReport)], whose export is DELETED (4221f00b+ 28-enum sweepb7b04b71, thiserror imports dropped in 20 files). LESSONS: a SECOND scratch-crate false-green (localFakeReportmasked open-world coherence, orphan on 07-27 - scratch crates lie); the.lift()measurement found 227 broken seams not ~11 (implicit?seams dwarf visible.into()sites), ~160 vanishing under native birth (towers pay.into_inner()at ~40-50 sites,.lift()/LiftExtonly at tower edges). Reviewer disproved the attribute-ordering hazard (attribute macros expand before derives). Byte-identical (A/B + 3 goldens) + wire e2e PASS vs86f3439f; intended delta:mando-simulatorinit moved to the report arm. UPDATE 2026-07-28 (later): the derive branch was RENAMED tofeature/BE-4000(its own Jira key; worktree dir stays.worktrees/BE-3657-derive; transplant nowgit rebase --onto origin/develop feature/BE-3657-phase3 feature/BE-4000). A SECOND assurance pass (3 Opus lenses over86f3439f..cd5d53a7, run because the 07-27 five-lens predated DX v2) returned CLEAN on all six dimensions (reflexive-Fromdisjointness + attribute-ordering both proven by scratch compile; native-birth towerinto_inner/liftlevel-preserving; 585 lib + 15 trybuild + A/B 5 + 3 goldens; clippy 0; single shared route-gen engine), with three latent no-instance edges documented (last-segment Error/Debug dedup vs a customderive_more::Debug; generic error enums unsupported by route-gen, pre-existing; hardcoded::thiserrorpath) and an OPEN judgment call for Andras (aninto_bare_report()helper collapsing ~52into_report().into_inner()tower sites, worth it only if unwalling is NOT the next MR). A 2-commit standards fix-wave (479cc312scoped rustfmt of theMandoFromon_unimplemented note +cd5d53a7renaming the two “duplicate report mapping” strings to “duplicate cause mapping” with 3 regenerated trybuild.stderr) moved the BE-4000 tip tocd5d53a7(clippy 0, report_derive 8, lib 585/0). On phase-3 !585, Andras RESTORED the oplcreate_ordersdead code review round 1 questioned (commit0427c68d, adapted to the current idiom, still dead; phase-3 tipee84ea50→0427c68d, all 3 !585 comments resolved; the BE-4000 base staysee84ea50, transplant excludes it). BE-4000 = the FINAL derive branch, DX v2 DOUBLE-ASSURED; both await !585 approval → merge → transplant → Andras’s MR yes. UPDATE 2026-07-28 (rebase + pipeline green): MR !585 was REBASED onto develop29ed8e34(22 commits ahead, opening a merge conflict) to clear it - phase-3 tip0427c68d→c3519e73, force-with-lease pushed, all 30 commits preserved (git range-diff27=/3!), gates 605 lib/0 + clippy clean, pipeline SUCCESS on the rebased head. LANDMINE: develop had merged 2 NEW consumers of the simulator-client API phase-3 migrated (mando-lib/src/service/mando/asset_simulator.rs+mando-bess/src/live_simulator_init.rs) that BROKE THE BUILD after the rebase WITHOUT being a textual conflict (brand-new files, no overlapping hunk; only a full workspace build catches it) - both migrated tosimulator_client_error_from_report(e.as_report())and FOLDED into the signature-owning commit so every commit builds (footprint 81 → 83 files). krisztian.fekete1 had APPROVED !585 after round 1; the rebase’s new commits RESET the approval (GitLab drops approvals on ANY new commit), so !585 isnot_approvedagain (conflict gone, pipeline green) and needs re-approval. Repo handoverdocs/superpowers/HANDOVER-error-handling-2026-07-29.md(untracked) supersedes the 07-24 one. Current tips: BE-4000cd5d53a7, !585 phase 3c3519e73, develop29ed8e34. - BE-3482 Datadog Logs and APM Conformance - BE-3482 status note (2026-07-13): core conformance MERGED 2026-07-08 (MR !558 squash
ae6d1098); the unpushed continuation adds the fingerprint ACTIVITY component (error.fingerprint=code|path|activityviaErrorWithStepStatus.activity: Option<String>+ consumingwith_activity(), all constructors default None so behavior is bit-identical when absent; reference wiring inmando-lib/src/service/volue/ems/ts_data_send.rs, every per-data-group error carries the data group name). Orphan commit478cd993(addsdd_formatter::app_version(), removes 3 panicking unwraps in mando-bess, fixes the known local mando-bess panic) was salvaged same day: cherry-picked onto the fingerprint branch asbc3727ba(Andras’s call, instead of a separate MR); the branch has 2 commits, gates green on both (clippy exit 0, 387 passed / 0 failed across 22 suites), pushed 2026-07-13 and open as MR !569 awaiting review; the old worktree + localfeature/BE-3482are purely historical and archivable. Known limit: at-site-logged variants (VolueEmsError::ApiError) reach the boundary as WARN with no fingerprint until the single-emission migration (own future ticket). UPDATE 2026-07-22: !569 MERGED (merge53912a70); the single-emission migration MERGED as !571 (BE-3541 Single Error Emission), closing the limit; !570 (pymando) + wheel also landed. - BE-3541 Single Error Emission - MERGED via MR !571 (squash
d1cf9c75“refactor: emit step errors once by removing the logged-at-site path”, merge1c4c33a5): one ERROR emission per step failure at the step boundary withHttpContextcarried on the error;ErrorWithStepStatus.logged_at_site+logged()REMOVED - construct withnew/message, never log at the detection site; unlocks fingerprint +http.*for previously at-site-logged variants. - mando-cli-datadog-local-logs-2026-07-22 -
mando up --datadogverifies BE-3482’sdd_formattererror/log JSON (error.code/error.fingerprint) in the real Datadog EU UI from a local run. Confirms the bodyddtagsservicetag overrides infra enrichment whiledd_formatterddtagscarries NOenv(agentDD_ENVauthoritative). Filterenv:dev-local-<user>→service:bess-os-service-mando. - BE-4067 whole-mando error unwalling - FINISHED the error-stack unwalling (BE-4047 began it): removed ALL production raw
error_stack::Report<C>frommando-liband adopted the full BE-4000#[mando_error]/#[cause(...)]/bare-?idiom. GOAL ACHIEVED, verified:git grep into_mando_result -- mando-lib/src= 0, the only productioninto_report().into_inner()is the intentionalwrap_http_callhelper body, zero raw-Report-returning fns. Branchfeature/BE-4067@0dff4f2dstacked onfeature/BE-4047(!599), 9 commits (one per subsystem: wrap_http_call helper / fingrid / data_platform / metis / ebs / opl report_polars born-lift / ems service tower / atpVolueAtpAuthenticationProviderErrordeletion / fingrid cleanup). NEW PATTERN:wrap_http_call<E,F>(source: HttpCallError, make: F) -> MandoReport<E>(adapter/http_cause.rs) - a SHARED helper (NOT a#[cause]route) replacing the triplicatedHttpCallError->ApiErrorwrap at metis/fingrid/ems, becauseApiErroris multi-field + reattaches an opaqueHttpContexta message-only#[cause]route would drop; MUST keep#[track_caller]. Recipe step (d): message-only foreign-error maps →#[cause(ForeignErr)]+ bare?; multi-field variants (NonSuccessStatus/InvalidUrl/CannotParseJson) and context-prefixed/multi-source maps (ebsSambaError, data_platformSdkError) KEEP manual construction. EMS SERVICE tower gotcha:type Error = Report<VolueEmsError>→type Error = MandoReport<VolueEmsError>(the assocErrorISE, NOTMandoResult); lives underservice/volue/ems/*, notadapter/. Emission pinned via per-subsystem BE-4014 Datadog Error-Rendering Test Harness goldens (location-trimmed, 11dd_emissiontests pass). Final review READY-TO-MERGE no findings; lib 376/0, clippy —all-features clean; atpnew()error change absorbed by anyhow?atmando-bess/lib.rs:198(no cross-crate ripple). North star: the mando-ERRORS axis of conformant traces+errors across all services; still open: mando trace conformance, downstreambess-optimization/bess-forecast-day-ahead/dashboards, phase 4#[error_meta], the unlandederror.tracefield +.step_context(). - BE-4067 error DX clarity - the grammar-clarity + mando-bess-lib adoption follow-on to the unwalling (branch
feature/BE-4067-grammar@0a3c2382, 10 commits, LOCAL/NOT pushed, stacked on unwalling tip9038b0c6). Phase 1 (emission-NEUTRAL) NEW GRAMMAR replacing generic#[cause]on#[mando_error]enums with self-naming verbs:#[reframe(Src)](flatten FOREIGN error → your{message}, bare?),#[chain(Inner)]/#[chain(Inner,"msg")](wrap a MANDO error + frame,.wrapped()?),#[transparent(Inner)](re-export; macro injects thiserrorerror(transparent)+#[from]+aMandoFrom;#[error(transparent)]inside#[mando_error]now a hard macro error). MENTAL MODEL: field shape = behavior ({message}=reframe, unit=chain,(Err)=transparent); reframe REPLACES with your message, chain ADDS on top, transparent SHOWS THROUGH (reframe vs transparent = source becomes TEXT vs stays ERROR). Plus reflexiveMandoFrom<Self>→ bareResult<T,E>?s into MandoResult (.reported()now rarely needed, ~35 sites dropped);MandoReportOPAQUE (Derefdropped,as_report()/into_inner()only) +flatten_reportclippy-banned (workspace-wide viadisallowed_methods=denyoverridingall=allow) = the no-silent-stack-loss guard. Phase 2 (SEMANTIC / telemetry change): 25 mando-bess-lib service enums adopted (#[mando_error], 72 newtypes →#[transparent]) + ~21 boundaries switched to the rich report path viaErrorWithStepStatus::reported/from_report= a deliberate DD telemetry change on financial flows (idc_order, auction):error.errors/error.detailsADDED,error.stackcolon-chain → tree,error.messagesource shifts;code/kind/typeUNCHANGED; captured as inlinedd_emissionasserts (mando-lib Phase-1 goldens stay byte-identical). GOTCHAS: workspaceall=allowdisablesdisallowed_methodsexcept mando-core (fixed P2.3); py_mando “could not compile (lib)” = macOS cdylib LINK failure not a compile error (cargo checkclean);cargo checkskips#[cfg(test)]; the#[chain]reshape drops the transparent#[from](breaks bare?at other construction sites); whole-file rustfmt reflows legacy lines. Gates @0a3c2382: clippy--release --all-featuresclean,-p mando_lib --lib379/0,-p mando_bess_lib10/0. PENDING: telemetry sign-off, final review, push + MR (each a separate yes). - BE-4014 Datadog Error-Rendering Test Harness - develop-based
pubtest helperrender_dd(|| error!(...)) -> Vec<serde_json::Value>(+render_dd_one) inmando_lib::app::dd_formatter::test_supportdriving the REALerror!macro through the REALDatadogFormatter(promotes the previously-private capture harness fromdd_formatter.rs’s test module);--features app,test-util(NEW mando-libtest-util = ["mando_core/test-util"]), 9 worked examples, runcargo test -p mando_lib --features app,test-util. Bundles a develop DD error-emission contract survey (now test-pinned; the error-stack !585/BE-4000 branches change/extend it): report arm =errors[]full-path outermost-first +details[]{code,file,line,message} per level, NO fingerprint, codekindtypeoutermost, messagedeepest; plain + http arms = BAREfingerprint==code(the{code}|{step_path}|{activity}shape isStepResult::log’s, NOT the macro’s) + NO errors/details; NO render-layer redaction (http.*.bodyverbatim, no-leak = CALLER passes None);error.detailshas NOattributeson develop (ErrorAttr branch-only). Branchfeature/BE-4014@9a8a7538PUSHED, not yet an MR. UPDATE 2026-07-29 (golden matcher): the harness gained a golden/snapshot MATCHER (Andras’s A/B style: assert a STATIC expectederror.*JSON against the generated) -assert_dd_matches(generated, expected)+assert_error_renders(body, expected)in the sametest_support; objects match as a recursive SUBSET (extra generated keys ignored so you omit volatilefile/line), arrays same-length element-wise, strings exact OR a leading*=ends_with(grep affordance for full-path codes), scalars exact, PANICS with the JSON path on mismatch; the field-by-field ABSENCE tests kept as complementary (a positive golden cannot assert a field is absent); self-tested 8 tests incl.should_panicnegatives + 3 goldenjson!({...})example tests as the new whole-shape template; 39/0 under--features app,test-util; tipeac5fb85→9a8a7538. - Test Env Deployment State 2026-08-18 - where the error chain actually IS deployed. On test (image
1.17.0-dev.2749963346.336ce49f, mando develop336ce49f): !571 BE-3541, !585 BE-3657-phase3, !592 BE-4000, !597 BE-4014 and !599 BE-4047 are ALL live (ancestry-verified); !601 BE-4067 is NOT (mergec2c9c9a2landed 19:26 UTC, ~5h after the test apply, and test has not been bumped since). So any Datadog read oferror.errors/error.details/tree-shapederror.stackon test measures the PRE-!601 world. Green-but-unpinned images that DO carry !601:1.17.0-dev.2751762903.c2c9c9a2and1.17.0-dev.2756529188.b5770766(develop tip).
External Integrations
- Agent Context - the External Integrations table was rebuilt 2026-08-13 against
poc/e2e-tests: ~15 external systems + 6 auth providers (was 7 systems), each with its plannedMaintenance/{country}/{Service}datapoint, protocol, auth model and env prefix. Authoritative in-code registry = thesystem = "..."attribute on#[step(...)]. Likron = OPL (system = "OPL (Likron)",mando-bess-lib/src/service/intraday/idc_order.rs:122). GOTCHA: system strings are NOT normalized (volue_atp_order_book.rssays"Likron"but talks to Volue ATP;as_auction_update/energy_bids.rssays bare"Volue"), so aggregation byflow.step.systemis broken. Config split: OPL/Volue ATP/Fingrid/Entra ID useparse_config_with_prefix; EBS/MDR/Metis/ET-3000/Volue EMS/Position Manager/OnePassport still#[derive(Envconfig)]. - External Service Outage Gate - bitmask design - the 13
Maintenance/{country}/{Service}interval-double datapoints that back the planned outage gate, and how flow steps declareexternal:deps against them.
Infrastructure / AWS
- AWS CLI Access via Identity Center - how AWS CLI auth works since 2026-07-21:
[sso-session alpiq-sso]in~/.aws/config(IdC start URLssoins-69878836cb6e09c7, eu-central-1) + fourDeveloper-role profilesbessos-{dev,test,int,prod}mapping to accounts 794038257734 / 071128452852 / 621553445748 / 282467977019 (same set as the section 14.1 deploy matrix); oneaws sso login --sso-session alpiq-ssocovers all four; saml2aws retired but left on disk. Landmine: the RTK hook manglesawsoutput, alwaysrtk proxy aws .... Used for ECR/ECS access during mando deployments. - BE-1597 DuckDB UI Exposure Research — reusable mando AWS-infra facts gathered for BE-1597: mando ALB
aws_lb.bess_os_serviceisinternal=true, 443-only, single target group on port 8080 (bess_os_load_balancer.tf); bastion (bastion.tf) t3.medium in a private subnet, no public IP, SSM Session Manager only, egress-all + ZERO ingress; mando runs on ECS Fargatedesired_count=1withenable_execute_command = environment=="dev"(bess_os_ecs.tf:499), and ECS Exec is interactive-only — no port forwarding (SSMAWS-StartPortForwardingSessionToRemoteHostvia the bastion is the actual port-forward path); thebess_os_ecsSG (security_group.tf:4-23) uses INLINEingressblocks — a standaloneaws_vpc_security_group_ingress_ruleis silently deleted on the next apply; must use adynamic "ingress"block inside the existing resource; NO NAT gateway / VPC endpoints defined (the VPC is abase-vpctag data lookup owned by another team) → Fargate egress to duckdb.org UNVERIFIED;int+prodboth map to account groupint_prod(main.tf:5-6). - BE-1597 DuckDB UI — Design — the approved terraform/infra shape for BE-1597: open port 4214 to the DuckDB UI via a
dynamic "ingress"block INSIDE thebess_os_ecsSG referencing the bastion SG (not the VPC CIDR), gatedvar.environment == "dev"— a standaloneaws_vpc_security_group_ingress_rulewould be silently reaped because that SG uses inline ingress blocks. No ALB listener / target group / health check (mando ALB alreadyinternal=true); the bastion SSM hop (AWS-StartPortForwardingSessionToRemoteHost) is required because ECS Exec is interactive-only. Blocking pre-check: Fargate egress to extensions/ui.duckdb.org is UNVERIFIED (no NAT / VPC endpoints;base-vpcowned by another team) — if blocked, Phase 1 can’t ship and no terraform fixes it.desired_count=1+ Fargate re-IPs each deploy, so theduckdb-ui.shhelper re-resolves the task IP every run. - Mando Deployment Ceremony - the environment matrix. REWRITTEN 2026-08-18: there are no component groups any more. 4 envs = 4 independent
terraform/environments/<env>.tfvarsfiles;terraform/main.tfreadsvar.components["mando"].versionandvar.ecr_repos["mando"]FLAT andlocal.environment_groupis gone from the wholeterraform/tree. ECR strings moved into the per-env files (dev+test identical843164609896…/poc/mando/deploy, int+prod identical748634852998…/poc/mando, but as separate copies that can drift). Accounts: dev 794038257734, test 071128452852, int 621553445748, prod 282467977019. int_prod naming trap survives only in older notes/AGENTS.md. Pins observed 2026-08-18 onorigin/develop@9600b34c: dev+test1.17.0-dev.2749963346.336ce49f, int+prod1.17.0-2718305160.c3f6530b. ⚠️ The local clone is onrc/1.11.0from 2026-05-27 - alwaysgit -C <iac> show origin/develop:terraform/environments/<env>.tfvars. Expected plan diff on a version bump: the mando container image in the bess-os ECS task def ONLY. - Test Env Deployment State 2026-08-18 - the test environment as of 2026-08-18: mando image
1.17.0-dev.2749963346.336ce49ffrom mando develop336ce49f(MR !610, built 09:07 UTC 2026-08-11), pinned by IaC16e8241and applied green at 14:12 UTC; nothing applied to test since. Includes the re-derivation recipe and the ancestry table of which error-handling MRs are live. - Dev Deploy 2026-07-14 (error telemetry) - terraform plan-reading lesson: a version bump producing 17 add / 19 change / 5 destroy was IaC develop drift catch-up (12 genuinely new resources: the
bess_os_frincoming_synclambda stack + 2 RDS analytics SG rules) plus immutable task-definition replacement mechanics (all destroys were “must be replaced” task defs), NOT danger; the second apply after the migration fix was down to 4 added / 19 changed / 4 destroyed. - fr-region-missing-datadog-logs-2026-07-21 - the FR deployment shape: dev-only
module "bess_os_fr"(terraform/bess_os_fr.tf,count = var.environment == "dev" ? 1 : 0) instantiating sharedterraform/modules/bess_oswithregion_code = "fr"in account 794038257734/eu-central-1; log shipping is awslogs → CloudWatch → subscription filter →datadog-forwarderLambda (dd-logs-forwardermodule from bit-tf-modules v3.21.4), the DD agent sidecar does metrics/APM only. Landmine:modules/bess_oslacks the forwarder wiring, so every regional instantiation ships log groups with no Datadog forwarding. - mando-cli-datadog-local-logs-2026-07-22 - DD_API_KEY acquisition route (2026-07-23): the deployed Datadog API key lives in AWS Secrets Manager, dev account, secret
DdApiKeySecret-pkeeEykkaqu3(referenced from optimization-universe-iacterraform/secret.tfvia CI varDATADOG_API_KEY_SECRET_NAME); fetch withaws sso login --profile bessos-devthenaws secretsmanager get-secret-value --secret-id DdApiKeySecret-pkeeEykkaqu3 --query SecretString --output text(eu-central-1). Needed because the Alpiq Standard Operations Datadog role lacks “API Keys Read” (org key page blocked) and Personal Settingsddpat_/ddapp_tokens do NOT work asDD_API_KEY(real API keys are plain 32-hex; app keys are 40-hex/ddapp_and pair with an API key for queries only). Follow-up: request the permission or a personal org key from the Datadog admin.
Licensing
- BE-1597 DuckDB UI Exposure Research — DuckDB UI frontend = proprietary MotherDuck code, blocks production use. DuckDB core, the prebuilt libduckdb binaries, duckdb-rs, libduckdb-sys, the
uiextension SOURCE (github.com/duckdb/duckdb-ui), quack, ICU (Unicode-3.0), yyjson are all MIT/permissive (IP: Stichting DuckDB Foundation) — safe to vendor closed-source with attribution/NOTICE. BUT the DuckDB UI frontend assets served fromui.duckdb.orgare proprietary, source unpublished (DuckDB’s own 2025-03-12 launch post says so); MotherDuck said Mar 2025 they were “reviewing licensing options” and as of Jul 2026 there is still no published license / terms of use. Served with no auth and (per DuckDB docs) “can access the data you load into DuckDB” → fine on a dev laptop / dev container, NOT acceptable for theint_prodtrading service. (The Quack remote-protocol extension, by contrast, is a core DuckDB-signed MIT extension — no such issue.) Cf. the GPL-avoidance decision in remotefs-smb to smb migration. - BE-1597 DuckDB UI — Design — the licensing finding turned into an approved decision: because the DuckDB UI frontend is proprietary MotherDuck JS served live from ui.duckdb.org with no auth (source unpublished, still no license as of Jul 2026), the spec forbids running the UI in int/prod and ships it dev-only (Phase 1) / on the developer’s own laptop via Quack (Phase 2, where MotherDuck’s JS is already trusted). Everything else in the supply chain (DuckDB core / libduckdb / duckdb-rs / libduckdb-sys / the ui-ext SOURCE / quack / ICU / yyjson) is MIT/permissive and safe to vendor closed-source with a NOTICE bundle. Evidence: License finding (the important one).
mando-cli
- mando-cli-v2 — current architecture (AppContext, CommandDelegate, 13 commands).
- mando-cli-docker-lifecycle — DockerClient + ContainerSpec, bollard 0.19 wiring.
- mando-cli-mock-command-2026-04-28 —
mando mocksubcommands and WireMock infra. - mando-cli-mock-down-idempotent-2026-05-06 —
mando mock down+ WireMock healthcheck endpoint fix; canonical 7-step lifecycle pattern; idempotent-teardown rule (404 = success). - mando-cli-status-readonly-2026-05-06 —
mando statusmade read-only and bounded (connect_readonly,table_exists,statement_timeout = '2s'); status-must-be-pure-reads rule. - mando-cli-build-context-filter-2026-05-06 — yaml-driven
context_includesandbuild_filtered_tarto stop COPY-everything build-context hangs. - mando-cli-build-variants-shelved-2026-05-06 — SHELVED design for profile-driven build variants (dev vs release Dockerfile,
pre_command, 3-field profile syntax). - mando-cli-wsl-linux-build — how to build a Linux x86_64 / WSL release binary locally from an Apple Silicon Mac (musl static target, Docker
--platform linux/amd64, optional-querypath-dep gotcha). - mando-cli-v0.4.0-compose-bugs-triage-2026-05-26 — v0.4.0 compose-runtime triage: two confirmed bugs in the post-bollard rewrite;
context: .relative-path resolution + bare-slug positional arg activating a malformedrender_overridestub. Documents the load-bearingprofiles: ["{run_tag}"]invariant. - mando-cli-v0.4.0-mando-bess-binary-missing-2026-06-25 — design gap surfaced after the triage fixes (commit
2233959): the default profile buildsmandofrommando/Dockerfile, a thin runtime image that COPYs a pre-compiledtarget/release/mando_bess, but no builtin profile mapsmandotopull/artifactand nothing inuprunscargo build --release(--cargoisartifact-only). Freshmando upaborts the compose build. Workaroundmando up --mando=pull; durable fix (default →pull+ missing-binary preflight inup) pending user decision. Reported by gabi (WSL). - mando-cli-v0.4.0-piped-output-invisible-failures-2026-06-25 —
mando upfailures are invisible when output is piped/redirected (tester does2>&1 | tee log): the user gets only a barefailed (exit 1)and an empty.mando/compose-up.log. Two fixes in the compose runner + buffer: (A)--progress plainon thedocker composecall (compose.rs~L231) so compose stops writing progress/errors to/dev/tty; (B)eprintln!fallback inCliBuffer::add_log_line(opts.rs:57) when stderr isn’t a TTY, becauseindicatif::MultiProgress::printlnsilently no-ops there. Validated macOS (35→138 lines). Also clarifies mando-cli’s “registry auth” (GitLab API creds) ≠docker login(container registry) — the now-visible tester failure is a registrypull access denied. Built+validated, not committed. - mando-cli-simulator-runtime-2026-05-30 — second first-class Docker Compose stack
mando-simcoexisting with the devmandoproject; six runners generated from a data-drivenSIMULATOR_RUNNERSlist; own Postgres on host port 5433;RunProfile.compose_project/layers()routes commands; newsrc/runtime/service_stack.rs::select_profileshared resolver forlogs/exec/volume; defines the §A orchestrator↔runner env contract that the (WIP)mando_simulatorcrate,simulator-runnerimage, and six sim repos must implement. Supersedes GitLab MR !2 (balint). Landedb244f2eonfeature/simulator-runtime. Process lesson: use fullcargo test, not--bin mando(skipstests/). - mando-cli-simulator-env-contract-2026-06-02 — follow-up to the simulator runtime: §A env contract realigned to real images (
simulator:1.11.0-feat.2569583284.7d69a070+ runner shapef7aa42f); adds arbitrary-extras passthrough viafilter_extras/MANAGED_SIM_ENV_KEYS; sim-postgres aliased toSIMULATOR_DATABASE_*so one.envoverride controls both. Landedc3f0af7onfeature/simulator-runtime(8 files, +440/−67, 813 tests). - mando-cli-datadog-local-logs-2026-07-22 -
mando up --datadoglocal Datadog log shipping; 2026-07-23 end-to-end test ofdocs/datadog-guide.md(tipc2becf1): guide accurate, every claim verified. HAZARD: a stale pre-feature binary reporting the SAME 0.4.0 version silently swallows--datadogas a service arg and starts a realup; the guide’s flag-position warning cannot protect against it (suggested guide follow-up: a version-check note). Nit: the new error strings use em dash characters, against Andras’s conventions elsewhere. - mando-cli-as-forecast-removal-2026-07-24 - removed obsolete
bess-os-algo-forecast-as(Project::AsForecast) via compiler-driven deletion (enum variant/slug/aliases/config default; builtin profiles now 4 services; README five repos/two algos). LESSON: an enum variant used as a persistedHashMap<Project,_>map key in.bessstate.json— deleting it made a stale"AsForecast"key fail deser and theunwrap_or_defaultfallback silently WIPE the whole projects map (all overrides/toggles). Fix: per-key tolerantfilter_known_projectsmirroringsave()’s PascalCase keys viaProject::deserialize(NOTfrom_alias),warn!per dropped key, fixture restored as regression guard. Rule: deleting an enum variant = audit every persisted format keyed by it before shipping. - mando-cli-flow-run-2026-07-28 -
mando flow list/run/status: reproducible TEST-MATCHABLE flow execution (golden-file-diffable, one command + one params file). Linchpin: trigger id DB exec id every log line’s rootflow.exec_id(one Uuid); local builds ALWAYS emit dd_formatter JSON; server pre-creates step rows as Queued; trigger returns immediately (tokio::spawn). Follow = verbatim byte-preserved local streaming (single-writer, filtered by rootflow.exec_id) + universal step digest (exit 1 on Error/Fatal). Terminal conformance kv pointers (dd_query/dd_url/started_at/stopped_at) into Datadog. Flows auction/intraday/data-update/as-auction-update/manual-schedule (kebab URL, snake_caseflow_keybody, server pins versions). Working tree, UNCOMMITTED 2026-07-28. - mando-cli-otel-capture-and-tail-2026-07-28 - always-on OTel trace capture (
mando-otel-collectorcontrib 0.109.0 in everymando up, raw OTLP JSON NDJSON to<workspace>/.otel/traces/spans.ndjson, 50MB/3 backups;OTEL_EXPORTER_OTLP_ENDPOINTinjected only-when-absent;up --datadogadds APM fan-out via agent OTLP receiver + collector otlphttp) + new local-onlymando tail [logs|traces|all] [FILTER...]zero-query-language substring filter over gron-flattened telemetry (verbatim default,--flatshows matching assignments).flow rungained--span-file/--bundle. Bug fixed:ensure_infrahardcoded the collector config every command, somando statusafterup --datadogreverted it and killed APM on next restart — now derived from datadog overlay file presence. Consolidation: ONE piped-child seam (system/logstream.rs), ONE OTLP walk/reader (flow/spans.rs). Working tree, UNCOMMITTED 2026-07-28. - mando-cli-json-output-agent-skill-2026-07-23 -
mando --json <cmd>/MANDO_OUTPUT=json(flag wins, exact-match"json") renders all output as terse NDJSON on stdout (kv/step/err/data+ ALWAYS-LAST{"t":"result","ok","code"}frommain.rs’s single exit path); human mode byte-identical, spinners suppressed, logs/exec/compose chatter stay raw on stderr. One pure mappersrc/ui/json.rs::element_to_eventcovers all 21 commands via the Element seam (format!-built fort-first order; payload keys alphabetical,preserve_orderoff). Agent skill.claude/skills/mando/SKILL.md(64 lines): grammar + minimal invocations + error→remedy + frugality (status before logs, trust exit codes, never parse human tables);mock -pgoes BEFORE the subcommand. NO top-levelwarnevent (warnings aresteps:"warn"). Working tree, UNCOMMITTED 2026-07-23. - mando-cli-e2e-harness-2026-08-04 - two-repo data-level E2E harness (
poc/e2e-testsin mando-cli AND mando, UNCOMMITTED, never run live).mando e2e run <suite>phases wipe/up/ready/migrate/seed/per-set;mando verify <expect.yaml>standalone (flow/mock/logs/spans/datapoints/outbound). New dev-gatedPOST /data/insert/csv/{*datapoint_id}server-side (Arrow-IPC insert body makes CSV impossible client-side).mando queryREMOVED, dropping mandarrow-client/mando-core/arrow-array worktree deps so--all-featuresbuilds clean. Two criticals: self-confirming query-body tests, and zero WireMock mappings in the runner’s own stack. Superseded 2026-08-05: committed, rebased and GREEN 8/8 live. - mando-cli-e2e-live-green-2026-08-05 - the harness’s FIRST live run: GREEN 8/8 twice from wiped volumes against the rebased mando (
2af0c6c9). The 08-04 “only a live run catches schema drift” prediction fired verbatim — upstream rewroteDataPointIdserde from{id, id_fragments}to a bare JSON string, unit tests stayed green, only the live run saw the 400 (fix:src/e2e/datapoints.rsdata_point_id()→json!(dp_id), 4 tests updated; MR !6bugfix/e2e-datapoint-id-string). Plus two env drifts in mando’se2e/suite.yamland a NEW upstream restriction (DataPointId::newrejects non-ASCII-alphanumeric path fragments) that makes underscore-bearing ids 400 —docs/e2e-guide.mddoc drift to fix. - mando-cli-junit-per-assertion-2026-08-06 -
--junitnow emits one testcase per assertion instead of one per test set (feature/junit-per-assertion,9c39a88);mando verifygained--junittoo. The fix was plumbing, not logic —verify::runwas discarding an already-per-assertionVec<Assertion>and returning abool. Introduces the dual-label pattern:Label { name, case }wherenameis the terminal’s machine label (mock.requests[GET /path]) andcaseis JUnit’s human sentence (GET /path at least 1x), kept separate because the two consumers have different stability requirements. THE RULE: case names must derive from the EXPECTATION, never the observed result — GitLab keys test history on(classname, name), so a moving label reads as a test vanishing plus a new one appearing rather than a pass→fail transition (pinned bya_case_label_does_not_move_when_its_assertion_flips_to_failing). False-green trap: a set that never reached the verify engine has zero assertions, soset_casesemits a fallback case or the report would be green by omission. Known gap: amock.requestsentry with nocount/min/maxasserts nothing and now shows up as a named green testcase (“any number of times”) that mimics real coverage. - mando-data-insert-wire-format-2026-08-06 - why the harness KEEPS mando’s dev-gated CSV seed endpoint instead of inserting through the normal
POST /data/insert. Client-side Arrow is feasible (~250 LOC + ~17-20 crates;mando-repository/src/arrow.rs:59-88proves arrow-rs 56.2StreamWriteroutput reads through the polars reader) but was rejected on drift risk — the body encoding is an undocumented internal of polars’Deserialize for DataFramethat already changed between=0.45.1and=0.49.1, and nothing would test the producing side. Also notes the seed request’sContent-Type: text/csvis decorative (the server takesbody: String). - mando-repos-history-rewrite-2026-08-05 - attribution-free history rewrite of mando-cli
main/release/4 re-pointed tags + mandopoc/e2e-tests(2af0c6c9); merged source branch deleted, stale clones must--tags --forcerefetch. Carries the NEW rtk landmine: rtk-wrapped grep/log pipelines returned fabricated zeros; onlyrtk proxy gitwritten to a file is trustworthy, and an rtk-mediated negative result is never evidence of absence. - mando-cli-flow-coverage-2026-08-06 -
mando e2e run(main5a135b8) closes withflow not covered: <name>lines +flow coverage: N/M flows (X.X%), denominator fetched from the live service flow inventory (themando flow listendpoint — no list maintained in the suite); a failed fetch warns and skips, never fails the suite; +36 tests → 1686. Same day: test settest_set_1renameddata-update-beskar-socwith no JUnit history impact — sincefabc589classnames come from the case’sflow:field (dir name only disambiguates two cases sharing a flow), so case dirs rename freely and the junit note’s<test set>.<section>table is superseded,docs/is now untracked + gitignored as AI-internal (so repo-doc pointers in older notes are dead), agent skill gained an e2e case-anatomy section for authoringcase.yaml.
py-mando / Python Bindings
- pymando-rust-log-trace-correlation-gap-2026-09-02 — Rust
tracingevents inside the Python algo containers carry nodd.trace_id/dd.span_id/logger.name/thread_name:py-mando/src/log_formatter.rsis a second, thinner formatter, anddd_formatter’sTraceInfo/lookup_trace_infoare private AND behind theappfeature py-mando never enables.TraceFiltercontextvars (tracing.py) also never reach Rust events. Reusingdd_formatter’s helper is RULED OUT (it needsOtelDataand py-mando installs no OTel layer); fix is B1 metadata parity + B2 bridging the ddtrace span context Python→Rust per-call. - Mando AGENTS.md Master Guide — round-2 (2026-07-13) verified py-mando facts: py-mando-simulation split out of py-mando in commit
3559042d—py_mando.SimulationRunneris GONE (breaking change); the new package requires Python ≥3.11, shipsddtrace, no polars/pandas. Both packages runinit()at import (JsonFormatter + rustls ring) and preload libduckdb (ctypesRTLD_GLOBAL/add_dll_directory);build.rscopieslib/libduckdb/1.4.2intodata/platlib. Two sanctioned async bridges: blockingpy.allow_threads(|| pyo3_async_runtimes::tokio::get_runtime().block_on(…))+pyo3_async_runtimes::tokio::future_into_pyfor the*_asyncawaitable variants. Errors: singlePyMandoErrorviamap_*_errorfns (mando-lib/src/python/error.rs). Test posture: py-mando-simulation Rust side zero tests, 1 Python integration test; CI job “PyMando Simulation Linux Dev” (pytest without--nbval). - py-mando — crate overview (abi3-py38 stable ABI, algo params, DataFrame ops, algo runner HTTP server).
- BE-3482 pymando Branch Review - 2026-07-13 read-only review of
feature/BE-3482-pymando(13 commits, py-mando DD log/APM conformance): NEEDS REWORK BEFORE MR despite genuinely green gates (clippy clean; 366 passed / 0 failed / 35 ignored viartk proxy;fmt_utilproven feature-flag-clean by buildingmando_libwith and without--features python). Blockers: 13/13 scoped commit titles (banned per !322), 7 orphaned ContextVars intracing.py, byte-identical duplicates of the fingerprint branch’s version-fallback fix. Landing order: fingerprint MR first, then rebase dropping the duplicates; sharpest option also drops the fmt_util extraction for a zero-conflict 9-commit MR. Rework EXECUTED same day: rebuilt from scratch asfeature/BE-3482-pymando-v2(worktree.worktrees/BE-3482-pymando-v2), 15 title-only no-scope commits off develop tip51d2b516, zero cherry-pick conflicts, 10 files +374/-55;9b0c4e70kept,86d7ffd8/34e749d5/d0986230dropped (dropping the extraction cost zero extra code,level_to_statuswas still inline); all blockers/should-fixes done (build_ddtagspublic, bare-code fingerprint,caplog+ genuine global restore); gates green (clippy 0, 434/0/35 cargo tests, rustfmt 0 branch hunks, pytest 120 passed / 1 skipped vs baseline 114); NOT pushed, no MR; v1 untouched as fallback. UPDATE 2026-07-22: pushed + MERGED as MR !570 (squashfde2425e“feat: pymando logging conformance”, merge14dfb3ad); conformant wheelpy-mando==1.16.1+dev.2691658359.14dfb3adpublished to Nexus dev. - BE-3613 Algo Services py-mando Conformance - moving the algo services (bess-os-algo-optimization + bess-os-algo-forecast; forecast-as OUT of scope) onto the conformant wheel
py-mando==1.16.1+dev.2691658359.14dfb3ad: pin bump + fork replacement +log_erroradoption per repo; code COMPLETE 2026-07-22 on bothfeature/BE-3613branches (bess-optimizationbb0555e/d47212d/71fb146; bess-forecast-day-ahead50231b6/927e1e5/e1d5ade+ fork-test file deletions), UNPUSHED, blocked only on VPN/Nexus forpoetry lock --no-update && poetry install && poetry run pytest. - BE-3657 error_stack Adoption - phase 3 (planned 2026-07-22 evening, execution gated on !580) plans a shared
error_json_field_valuefn in mando-core unifying theerror.errors/error.detailsJSON parsing across mando-lib’sdd_formatterand BOTH python package log formatters (py-mando/src/log_formatter.rs,py-mando-simulation/src/log_formatter.rs); today only dd_formatter has that parse. UPDATE 2026-07-23: SHIPPED onfeature/BE-3657-phase3@e0184bc3(unmerged) - the shared hook unifies all three formatters; PyO3 also gainedmap_report_errorforward plumbing, its wiring grep-proven unreachable this phase; py-mando/py-mando-simulation pytest deferred to CI (no local venv, Nexus VPN-gated). - BE-4014 Datadog Error-Rendering Test Harness - carries a DIAGNOSED-not-fixed Windows py-mando CI red: 3
test_dd_conformance.pytests (test_log_error_uses_deepest_cause/_without_step_context_omits_path/_extra_does_not_override_error_fields) fail with IndexError on emptycaplog.records. Root cause = test-isolation leak, NOT a logic bug:log_erroris a purelogger.error,dictConfigdisable_existing_loggers=True+ thelogger_statefixture not restoring per-logger disabled/propagate makes caplog stop capturing after the first caplog test (first passes, next 3 fail = classic leak signature). Featurefde2425e(pymando logging conformance). Fix direction:disable_existing_loggers=Falseand/or reset the dd-conformance-error logger in theerror_logfixture. - pymando-logger-init-race-flaky-2026-08-10 - another
test_dd_conformance.pyisolation leak, this one from the Rust side:AlgoRunner::new(algo_runner.rs:~113)std::thread::spawns a permanent Axum server thread on CONSTRUCTION (no shutdown inside pytest), sotest_algo_runner.pyleaks two threads whosestart_serverre-runspy_mando.logger.init()with service=None, racing the non-thread-safeinit()and overwriting the module-globalidentity.test_init_populates_identity_and_dd_envthen assertsidentity["service"]and seesNone. Shipped mitigation: athreading.Lockaroundinit()(MR !601). Two globals (identity,_initialized) mutated by leaked background threads = the shared-state collision class. Same category as the caplog leak above and the env-var races in mando-known-flaky-tests-2026-07-15.
Testing
- mando-cli-e2e-harness-2026-08-04 - the data-level E2E harness (
mando e2e run/mando verify), CSV suites in the team’s test-data taxonomy. Also the canonical write-up of the self-confirming-test trap: unit tests that assert a hand-invented wire shape against the same invented struct prove only self-consistency; wire-format tests need ground truth from outside the code under test. - bess-os-ci-components — how the Python-side suites are actually run in CI:
python-testis a plain runner with NOservices:(bess-optimization setsDB_DISABLED=true) installing a prebuiltpy-mandowheel from Nexus, so no Rust compiles and the 54/68-case suites cost only 7.4/13.8 min. Reporting convention: pytest--junitxmlper group →junitparsermerge →artifacts: when: always+reports: junit:(always-upload is what makes failures readable in the MR widget). mando-cli’s e2e job takes this reporting shape but needs wiremock/postgresservices:from mando’s.integration-test-linuxinstead. - mando-e2e-rebase-2026-08-05 - two lessons about gates that lie. Feature-gated code is invisible to a bare
cargo test: theappmodule is feature-gated, so an upstream type rename left the new CSV route uncompilable while the suite stayed green — only--features appcaught it. And any test mutating a process-global env var is a latent cross-test race:MANDO_TEST_ENDPOINTSraced across parallel tests (fixed by passing the flag intotest_router+ aMutexaround set/read/clear), and the identical bug exists upstream onMANDO_DEBUG_MOCK_ERRORwhere CI’s--test-threads=1hides it entirely. - mando-cli-e2e-live-green-2026-08-05 - the self-confirming-test trap’s SECOND ACT, and the strongest available evidence for the rule. The 08-04 note predicted in writing that only a live run could catch drift in the hand-mirrored query schema; the mirror test
query_body_deserializes_as_the_service_would_read_itthen stayed green through a full upstream serde rewrite (DataPointIdstruct → bare string) while the live query 400’d. A hand-mirrored wire type has no mechanism to notice upstream moving — only a live run gates that seam. - mando-e2e-ci-green-2026-08-05 - the e2e suite moved from “green on my machine” to green in CI, proving
mando e2e run --external-stackagainst a CI-provided stack rather than a locally-orchestrated compose project. Shape worth reusing: prebuilt binary pulled cross-project + GitLabservices:sharing the job’s network namespace (so sidecars are onlocalhost:<port>) + the service-under-test as a background host process. Its one open improvement — junit reporting the wholetest_set_1as ONE case — is CLOSED: per-assertion cases shipped9c39a88and render as 8 named cases in pipeline2735668937(see below). - mando-cli-junit-per-assertion-2026-08-06 - how to name a CI testcase so its history survives: GitLab attaches a test’s past runs to
(classname, name), so a case label built from the observed result reads as one test disappearing and another appearing instead of a pass→fail transition. Labels must come from the expectation (step X is Successfrom the expected status;GET /path at least 1xfrom the expectation’smin), which is why the terminal label and the JUnit label are two separate fields on the sameLabel. Also the false-green-by-omission rule: a test that never ran must still emit a case, while an expectation merely absent from config must not. - mando-data-insert-wire-format-2026-08-06 - the build-vs-borrow rule for test seams: do not re-implement a wire format whose only specification is another crate’s private serde impl (polars’
Deserialize for DataFrame), because a dependency bump splits producer from reader with no test in either repo spanning both. Keeps the E2E harness on mando’s server-side CSV seed endpoint (14 tests, same repo as the polars pin) over a client-side Arrow insert. Open ~3-line improvement: replace the runtimeMANDO_TEST_ENDPOINTSgate with a cargo featuretest-endpointsso test routes are absent from prod binaries, not merely unreachable. - mando-known-flaky-tests-2026-07-15 - running catalogue of mando flakes; now three entries, all “no code defect, retry the job”: a wall-clock-boundary scheduler flake, the
debug_errorenv-var race betweengate_enabled_when_var_is_trueandgate_defaults_off_when_var_unset(~2/3 parallel failure rate, masked in CI by--test-threads=1), and the py-mando logger-init race in pymando-logger-init-race-flaky-2026-08-10. - pymando-logger-init-race-flaky-2026-08-10 - the deepest of the flakes: a leaked background thread (spawned by merely CONSTRUCTING
AlgoRunner) re-runs a non-thread-safelogger.init()and overwrites a module global thattest_dd_conformanceasserts on. Reusable lesson: a test that asserts on process-global state is a latent race whenever any live background thread can mutate that state — and aLocknarrowing the window is a mitigation, not a fix; the fix removes the shared-state seam (stop leaking the thread, or isolate the test in a fresh process / local monkeypatch). - mando-cli-flow-coverage-2026-08-06 - what “coverage” means for this suite: flows exercised over flows the service declares, computed against the live flow inventory rather than a hand-kept list, and deliberately chosen over
cargo-llvm-covline coverage (the interim job hit 40.36% green and was reverted). The metric immediately reframed the earlier 8/8 assertion green as 1 of 5 flows (20.0%) — assertion counts say nothing about behavioural breadth. Design detail worth copying: a coverage report must degrade to a warning, never fail a functional suite.
Tracing / Observability
- pymando-rust-log-trace-correlation-gap-2026-09-02 — Rust
tracingevents inside the Python algo containers carry nodd.trace_id/dd.span_id/logger.name/thread_name:py-mando/src/log_formatter.rsis a second, thinner formatter, anddd_formatter’sTraceInfo/lookup_trace_infoare private AND behind theappfeature py-mando never enables.TraceFiltercontextvars (tracing.py) also never reach Rust events. Reusingdd_formatter’s helper is RULED OUT (it needsOtelDataand py-mando installs no OTel layer); fix is B1 metadata parity + B2 bridging the ddtrace span context Python→Rust per-call. - clippy-disallowed-macros-allow-placement-2026-06-22 — mando bans
tracing::error!directly (clippy.tomldisallowed-macros) so all error logging routes through themando_core::error!wrapper; the wrapper suppresses the lint for its own expansion via a module-root#![allow(clippy::disallowed_macros)]inmando-core/src/error.rs(call-site allow is ineffective on clippy 1.95). - BE-3117 Per-Flow Error Context Store —
FLOW_ERROR_CONTEXTtask-local store created inside the spawned flow task (task-locals don’t crosstokio::spawn); drained after scope returns into ONEtracing::error!aggregate withflow.error.*flat facets + fullflow.errorsJSON. Hand-writtenSerializeredacts HTTP bodies whenhttp_context.sensitive. - BE-2272 — DD log JSON flattening to root.
- BE-1842 Datadog Observability — span-scope walking,
tokio::spawncontext-inheritance gotcha, dd_formatter field plumbing. - BE-3482 Datadog Logs and APM Conformance - Datadog logs/APM conformance state + ranked open deferrals: single-emission migration (biggest win, unlocks fingerprint +
http.*on at-site errors) >error.*/http.*on trace spans > verifydd.trace_idcorrelation in DD >flow.step.timeout_ms. The fingerprint ACTIVITY component (pushed 2026-07-13, open as MR !569; MERGED by 2026-07-15 as53912a70) makes DA vs AS schedule failures in the auction scheduling step group SEPARATELY in Datadog (previously ONE error group). Authoritative fingerprint standard: architecture-design repo,logs-and-apm.md(error.code + flow.step.path + specific activity). - BE-3482 pymando Branch Review - review outcome for the py-mando DD conformance branch: solid work (flat
flow.step.path, conformantlogging.conf,log_errorhelper with root-cause unwinding,set_log_tagsddtags bridge into the Rust formatter) but needs rework before MR. Sharpest conformance finding:error.fingerprintserializescode|Nonewhen no step context is set (tracing.py:109), polluting DD fingerprint aggregation forlog_errorcalls outside atrace()/run_with_tracescope. The same-day rework FIXED it (bare code, no"None", with a new test) and added a NEWflow.step.systemspan tag: the non-Mando counterparty taken straight fromStepMetadata.systeminmando-lib/src/workflow/mod.rs(the structured sourceflow.step.connectionis formatted from), one line, no string parsing; runtime JSON conformance proven from the Rust stream during pytest (120 passed / 1 skipped). - Dev Deploy 2026-07-14 (error telemetry) - first LIVE validation of the BE-3482 telemetry on dev, observed from the deploy’s own crash-loop logs:
error.code==error.kind==error.type(spec-verified: architecture-designlogs-and-apm.mdlines 87-104 explicitly mandate three fields with the same value for APM/logs/mobile),error.message= root cause,error.stack= source chain (wrapper-embeds-source Display pattern causes cosmetic text duplication),error.fingerprintabsent as expected for non-step errors. Follow-up idea: emit a bare-code fingerprint from the Rusterror!macro outside flow steps (parity with py-mandolog_error), needs one sentence added to the standard. - fr-region-missing-datadog-logs-2026-07-21 - why FR services show APM/metrics but zero logs in Datadog: container logs travel CloudWatch →
datadog-forwarderLambda via subscription filters (NOT the agent sidecar), and the-frlog groups have no subscription filters becausemodules/bess_os/cloudwatch.tfnever got thedd-logs-forwarderblocks. - BE-3657 error_stack Adoption - Datadog verification of the dev throwaway build
1.16.1-feat.2682437891.b7d15e2c(2026-07-16..22): nestederror.details(4-level chain, real file:line),error.errorsarray queries (element/wildcard/negation), nestedhttp.{request{method,url,version,body,content_length}, response{status_code,body,content_length}}, trace correlation confirmed (sample trace0dc8d42739a08ac0805808b2d14b6644); startup mock deep error fires once per task boot (still the typederror!arm, not the report arm). PENDING DD cutover AT BE-3657 MERGE: create facets@error.fingerprint/@error.errors/@error.details.code; inventory monitors keyed on short codes, flathttp.*, exact message “Failed running step” - all code/fingerprint values change format at that deploy. UPDATE 2026-07-23: !580 (phases 1+2) MERGED (merge4c3442df), so the cutover checklist becomes actionable with the first develop-based dev deploy carrying it; the mock is superseded on the phase 3 branch by an env-gated report-backed port (MANDO_DEBUG_MOCK_ERROR, default off, unmerged). UPDATE 2026-07-23 evening: the short-code decision (commita58df341, in review with MR !585) narrows that format break:error.codebecomes the SHORTEnum::Variantform (close to the legacy shape, so@error.code-keyed monitor impact shrinks), whileerror.kind/error.type/error.fingerprint/error.errors/error.detailscodes stay full module path and DO still change format at that deploy. NEW cutover item:@error.sourceis RETIRED (renamed toerror.messagein the redesign, pinned by a mando-core regression test) - delete or rekey any facet/monitor still on it to@error.message/@error.errors(an emptyerror.sourceconsumer already hit and fixed by Andras 2026-07-23). UPDATE 2026-07-24 (DX batch + live stream):error.details[].codealso goes SHORT (commit4e519269;errors/kind/type/fingerprintstay full path), so the@error.details.codefacet keeps a readable short form at cutover. Local LIVE stream now operational: server on 8081 + a CONTINUOUS forwarder shipping error/warning lines to Datadog EU within ~5s underenv:dev-local-levander(MANDO_ENVIRONMENT=dev-local-levanderset at source, no forward-time ddtags rewrite); complete field contract verified live (code short, errors full, details codes short per level, fingerprint full, Caused-by stack, nested http blocks on 5xx paths). GOTCHAS: facet queries with::in the value MUST be quoted (@error.code:"VolueEmsError::ApiError", unquoted silently matches nothing); a survived stale local server held port 8081 so a new launch died AddrInUse while old-format emissions kept flowing (diagnose with lsof on the port + the holder’s binary path). UPDATE 2026-07-27 (short codes REVERSED): the short-code decisions are undone (commit4df2562a, the new MR !585 head) -error.codeANDerror.details.codeare FULL module path again. This REVERSES the shrink/readable-short-form notes above: the@error.details.codefacet is full-path (no longer short), and the DD-cutover format break is back to FULL impact across@error.code/@error.kind/@error.type/@error.fingerprint/@error.errors/@error.details.code(nothing resembles the legacy short codes now);::-quoting rule still applies. Theshort_error_codehelper is deleted. - mando-cli-flow-run-2026-07-28 -
mando flow runemits machine-consumable conformance pointers at terminal for a downstream test suite:dd_query(@flow.exec_id:<id>, env-scoped from the rendered.infra/datadog.builtin.yamlDD_ENVwhen the local datadog overlay is active),dd_url(DD_SITE-aware deep link with epoch-ms bounds),started_at/stopped_at. Local builds ALWAYS emit dd_formatter JSON (init_loghardcodesdd_enabled=true). Trace-backend hint: local+datadog→Datadog, local-without→generic OTel (no Jaeger UI in repo), remote→Datadog. Local follow filters container logs by the rootflow.exec_idJSON key. - mando-cli-otel-capture-and-tail-2026-07-28 - every
mando upstack now captures OTel traces to disk via a bundledmando-otel-collector(raw OTLP JSON NDJSON, rotated) andup --datadogfans out to Datadog APM (agent OTLP receiver + collector otlphttp);mando tailreads those capture files + local logs with a gron-flatten + substring-AND filter (flow.exec_id = "<id>), verbatim/golden-friendly, local-only by design (remote = Datadog dd_query pointers).flow run --span-file/--bundleextract this run’s spans byflow.exec_idattribute. Callout bug:ensure_infracollector-config revert killed APM after anymando status— fixed via overlay-file-derived variant. - mando-cli-datadog-local-logs-2026-07-22 - Datadog host-tag join latency (2026-07-23 guide test):
env:dev-local-<user>ridesDD_ENVHOST metadata, and the host-tag join takes up to ~10 min for a brand-new agent host (~8 min observed), so freshly shipped logs are initially queryable byservice:/container_name:only and get the env tag retroactively; an env filter that looks broken right after agent start is just the join lag. Logs Search APIapi.datadoghq.eu/api/v2/logs/events/searchworks with the org API key + a Personal Settingsddapp_application key. - BE-4014 Datadog Error-Rendering Test Harness - unit-test harness for DD error rendering:
render_dd(|| error!(...))/render_dd_oneinmando_lib::app::dd_formatter::test_support(realerror!macro through realDatadogFormatter,--features app,test-util). Test-pins develop’s DD emission contract: report armerrors[]/details[]+ NO fingerprint (codekindtypeoutermost, messagedeepest); plain + http arms barefingerprint==code(the{code}|{step_path}|{activity}shape isStepResult::log’s); NO render-layer redaction ofhttp.*.body(caller-owned no-leak); noattributesonerror.detailson develop (branch-only). Develop-based, independent of the error-stack train.
Workflow / Flow Engine
- fatal-step-double-logging-ancestor-boundary-2026-09-02 — Fatal steps emit the SAME error at the leaf boundary and again at every ancestor wrapper node (
run_steplogs unconditionally atmod.rs:499;status_or_error()returnsErronly for Fatal,?re-surfaces it at the wrapper). Fingerprints diverge by step path so copies don’t aggregate. Not the BE-3541 axis; still live on developb5770766. Trigger was a DuckDB duplicate PK already fixed by MR !610 (dab6cba6) but absent from prod7c1ef44f. - External Service Outage Gate - bitmask design - gating flow/step execution on declared external-service outages collapses to ONE bitmask AND: each service is one bit (severity 1 = Warning never blocks, so only 0/2 matter),
required = fold s: acc | (R(s) << i(s))is a codegen-emitted per-flow const,blockers = required & outages,can_run = blockers == 0.R(s) = OR over declarations of (propagates_failure(step) AND NOT transient); the expectedminover thresholds degenerates to OR. GOTCHA:propagates_failuremust fold up the ANCESTOR chain (a hard step under afailure_status: Warningparent contributes nothing) or the guard is stricter than the runtime.transient: trueandfailure_status: Warningare identical AT THE GATE - one code path only. OR being idempotent+associative means dedup is a structuralWorkflowSteptree fold, no HashSet. Blocked run maps to HTTP 423 beside FlowDisabled/SystemDisabled. - be-1595-flow-repository-sqlite-bugs-2026-06-17 — Two latent
FlowRepositorySqlitebugs (Sqlite dev/test only):create_flowlazy-query()INSERT that never persists, andstarted_atcurrent_timestampTEXT default unreadable asi64inget_execution. - be-1595-flight-execution-id-parity-2026-06-17 — Execution-bound manual overrides resolved via
FlowRepository.execution_idlookup, now shared between REST and Flight paths (apply_execution_overrides);MandoFlightServicecarries anArc<FlowRepository>so the Flight handler can resolve execution overrides like REST. - BE-3117 Per-Flow Error Context Store — flow boundary in
workflow/flow.rswraps execution inFLOW_ERROR_CONTEXT.scope(...)+FlowCompletionGuard(Drop-flush for panic/cancel). Step-level recording inStepResult::logviaErrorRecord::from(&ErrorWithStepStatus), gated on!logged_at_site. Thelogged_at_siteflag is the successor tologged_at_failure_site. - flow-step-log-message-dropped-2026-05-26 — Step-failure logging contract:
ErrorWithStepStatus::log(status, message)is the “I haven’t logged yet, parent please log me” path (logged_at_failure_site: false), but the parent’sStepResult::log()dropsmessage. Documents the constructor/Display/status_or_error/top-level-catch chain and thec945514e/4c543cb1cross-check. - Mando AGENTS.md Master Guide — released flow versions are immutable: never change a released vN’s YAML or generated code; breaking behavior → v(N+1); exemplar manual_schedule v3. Definition YAML (
mando-bess/config/flows/fi/*.yaml) →build.rs→ generated Rust that is TRACKED in git (commit YAML + output together). Tip flows @ 2026-07-08: as_auction_update_v2, auction_v4, data_update_v2, intraday_v2, manual_schedule_v3. Jul-08 develop restructured src-side wiring from per-typesrc/flow/{type}/v{n}/dirs toflow_registry.rs, backed by new cratesmando-flow-step(FlowStepServicetrait: uniform async step construction from YAML) +mando-flow-step-derive(ParamEnum/ParamMetaderives). Round 2 (2026-07-13):FlowStepServiceis a construction trait, NOT execute()-style —type Params: ParamMeta; type Response;+ asyncfrom_config(config: &str, providers: &StepProviders);StepProviderscarries flow_repository/data_point_registry/6 auth providers/simulation_enabled; 36type_entry!registrations inmando-bess/build.rs; param structs derive#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ParamMeta)];config/flows/manifest.yamlis the deployment catalog (setup: {version}: flows: {name}: {path, schedule_env, semaphore_group}— scheduling env + concurrency groups in the manifest, not code; semaphores capacity 1/group); the flow-step crates ship with zero tests. - BE-3656 APM Span Enrichment -
SpanEnrichmentLayercopieserror.*/http.*off ERROR-level tracing events onto the active OTel span as Datadog APM tags; readsopentelemetry::Context::current().span()NOTtracing::Span::current()(empty insideLayer::on_eventunder scoped subscribers). MERGED via MR !578 2026-07-22 (squash46a9be1b, merge4a0d297f). Verified live on dev (3 commits,a0735ebbreview-hardening: bodies off spans, first-error-wins guard, no MR yet). Dev-deploy prototyped17227acvalidates the BE-3657 field shapes: full-patherror.code, startup mock deep error,error.errors/error.detailsas real JSON arrays (targeteddd_formatterparse),flow.step.systemfrom !570; DD array queries verified (element/wildcard/object-field/numeric/negation). Note: DD flattens array SPAN attrs tokey.0/key.1(breaks faceting) so arrays are log-side only, spans keep scalarerror.code. Three tooling landmines added: Bash cwd resets to main checkout between calls, RTK garbles grep/sed (Read to ground-truth),grep "test result" | tail -1cargo false green. - mando-cli-flow-run-2026-07-28 - the
mando flowCLI (list/run/status) triggers + follows a flow run. Wire model: kebab URL segments (auction/intraday/data-update/as-auction-update/manual-schedule), snake_caseflow_keybody wrapper, server pins the version. Terminal detection is sound because the server pre-creates ALL step rows as Queued and the returned trigger id the DB execution id every log line’s rootflow.exec_id; step digest maps Success/Warning/Skip/Error/Fatal/Canceled, exit 1 on Error/Fatal. - BE-3482 pymando Branch Review - the step
info_span!inmando-lib/src/workflow/mod.rsgained aflow.step.systemtag (2026-07-13 rework, commit52b4d270onfeature/BE-3482-pymando-v2, unpushed): the non-Mando counterparty taken directly fromStepMetadata.system(the same structured sourceconnection()formats), no string parsing; verified all 14DataFlow::Wrappersteps declaresystem: "Mando"and externals declare real names (Volue EMS x19, Metis x14, Position Manager x8); 4-case#[test_case]matrix covers Receive/Send/Wrapper semantics. Scope tension: one mando-lib commit on an otherwise py-mando-only branch, trivially splittable.