Architecture
Cents is intentionally boring on the storage layer: SQLite, repository
pattern, no daemon. The interesting part is the agent orchestration and
the AgentResult contract that every research agent returns.
Data flow
Section titled “Data flow”CLI commands hit repositories, which talk to a single SQLite database. External data providers are called from the agents only — the persistence layer never reaches out to the network.
flowchart LR CLI[CLI commands<br/>thesis · research · scan · ...] Repo[Repositories<br/>ThesisRepository · EvidenceRepository<br/>PositionRepository · WatchlistRepository] DB[(SQLite<br/>~/.cents/data/cents.db)] Agents[Research agents] FMP[(FMP)] Alpaca[(Alpaca)] FRED[(FRED)] News[(NewsAPI)] Anthropic[(Anthropic)]
CLI --> Repo Repo <--> DB CLI --> Agents Agents --> FMP Agents --> Alpaca Agents --> FRED Agents --> News Agents --> Anthropic Agents --> RepoThe repository pattern accepts an optional conn so tests can inject an
in-memory SQLite connection — see tests/conftest.py for the fixtures.
Agent orchestration
Section titled “Agent orchestration”The orchestrator runs every child agent, collects their AgentResults, and
folds them into a single weighted synthesis.
flowchart TB Symbol[symbol + thesis] F[Fundamentals<br/>FMP] T[Technical<br/>Alpaca] M[Macro<br/>FRED] S[Sentiment<br/>NewsAPI + Anthropic] Mo[Moat<br/>FMP] I[Insider<br/>FMP] Orch[Orchestrator<br/>weighted aggregate] Result[AgentResult<br/>evidence · conviction_delta · dimension_scores]
Symbol --> F Symbol --> T Symbol --> M Symbol --> S Symbol --> Mo Symbol --> I
F -- AgentResult --> Orch T -- AgentResult --> Orch M -- AgentResult --> Orch S -- AgentResult --> Orch Mo -- AgentResult --> Orch I -- AgentResult --> Orch
Orch --> ResultThe orchestrator’s weighting combines two factors:
- Confidence weight — each evidence item carries a
confidencein[0, 1]; the agent’s mean evidence confidence scales its conviction delta. - Age decay — evidence weight decays linearly from
1.0toward a0.1floor over a per-dimension TTL (7 days for technical/sentiment, 30 days for macro/valuation/risk, 90 days for quality/moat).
A per-agent clamp of ±10 conviction points keeps any single agent from dominating the result.
The AgentResult contract
Section titled “The AgentResult contract”Every agent — including the orchestrator — returns the same dataclass. This is the surface the HTML export, the JSON serializer, and the CLI all consume.
classDiagram class AgentResult { +list~Evidence~ evidence +float conviction_delta +str summary +dict~str, float~ dimension_scores +dict metadata +__post_init__() } class Evidence { +str symbol +EvidenceType type +str description +float confidence +ThesisDimension dimension +datetime timestamp +str source } AgentResult "1" --> "*" Evidence : containsevidence— every supporting / contradicting / neutral observation the agent generated, including a numeric confidence and the dimension it speaks to.conviction_delta— clamped to ±10 in__post_init__.dimension_scores— per-dimension contributions (valuation, quality, moat, technical, risk, macro, sentiment).summary— human-readable string surfaced in CLI output.metadata— escape hatch for agent-specific extras (signal mode flags, raw provider responses for debugging).