4.2 KiB
ADR 015 — MongoDB Report Store, Run-ID Namespacing, and Reflexion Memory
Status: superseded by ADR 018 Date: 2026-03-24 Deciders: @aguzererler
Superseded: The
run_idnamespacing described here has been replaced byflow_id-based layout. The MongoDB vs local storage decision guide has been expanded in ADR 018, which is now the canonical reference.
Context
Three problems with the existing filesystem report store:
-
Same-day overwrites — Re-running
scan,pipeline, orautoon the same day silently overwrites earlier results because all reports land in the same flat directory (reports/daily/{date}/…). -
Read/write consistency — If we simply add a
run_idto filenames or paths, all existing code that reads from fixed paths (e.g.load_scan(date),load_analysis(date, ticker), the directory iteration inrun_portfolio) breaks. -
No learning from past decisions — Agent decisions are fire-and-forget. There is no mechanism for agents to reflect on the accuracy of previous calls and adjust accordingly.
Decisions
1. Run-ID Namespacing (Filesystem)
All path helpers in report_paths.py accept an optional run_id. When set:
reports/daily/{date}/runs/{run_id}/market/…
reports/daily/{date}/runs/{run_id}/{TICKER}/…
reports/daily/{date}/runs/{run_id}/portfolio/…
A latest.json pointer at the date level is updated on every write:
{"run_id": "abc12345", "updated_at": "2026-03-24T12:00:00Z"}
Load methods resolve through the pointer when no run_id is specified,
falling back to the legacy flat layout for backward compatibility.
2. MongoDB Report Store
MongoReportStore stores each report as a MongoDB document with run_id,
date, report_type, ticker, and portfolio_id as natural keys. Multiple
runs on the same day create separate documents — no overwrites by design.
Load methods return the most recent document (sorted by created_at DESC)
unless a specific run_id is requested.
3. Store Factory
create_report_store(run_id=…) returns:
MongoReportStorewhenTRADINGAGENTS_MONGO_URIis set (ormongo_uriparam)ReportStore(filesystem) otherwise
MongoDB failures fall back to filesystem with a warning log.
4. Reflexion Memory
ReflexionMemory stores decisions with rationale and later associates
outcomes. Backed by MongoDB when available, local JSON file otherwise.
Key methods:
record_decision(ticker, date, decision, rationale, confidence)record_outcome(ticker, decision_date, outcome)— feedback loopget_history(ticker, limit)— recent decisions for a tickerbuild_context(ticker, limit)— formatted string for agent prompts
Consequences & Constraints
MUST
- All report writes use a
run_id— engine methods generate one viagenerate_run_id()at the start of each run. - All report reads resolve through
latest.json— when norun_idis specified, the pointer file is consulted. - MongoDB is opt-in — requires setting
TRADINGAGENTS_MONGO_URI. Filesystem remains the default. - Factory failures degrade gracefully — if MongoDB is unreachable, the filesystem store is used.
MUST NOT
- Never hard-code
ReportStore()in engine run methods — always usecreate_report_store(run_id=…). - Never assume flat layout for reads — the directory iteration in
run_portfoliosearches bothruns/*/and the legacy flat layout.
Actionable Rules
- When writing a report, always use a store with
run_idset. - When reading a report (for skip-if-exists checks or loading data),
use a store without
run_id— it will resolve to the latest. - The
daily_digest.mdis always at the date level (shared across runs). pymongo >= 4.12is a required dependency (installed but optional at runtime — only loaded when MongoDB URI is configured).
Alternatives Considered
- S3/GCS object store — Rejected: adds cloud dependency for a local-first tool. MongoDB is self-hostable.
- SQLite for reports — Rejected: lacks the flexible document model needed for heterogeneous report types.
- Redis for report caching — Already in use for data caching, but not suitable for persistent document storage.