Mando AGENTS.md Master Guide

A master conventions document for AI agents lives at /Volumes/bandi/coding/poc/mando/AGENTS.md — deliberately untracked (working docs never get committed per house rule; the guide itself codifies this in §10). Synthesized 2026-07-10 from 5 parallel researcher agents; fully rebuilt 2026-07-13 by 5 re-run researchers against the true origin/develop tip 92bfe1c8 (2026-07-09, v1.16.0) on a dedicated worktree at .worktrees/develop. This note records what it covers, its provenance, the round-1 corrections to prior assumptions, and the round-2 new verified facts.

For Agents

Read AGENTS.md at the mando repo root before writing any mando code. Its own maintenance rule: when the document and the code on develop disagree, the code wins — then update the file. It is a local artifact; if missing, it was not deleted from git (it was never tracked).

What it covers (section map)

§Topic
1Orientation: crate map + dependency direction (never invert), feature-flag matrix
210 non-negotiables (generated code, error!-only logging, per-file rustfmt (all cargo fmt banned, see the 2026-07-13 field correction below), single-threaded tests, clippy gate, no unsafe/unwrap, title-only Conventional Commits, feature/BE-xxxx branches, pinned =x.y.z deps in #regions, comment-sparse code)
3Toolchain & build (1.89.0 pin, git LFS for lib/libduckdb/**, Nexus registries, MANDO_*/adapter env prefixes)
4Architecture: mando-lib layering (Repository → ServiceBase → dispatch → Adapter → Service → Workflow), flow engine + versioning, data model, refinery migrations
5Rust style (house rules on top of rustfmt.toml: imports top-only StdExternalCrate, blank line before returns, inline format! args, naming/derive/async/tracing conventions)
6The mando_core::error! system (signature pattern — see corrections below)
7mando-bess API conventions (utoipa routing, ValidatedJson + local JsonSchema trait, token_layer, fixed middleware order, audit events, cron jobs)
8py-mando PyO3 patterns (abi3-py38, async bridge, PyMandoError, pyo3-polars)
9Testing ceremonies (inline #[cfg(test)], httpmock, test-case/parameterized, test-util DataFrame fixtures, in-memory DB pools)
10Git/CI ceremonies (pre-push checklist, pipeline stages, version suffixes, MR etiquette)
11Landmines table (verified gotchas)
128 recipes: A adapter · B repository capability · C REST endpoint · D error variant · E flow version · F Rust→Python · G datapoint · H migration

The guide has since grown past the original 12 sections: section 13 “Working method - how to think before you type”, and section 14 “Deployment - getting a build onto an environment” (added 2026-07-13, verified from optimization-universe-iac + mando .gitlab-ci.yml). Section 14 covers the two-repo deploy ceremony (mando CI publishes images; optimization-universe-iac decides what runs where), the 4-env/2-group matrix, the dev deploy steps, the feature-branch publish trick, runtime config via TF_VAR_*, and post-deploy verification. Full vault record: Mando Deployment Ceremony.

Snapshot provenance

Current: the guide reflects the true develop tip 92bfe1c8 (2026-07-09, v1.16.0). Fully rebuilt 2026-07-13 by 5 re-run researcher agents working on a dedicated worktree at .worktrees/develop. The local develop branch was fast-forwarded and now tracks origin/develop — both the v1 “guide reflects stale merge-base” caveat and the “local develop is much staler” warning are resolved (still fetch + diff against origin/develop before trusting local state; that habit remains landmine #3 in the guide).

History: v1 (2026-07-10) reflected merge-base 8bbd407e (2026-07-01, v1.15.0); its documented drift — the 4 new crates (mando-flow-step, mando-flow-step-derive, mando-bess-lib, py-mando-simulation), the flow_registry.rs wiring restructure, the v1.16.0 bump — is now folded into the guide proper, including recipe E’s wiring steps.

Rebuild process notes (for the next re-run)

agentops:researcher subagents cannot SendMessage — collect their reports as scratchpad files, or extract them from transcripts at ~/.claude/projects/<project>/<session>.jsonl (take the longest assistant text). One of the five v2 researchers (style scope) died early; its scope was verified by hand instead.

Round-2 verified facts (2026-07-13)

All verified against tip 92bfe1c8 and merged into the guide.

Flow-step machinery (the Jul-08 restructure, now fully mapped)

  • FlowStepService (mando-flow-step/src/lib.rs) is a construction trait, NOT an execute()-style trait: type Params: ParamMeta; type Response; + async from_config(config: &str, providers: &StepProviders).
  • StepProviders (mando-flow-step/src/providers.rs) carries flow_repository, data_point_registry, 6 auth providers, simulation_enabled.
  • 36 type_entry! step registrations in mando-bess/build.rs.
  • ParamMeta param structs derive exactly: #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ParamMeta)].
  • config/flows/manifest.yaml is the deployment catalog: setup: {version}: flows: {name}: {path, schedule_env, semaphore_group} — scheduling env vars and concurrency groups are configured in the manifest, not in code. Semaphores: capacity 1 per group.

Error redesign PARTIALLY landed (commit ae6d1098)

Landed: error.message = root cause; error.stack = source chain; error.fingerprint = "{code}|{step_path}"; ErrorWithStepStatus.logged_at_site double-log guard with new()/logged() constructors. NOT landed: error.trace, .step_context(). docs/error-handling-redesign-plan.md and doc/errors.json are NOT tracked on develop. Direction: BE-3117 Per-Flow Error Context Store.

Provenance confirmed 2026-07-13: ae6d1098 is the squash of BE-3482 MR !558 (merged 2026-07-08, 30 files, +402/-173, merge commit c2b7d401; origin branch deleted). Continuation state, the unpushed fingerprint ACTIVITY extension (code|path|activity, branch feature/BE-3482-fingerprint-activity, now 2 commits: 6f541363 + the salvaged version-fallback panic fix bc3727ba, cherry-picked from orphan 478cd993), and the ranked open deferrals live in BE-3482 Datadog Logs and APM Conformance.

py-mando-simulation split (commit 3559042d) — BREAKING

py_mando.SimulationRunner is GONE from py-mando — moved to the new py-mando-simulation package (requires Python ≥3.11, ships ddtrace, no polars/pandas). Both packages: init() runs at import (JsonFormatter + rustls ring), libduckdb preload (ctypes RTLD_GLOBAL / add_dll_directory), build.rs copies lib/libduckdb/1.4.2 into data/platlib. Second sanctioned async bridge confirmed on tip — see correction #7 (amended).

rustfmt reality

All the import-grouping/comment options in rustfmt.toml are nightly-only and silently ignored on the stable 1.89 toolchain; the tip tree is NOT fmt-clean; CI has no fmt gate — import grouping is maintained by hand.

Test coverage + CI

  • New crates (mando-flow-step, mando-flow-step-derive, mando-bess-lib, py-mando-simulation Rust side) currently have ZERO tests; py-mando-simulation has 1 Python integration test.
  • CI gained a separate “PyMando Simulation Linux Dev” job (pytest without --nbval) and a simulator docker publish child pipeline.

Repo hygiene + style

  • No MR/issue templates, no CODEOWNERS in the repo.
  • format! style on tip: positional ~3:1 over inline; inline args are the review rule for new code (don’t mass-convert).

Recurring review feedback + dictated rules (2026-07-13)

Same day as the round-2 rebuild, the guide gained a “Recurring review feedback” section distilled from mining ALL 21 of Andras’s mando MRs (!322 through !558) via read-only glab API: 108 reviewer notes, nine recurring themes (unrelated-change hygiene, unused-code deletion, failures-are-errors policy, placement, completeness before review, green pipeline + no conflicts before assigning review, rustfmt on new files, Jira sync, idioms), plus a config-direction finding (NEW config uses config::Config over Envconfig; existing adapters keep envconfig). Full evidence + the theme list with source MRs: Mando MR Review Patterns.

Andras also dictated new rules into the guide in the same session:

  • Commits are title-only with NO scopes and no descriptions (the no-scope rule is reviewer-mandated, MR !322). Tightens the section-2 commit non-negotiable.
  • Commit every ~3 tasks, in short form.
  • Never use em dashes anywhere; the entire AGENTS.md was purged of them.
  • Always subagent-driven development, without asking.

Field correction (2026-07-13): cargo fmt NEVER scopes

Falsified during the BE-3482 continuation work: cargo fmt -- <file> does NOT scope to the named file (it touched api_docs.rs, which was never named on the command line). The round-1 advice “scope to touched files with cargo fmt -- path/to/file.rs” in correction #3 below is superseded.

Field landmine (2026-07-13): the RTK hook guts cargo test

Also found during the BE-3482 continuation: on RTK-hooked shells, the hook rewrites the canonical test command cargo test --all-features --release -- --test-threads=1 so that the threads flag becomes a test-name filter. Every test is filtered out and the run exits 0 in 0.00s (signature: 0 passed, N filtered out). Two gate runs were false-green this way before detection.

Field landmine (2026-07-22): the CI test gate is --lib-only; tests/ targets rot

Found running the BE-3657 gates: .gitlab/scripts/test.sh on develop runs cargo test --all-features --release --lib -- --test-threads=1. The --lib (added with the archiver integration split, commit 7278f36a) means CI compiles and runs ONLY lib unit tests; */tests/ integration targets are never built by the pipeline (the separate “Integration Test Linux Dev” job covers only the mando-lib archiver Postgres suite) and can rot silently.

  • Concrete rot: mando-bess/tests/flight_end_to_end.rs fails to compile on develop itself (E0603: imports DataPointUpdateInfo via mando_lib::service where it is only a private use; broken since the mando-repository crate split b3ce27a8). A full local cargo test without --lib hits it.
  • Local gates must use --lib for CI parity: rtk proxy cargo test --all-features --release --lib -- --test-threads=1.
  • Propagated to AGENTS.md in 4 places (section 2 non-negotiable 4, the section 10 pre-push command + its RTK warning, the section 13.5 gate list) plus a new landmine row; Mando AGENTS Guide (mirror) resynced.
  • The flight test fix is a pending one-liner (import from mando_core::model::datapoint directly), to be its own change on Andras’s yes, not part of BE-3657. Full record: mando-ci-lib-only-test-gate-2026-07-22.

Key corrections to prior assumptions (round 1, 2026-07-10)

1. Generated code is TRACKED in git

mando-bess/build/generated/**.rs (domain.rs + flows/*.rs) is generated by build.rs from mando-bess/config/**.yaml + mando-codegen/templates/*.askamabut it is committed. Rule: edit the YAML/template → cargo buildcommit input + regenerated output together. Never hand-edit. Same for *.out golden files in mando-codegen/test/domain/ (rustfmt ignores them). mando-codegen is a build-time generator, not a proc macro.

2. No ErrorCode derive, no mando-lib-macro crate on develop

Verified: git grep ErrorCode origin/develop → empty; no mando-lib-macro in the tree. Both exist only in .worktrees/ experiments (poc-error-extractor, BE-2023, and inside mando-flow-step-derive in the BE-1595/BE-3482 worktrees). Consequences:

  • error.kind (fully-qualified enum path), error.code (Enum::Variant), error.source are extracted at runtime by the mando_core::error! macro (mando-core/src/error.rs) from the value’s Debug repr — no derive needed.
  • The parent poc/CLAUDE.md is stale: it references an ErrorCode derive, a mando-lib-macro crate, and a mando/CLAUDE.md — none exist on develop.
  • The vault note mando-lib-macro is now marked status/outdated with a correction callout.
  • Naming collision trap: develop tip does have a crate named mando-flow-step-derive, but it derives ParamEnum/ParamMeta (flow-step params), not ErrorCode.
  • Never grep-and-copy from .worktrees/ as if it were develop (guide landmine).

3. CI gates: no fmt gate, clippy is the gate, single-threaded tests

  • No cargo fmt gate in CI. Never run bare cargo fmt (reformats ~64 legacy files). Scope to touched files: cargo fmt -- path/to/file.rs SUPERSEDED 2026-07-13: cargo fmt -- <file> does not scope either; use rustfmt --edition 2021 path/to/file.rs (see the field correction above).
  • Lint gate: cargo clippy --release --all-features clean. Workspace lints: correctness/perf/suspicious denied + cherry-picked denies (panic_in_result_fn, exit, print_stderr, undocumented_unsafe_blocks); unsafe_code = "deny".
  • Canonical test command: cargo test --all-features --release -- --test-threads=1 (.gitlab/scripts/test.sh). Single-threaded is mandatory — shared in-memory DB pools (DuckdbConnectionManager::memory()) corrupt under parallel tests. Never “fix” a flaky test by removing it.

4. doc/errors.json is an untracked generated artifact

Rustdoc-JSON–shaped error catalog; generator not in CI — likely from the poc-error-extractor worktree work. Regenerate rather than edit; do not commit unasked. Same for bench/, docs/, .ai/.

5. JWT signature validation is DISABLED

token_layer (mando-lib/src/app/route/token_layer.rs) parses the Bearer JWT with insecure_disable_signature_validation — the Extension<Option<User>> is audit metadata only, not authz. Known dev posture: do not build authorization on top of it and do not “fix” it without a ticket.

6. Flow versions are immutable once released

Never change a released vN’s YAML or generated code — breaking behavior goes to v(N+1). Canonical exemplar: manual_schedule v3. Verified flows on develop tip (mando-bess/config/flows/fi/): as_auction_update_v2, auction_v4, data_update_v2, intraday_v2, manual_schedule_v3. (Src-side wiring moved to flow_registry.rs on tip — see drift table.)

7. py-mando async bridges — two sanctioned patterns

py.allow_threads(|| {
    pyo3_async_runtimes::tokio::get_runtime().block_on(async { /* .await */ })
})

Amended 2026-07-13: a second sanctioned bridge is confirmed on tip — pyo3_async_runtimes::tokio::future_into_py for the *_async awaitable variants (alongside the blocking allow_threads + block_on pattern above).

Never hold the GIL across .await; never Python::with_gil inside an async block; clone Py<T> with clone_ref(py). Errors: single exception type PyMandoError; every boundary maps via the existing map_*_error fns in mando-lib/src/python/error.rs (add a new map_x_error there for new enums).

8. Toolchain 1.89.0, MSRV 1.88.0

rust-toolchain.toml pins 1.89.0 (bumped by the remotefs-smb to smb migration); rust-version = "1.88.0" is the MSRV. Parent docs saying “Rust 1.88.0” conflate the two. Workspace version on develop tip: 1.16.0 (as of 2026-07-08). Related: the CI build image base is still rust:1.88.0-bookworm and still installs samba libs, while the runtime image (debian:13.1-slim) dropped them — only ca-certificates, libc6, libssl3 + bundled libduckdb.so remain.