For: Apollo (UI/design agent — downstream)
From: Plutus (planning + research)
Date: 2026-05-04
Session: War Room Session 10 — TradingAgents baseline adoption
Sibling: apollo-design-brief.md (v1 — 5 pages already specced)
Depth: Hybrid — full data contracts + page intent + key components. Layout/visual decisions delegated to Apollo.
PRD for four new dashboard pages added to the existing Trading War Room dashboard surface, plus the n8n telemetry + human-approval gate infrastructure that backs them.
These pages exist because Plutus adopted TauricResearch/TradingAgents v0.2.4 as the baseline analyst/researcher pipeline (operator override 2026-05-04). Operator priority is "UI from day 1 that allows tracking of all operations." These pages deliver that.
This PRD covers:
/pipeline — Live TradingAgents run state per ticker/debate — Bull/bear/manager debate transcript per session/approvals — Human-approval gate (ADR-006) UX with required operator reasoning/agents — Full plutus fleet visibilityPlus the supporting layer:
BaseCallbackHandler → n8n webhook → SQLite + R2| In scope | Out of scope |
|---|---|
| Page intent + components for the 4 new pages | TradingAgents adoption mechanics (separate baseline-integration backlog) |
| Data contracts (R2 paths, JSON schemas, SQLite DDL) | Apollo handoff doc (drafted separately AFTER this PRD) |
| n8n workflow specs (intent + IO contracts) | n8n workflow JSON build (n8n-hub task) |
| Telemetry event schema | VPS provisioning of TradingAgents Python (Hades + Proteus task) |
| Operator decision log schema | Per-node LLM routing patch to llm_clients/factory.py (~1 day, separate Python task) |
| Acceptance criteria per page | Layout/CSS/component implementation (Apollo craft) |
| Build order | FinBERT deployment, VectorBT integration (PRD 1D) |
| # | Decision | Choice | Rationale |
|---|---|---|---|
| 1 | PRD depth | Hybrid (data contracts + intent + key components, layout to Apollo) | v1 brief over-specified visuals; bypass Apollo's craft. New PRD locks engineering surface, frees design surface. |
| 2 | New pages | /pipeline, /debate, /approvals (own page + topbar badge), /agents (full fleet) |
Operator priority: "track ALL operations from day 1" |
| 3 | /pipeline multi-instrument layout |
Grid summary (7 tickers) + detail panel for selected | Glance + drill-down in single view |
| 4 | /debate multi-instrument layout |
Per-ticker tabs, single-ticker view | Debate is per-ticker per-session by nature |
| 5 | Approval UX | Stage gate (expand thesis + risk panels) + required reasoning field (30-200 chars) | Forces eye-pass without slowing fast calls; reasoning becomes institutional memory |
| 6 | Reject path | Reason field required; reject-only (no modify-and-resubmit in v1) | Modify path adds round-trip complexity; defer |
| 7 | TradingAgents host | Hostinger VPS (alongside n8n) | Single deployment surface, n8n triggers via Execute Command, telemetry stays in-network |
| 8 | Telemetry transport | TradingAgents BaseCallbackHandler → HTTP POST to n8n local webhook → SQLite + R2 |
Async fire-and-forget, ~50-200ms per event, doesn't slow agent runtime |
| 9 | Refresh strategy | R2 polling — 3s on active pages, 5s on topbar approval badge, 30s on /agents |
Matches existing dashboard pattern; no WebSocket infra needed |
| 10 | Per-node LLM routing display | Surfaced read-only on /pipeline drill-down + /debate per-section badge |
Config-driven, exposed for transparency |
| 11 | Mobile bounds | 375px (matches v1 brief). /approvals especially critical |
Operator approves from phone |
| 12 | Build order | 1 = /approvals, 2 = /pipeline, 3 = /debate, 4 = /agents |
/approvals gates real money; ship first |
| 13 | Retention | SQLite hot 30d, R2 archive forever (Parquet) | Hot queryable + cold immutable archive |
| 14 | /agents scope |
Full fleet (4 trading-* repos + TA nodes + n8n workflows + Watchdog + CB Pipeline + system daemons) | "Track all operations" priority |
TradingAgents (VPS, Python 3.10+, v0.2.4 SHA-pinned fork)
│
├── PlutusTelemetryCallback (LangGraph BaseCallbackHandler)
│ POST localhost:5678/webhook/ta-telemetry → n8n
│
├── trading_memory.md (per-ticker reflection log)
│ n8n Local File Trigger watches → parses → SQLite + R2
│
└── PM output halt (ADR-006 gate)
Writes /tmp/.../pending-{order_id}.json
n8n Local File Trigger → SQLite order_log → R2 pending-orders/index.json
n8n (VPS) — webhook handlers + cron workflows
│
├── ta-telemetry-receiver (webhook) → SQLite pipeline_log + R2 pipeline/{session_id}/
├── ta-decision-log-mirror (file) → SQLite trade_log + R2 decisions/{ticker}/
├── ta-pending-order-receiver (file) → SQLite order_log + R2 pending-orders/
├── approval-receiver (webhook) ← dashboard POST → SQLite operator_decision_log + R2 → forward to Alpaca
├── alpaca-fill-watcher (cron 30s) → SQLite order_log update + R2 fills/
└── fleet-health-aggregator (cron 60s) → R2 fleet-health/index.json
R2 (Cloudflare) — read-only static fetch from dashboard
├── pipeline/{session_id}/{node}-{ts}.json
├── debate/{ticker}/{session_id}.json (full transcript per session)
├── pending-orders/index.json (active queue)
├── operator-decisions/{date}.json
├── agent-state/{agent_name}.json (per-agent latest)
├── fleet-health/index.json (aggregate /agents view)
└── archive/parquet/{table}/{date}.parquet (cold storage)
Dashboard (CF Pages, React) — polls R2
├── /pipeline ← R2 pipeline/* + agent-state, poll 3s
├── /debate ← R2 debate/{ticker}/{session_id}, no poll (final once written)
├── /approvals ← R2 pending-orders, poll 3s; topbar badge poll 5s
├── /agents ← R2 fleet-health/index.json, poll 30s
└── POST → n8n approval-receiver webhook (operator decisions only)
Why this shape: - TradingAgents Python on VPS keeps network short for telemetry + file watching - n8n owns all writes to SQLite + R2 (single mutation surface, audit-clean) - Dashboard reads R2 only (no Alpaca creds in browser, no SQLite access from frontend) - Operator decisions go through n8n → can't reach Alpaca without n8n audit row first (security win) - File-based agent comms (ADR-003) preserved at TradingAgents output boundary
Required infra: n8n must have read/write access to TradingAgents working directories (/tmp/trading-war-room/{session_id}/ and ~/.tradingagents/memory/). Since both run on the same VPS, this is a Docker volume mount of the host paths into the n8n container. Provisioning lands in the VPS-baseline backlog task (Hades + Proteus).
pipeline-event.json (NEW — _schemas/pipeline-event.json)Telemetry event emitted by TradingAgents PlutusTelemetryCallback. One row per node entry/exit, LLM call, tool call, or error.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["event_id", "session_id", "run_id", "ticker", "node", "phase", "ts"],
"properties": {
"event_id": { "type": "string", "format": "uuid" },
"session_id": { "type": "string", "description": "War room session id" },
"run_id": { "type": "string", "format": "uuid", "description": "Single TradingAgents.propagate() invocation" },
"ticker": { "type": "string", "description": "Instrument symbol, e.g. AAPL" },
"node": {
"type": "string",
"enum": [
"market_analyst", "social_analyst", "news_analyst", "fundamentals_analyst",
"bull_researcher", "bear_researcher", "research_manager",
"trader",
"aggressive_debator", "conservative_debator", "neutral_debator",
"portfolio_manager"
]
},
"phase": {
"type": "string",
"enum": ["chain_start", "chain_end", "llm_start", "llm_end", "tool_start", "tool_end", "error"]
},
"ts": { "type": "string", "format": "date-time" },
"provider": { "type": "string", "description": "openrouter|anthropic|openai|google|...; absent for non-LLM phases" },
"model": { "type": "string", "description": "x-ai/grok-4.1-fast etc." },
"tokens": {
"type": "object",
"properties": {
"prompt": { "type": "integer" },
"completion": { "type": "integer" }
}
},
"cost_usd": { "type": "number" },
"latency_ms": { "type": "integer" },
"tool_name": { "type": "string", "description": "Set when phase=tool_*" },
"tool_input": { "type": "string", "maxLength": 1024, "description": "Truncated to 1KB" },
"tool_output": { "type": "string", "maxLength": 1024, "description": "Truncated to 1KB" },
"payload_ref": { "type": "string", "description": "R2 path to full payload if truncated" },
"error_message": { "type": "string", "description": "Set when phase=error" }
}
}
operator-decision.json (NEW — _schemas/operator-decision.json)One row per human-approval-gate decision (approve or reject).
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["decision_id", "order_id", "ticker", "session_id", "decision", "operator_reasoning", "pm_output_snapshot", "ts"],
"properties": {
"decision_id": { "type": "string", "format": "uuid" },
"order_id": { "type": "string", "format": "uuid" },
"ticker": { "type": "string" },
"session_id": { "type": "string" },
"decision": { "type": "string", "enum": ["approve", "reject"] },
"operator_reasoning": { "type": "string", "minLength": 30, "maxLength": 200 },
"pm_output_snapshot": { "type": "object", "description": "Full PM output the operator saw at decision time" },
"ts": { "type": "string", "format": "date-time" }
}
}
pending-order.json (NEW — _schemas/pending-order.json)Written by TradingAgents PM-output halt wrapper.
{
"type": "object",
"required": ["order_id", "ticker", "session_id", "run_id", "pm_output", "created_ts"],
"properties": {
"order_id": { "type": "string", "format": "uuid" },
"ticker": { "type": "string" },
"session_id": { "type": "string" },
"run_id": { "type": "string" },
"pm_output": {
"type": "object",
"description": "Full portfolio_manager node output — proposed entry/stop/target/size + thesis + risk panel",
"required": ["entry", "stop", "target", "position_size", "thesis", "risk_panel"]
},
"debate_ref": { "type": "string", "description": "R2 path to /debate transcript for this session" },
"created_ts": { "type": "string", "format": "date-time" }
}
}
data/trading.db)-- Per-event telemetry stream
CREATE TABLE pipeline_log (
event_id TEXT PRIMARY KEY,
session_id TEXT NOT NULL,
run_id TEXT NOT NULL,
ticker TEXT NOT NULL,
node TEXT NOT NULL,
phase TEXT NOT NULL,
ts TIMESTAMP NOT NULL,
provider TEXT,
model TEXT,
prompt_tokens INTEGER,
completion_tokens INTEGER,
cost_usd REAL,
latency_ms INTEGER,
tool_name TEXT,
payload_ref TEXT,
error_message TEXT
);
CREATE INDEX idx_pipeline_run ON pipeline_log(run_id, ts);
CREATE INDEX idx_pipeline_ticker_ts ON pipeline_log(ticker, ts DESC);
CREATE INDEX idx_pipeline_session ON pipeline_log(session_id);
-- Order lifecycle (pending → approved/rejected → submitted → filled/cancelled)
CREATE TABLE order_log (
order_id TEXT PRIMARY KEY,
ticker TEXT NOT NULL,
session_id TEXT NOT NULL,
run_id TEXT NOT NULL,
pm_output JSON NOT NULL,
status TEXT NOT NULL CHECK(status IN ('pending','approved','rejected','submitted','filled','cancelled','error')),
alpaca_order_id TEXT,
created_ts TIMESTAMP NOT NULL,
updated_ts TIMESTAMP NOT NULL
);
CREATE INDEX idx_order_status ON order_log(status, updated_ts DESC);
-- Operator reasoning trail (audit + institutional memory)
CREATE TABLE operator_decision_log (
decision_id TEXT PRIMARY KEY,
order_id TEXT NOT NULL,
ticker TEXT NOT NULL,
session_id TEXT NOT NULL,
decision TEXT NOT NULL CHECK(decision IN ('approve','reject')),
operator_reasoning TEXT NOT NULL CHECK(length(operator_reasoning) >= 30 AND length(operator_reasoning) <= 200),
pm_output_snapshot JSON NOT NULL,
ts TIMESTAMP NOT NULL,
FOREIGN KEY(order_id) REFERENCES order_log(order_id)
);
CREATE INDEX idx_decision_ticker_ts ON operator_decision_log(ticker, ts DESC);
CREATE INDEX idx_decision_session ON operator_decision_log(session_id);
Retention policy. SQLite keeps 30d hot. n8n weekly cron archives older rows to R2: archive/parquet/{table}/{YYYY-MM}.parquet (one file per table per month), then DELETEs from SQLite WHERE ts < now-30d.
/approvals — Build Phase 1 (highest priority — gates real money)Intent. Operator-facing gate for ADR-006. Approve / reject pending orders with required reasoning. Topbar badge surfaces pending count from any page so operator cannot miss a queued approval.
Data contract.
- Reads R2: pending-orders/index.json — array of pending-order.json objects, sorted by created_ts ASC.
- Reads R2: operator-decisions/{date}.json for history view (last 30d).
- Writes via POST {N8N_PUBLIC_URL}/webhook/approval — payload matches operator-decision.json minus decision_id + ts (n8n assigns). Resolve N8N_PUBLIC_URL from fleet SSOT (fleet_services WHERE name = 'n8n-hub'). Per global rule 10-no-hardcoded-infra.md: do NOT hardcode the URL in dashboard config; inject at build time from CF Pages env var sourced from SSOT.
Components (key functional pieces — Apollo decides layout).
- Topbar approval badge (cross-page component): amber-pulsing dot + count when pending > 0; click → /approvals. Polls R2 every 5s.
- Pending queue — count + list of orders awaiting approval, sorted oldest-first.
- Per-order detail card — ticker, entry, stop, target, position size, PM thesis (collapsed by default), risk panel (collapsed by default), debate link to /debate?ticker=X&session=Y.
- Stage gate — Approve button DISABLED until both thesis and risk panels have been expanded once during this page session. Tooltip on hover: "Expand thesis + risk to enable approval."
- Reasoning field — required text input, 30-200 chars, live char counter. Approve button DISABLED until count ≥ 30. Field clears on submit.
- Approve / Reject buttons — green / red. Reject also requires reason field (same 30-200 char rule).
- History tab — past 30d of operator decisions. Shows ticker, decision, reasoning text, ts. Searchable by ticker.
Mobile (375px). Stage gate and reasoning field must be operable on phone. Approve button reachable without zooming.
Polling. 3s on the queue list. 5s on the topbar badge.
Edge cases.
- TradingAgents writes pending-order.json while operator already on page → new card appears at bottom of queue without page reload.
- Network failure on POST approval → show retry button, do NOT disable form, do NOT lose typed reasoning.
- Double-click guard on Approve / Reject (debounce 1s).
- Approve while operator already submitted on another tab → n8n returns 409, dashboard shows "already-decided" state.
/pipeline — Build Phase 2Intent. Live view of TradingAgents runs across all watchlist instruments. Operator sees what's running, what's stuck, what each LLM call costs, where errors happened.
Layout pattern: D — grid summary + detail panel. - Top: 7-instrument grid (one card per watchlist ticker). Each card shows ticker, current node (e.g. "bull_researcher"), status dot (green/amber/red), elapsed time, cumulative cost USD. - Bottom: detail panel for currently-selected ticker (single-ticker focus).
Detail panel components.
- Node timeline — analyst → researchers → trader → risk debators → portfolio_manager. Each node renders status (queued / running / done / errored), entry timestamp, exit timestamp, duration.
- Per-node telemetry — provider badge ("openrouter:grok-4.1-fast"), token counts (prompt + completion), cost USD, latency ms.
- Live LLM call log — latest 20 events for the selected ticker, newest first. Each row: timestamp, node, model, tokens, cost, latency.
- Tool call log — tool_name, input snippet (1KB), output snippet (1KB), latency.
- Run summary — total cost USD, total duration, errors (count + list).
- Cross-page links — "View debate →" → /debate?ticker=X&session=Y. "View approvals →" → /approvals?order=X (if PM output produced an order).
Cross-page component: cost ticker. Top-right of /pipeline shows running USD total for current war room session across all tickers.
Data contract.
- Reads R2: pipeline/{session_id}/{run_id}/{event_id}.json (one file per event) OR R2: pipeline/{session_id}/index.json (rollup).
- Operator should expect a rollup index for grid view + per-event reads only when drilled into a specific ticker.
- Reads R2: agent-state/tradingagents.json for overall pipeline daemon health.
Polling. 3s while page is active. Pause polling when document.visibilityState !== 'visible'.
Edge cases. - TradingAgents emits an error event → status dot red on grid card + error block highlighted in detail panel. - Empty state (no runs today) → all 7 cards show "idle" with last-run timestamp. - Multiple concurrent runs for same ticker → grid card shows latest; detail panel has run selector dropdown.
/debate — Build Phase 3Intent. Audit-grade view of bull/bear/manager debate transcripts per ticker per session. Read-only. Searchable history of every prior session's debate.
Layout pattern: A — per-ticker tabs, single-ticker view. - Top: 7 ticker tabs (matches watchlist). - Right of tabs: session selector dropdown (date + time of the session). - Body: 3-column layout for the selected (ticker, session) pair.
Components.
- Ticker tabs — click switches the displayed ticker.
- Session selector — dropdown, lists all sessions where this ticker had a debate. Default = most recent.
- 3-column transcript — bull thesis | bear thesis | research-manager synthesis. Each column scroll-independent.
- Per-round expandable — max_debate_rounds config drives how many rounds rendered. Each round collapsible.
- Risk debate section (below 3-column) — aggressive | conservative | neutral debators. Same 3-column shape.
- PM final synthesis — full text at bottom, includes proposed entry/stop/target/size.
- Per-section provider badge — small inline badge showing which LLM produced each section ("openrouter:grok-4.1-fast" for bull, "anthropic:claude-4.6" for bear, etc.). Surfaces per-node LLM routing decision visibly.
- Permalink — copy URL button → /debate?ticker=AAPL&session=2026-05-04T13:30:00Z.
Data contract.
- Reads R2: debate/{ticker}/{session_id}.json — single full transcript document per (ticker, session) pair.
- Schema: bull rounds[], bear rounds[], research_manager_synthesis, risk_debators (aggressive/conservative/neutral), portfolio_manager_output, metadata (per-section provider/model/tokens/cost).
- Written by ta-decision-log-mirror n8n workflow when PM output completes (one PUT per session per ticker).
Polling. None — debate is final once written. Page fetches once on load.
Edge cases.
- Mid-session ticker has no debate yet → empty state with "No debate yet — pipeline still running. View /pipeline →" link.
- Session selector empty for ticker with no historical debate → state "No prior debates for this ticker."
/agents — Build Phase 4Intent. Full plutus fleet visibility. One page where operator sees health of every operational component — Claude Code agents, TradingAgents pipeline, n8n workflows, monitoring daemons, infrastructure.
Sections (vertical stack — Apollo decides exact layout).
War Room Agents (4 trading-* Claude Code repos)
- For each: name (trading-operator, trading-analyst, trading-risk, trading-execution), last session timestamp, current state (idle / running / errored), error count last 7d, skill version drift flag (boolean — is repo's skill set in sync with master spec?).
TradingAgents Pipeline - Per-instrument: ticker, last run completion timestamp, total runs today, total cost today USD, error rate last 7d (% of runs that errored). - Aggregate: total cost today USD across all tickers.
n8n Workflows - For each (Watchdog, Market Data Pipeline, Trade Log Pipeline, Weekly Report, Grok Briefing, CB Pipeline, Signal Intake, ta-telemetry-receiver, ta-decision-log-mirror, ta-pending-order-receiver, approval-receiver, alpaca-fill-watcher, fleet-health-aggregator):
System Daemons - Argus (Pantheon governance), Hypnos (overnight reports), Talos (probe family). - Per daemon: last green check timestamp, alert count today.
Infrastructure - VPS reachable, R2 reachable, Cloudflare API reachable, Alpaca paper API reachable, OpenRouter reachable. - Each: yes/no + last check timestamp.
Data contract.
- Reads R2: fleet-health/index.json — single aggregate document.
- Written by fleet-health-aggregator n8n workflow on cron 60s.
- Aggregator queries: SQLite for trading agent state, n8n MCP for workflow status, daemon HTTP endpoints, Alpaca/R2/CF/OpenRouter ping endpoints.
Polling. 30s on /agents page.
Edge cases. - Aggregator failure (last write > 5 min ago) → top-of-page banner "Fleet aggregator stale — last update {timestamp}". - New agent / workflow added → aggregator picks it up automatically (config-driven).
| Workflow | Trigger | Action | Failure mode |
|---|---|---|---|
ta-telemetry-receiver |
Webhook POST /webhook/ta-telemetry |
Validate payload against pipeline-event.json. INSERT into pipeline_log. PUT to R2: pipeline/{session_id}/{run_id}/{event_id}.json. Update R2 rollup pipeline/{session_id}/index.json. Return 202 fire-and-forget. |
Drop event with structured error log; never block TradingAgents runtime. |
ta-decision-log-mirror |
Local File Trigger on ~/.tradingagents/memory/trading_memory.md change |
Parse new entries since last watermark. INSERT new rows into trade_log. PUT to R2: decisions/{ticker}/{date}.json (one file per ticker per day, append). When PM output completes, also PUT to R2: debate/{ticker}/{session_id}.json (full transcript snapshot). |
Skip malformed entry, log to error sink. Re-process on next file change. |
ta-pending-order-receiver |
Local File Trigger on /tmp/trading-war-room/{session_id}/pending-{order_id}.json create |
Validate against pending-order.json schema. INSERT into order_log status=pending. PUT to R2: pending-orders/index.json (rebuild full active queue). |
Reject malformed file, alert via Watchdog channel. |
approval-receiver |
Webhook POST /webhook/approval (from dashboard) |
Validate payload against operator-decision.json (minus assigned fields). INSERT into operator_decision_log. UPDATE order_log status. IF approve → call Alpaca place_order via Alpaca MCP / Alpaca API → record alpaca_order_id. PUT to R2: operator-decisions/{date}.json. PUT updated R2: pending-orders/index.json removing this order. Return 200 on success or 409 if already-decided. |
If Alpaca submit fails → status=error in order_log, write to R2, dashboard surfaces error. |
alpaca-fill-watcher |
Cron 30s | Poll Alpaca paper API for fills/cancellations on outstanding submitted orders. UPDATE order_log status. PUT to R2: fills/{order_id}.json. |
Skip transient API errors, retry next cron tick. |
fleet-health-aggregator |
Cron 60s | Query all sources (SQLite, n8n MCP, daemons, infra ping). Compose aggregate JSON per /agents data contract. PUT to R2: fleet-health/index.json. |
If any source fails, mark that section as unknown with error message; still write the doc. |
archive-cron |
Cron weekly Sunday 03:00 UTC | For each table with retention policy (pipeline_log, operator_decision_log, etc.), SELECT rows older than 30d → write to R2: archive/parquet/{table}/{YYYY-MM}.parquet → DELETE from SQLite. |
Halt + alert if Parquet write fails; do NOT delete without successful archive. |
Key constraint: all SQLite writes go through n8n. Dashboard never writes to SQLite directly. Order submissions to Alpaca never originate from dashboard — always via n8n approval-receiver. This preserves the audit invariant: every order has both an order_log row AND an operator_decision_log row before it reaches Alpaca.
| Component | Where used | Behavior |
|---|---|---|
| Topbar approval badge | All pages | Amber pulsing dot + count, visible when pending > 0. Click → /approvals. Polls R2 every 5s. Hidden when count = 0. |
| Per-node LLM badge | /pipeline, /debate |
Inline pill showing provider + model. Format: openrouter:grok-4.1-fast. Color-coded by provider family (optional, Apollo decides). |
| Cost ticker | /pipeline top-right |
Running USD total for current war room session. Updates on every telemetry event. |
| Status dot | /pipeline, /agents |
Green = healthy, amber = degraded/stale, red = errored. Same semantic as v1 brief. |
/approvals/pipeline/debatepipeline_log.provider for the corresponding node)./agentsfleet-health/index.json every 60s; staleness banner appears if last write > 5 min.PlutusTelemetryCallback adds < 200ms overhead per node transition.operator_decision_log BEFORE submitting to Alpaca (audit invariant).| Phase | Page | Depends on | Why this order |
|---|---|---|---|
| 1 | /approvals |
TradingAgents v0.2.4 baseline running on VPS, PM-output halt wrapper, n8n approval-receiver workflow, Alpaca paper credentials wired | Gates real money flow — highest stakes, ship first. |
| 2 | /pipeline |
TradingAgents PlutusTelemetryCallback integrated, n8n ta-telemetry-receiver workflow, R2 pipeline/* schema |
Visibility into runs — operator can see what's happening. |
| 3 | /debate |
n8n ta-decision-log-mirror writing R2 debate/{ticker}/{session_id}.json | Audit trail completeness — final view of reasoning. |
| 4 | /agents |
All other workflows operational + fleet-health-aggregator workflow | Fleet view depends on the underlying data being there. |
Parallel work that doesn't block this PRD:
- VPS provisioning of TradingAgents Python (Hades + Proteus task)
- Per-node LLM routing patch to llm_clients/factory.py (~1 day Python task — NOT required for v1; default config uses single provider)
- TradingAgents PM-output halt wrapper Python implementation
- PlutusTelemetryCallback Python implementation
llm_clients/factory.py/pipeline, /debate, /agents (only /approvals is mobile-critical in v1)When the Apollo handoff is drafted (next session, separate doc), these are the design-craft decisions to delegate:
/pipeline 7-ticker grid/approvals (animation? glow? muted-then-unmute?)/debate (collapse to single column at what breakpoint?)docs/trading-war-room/05_dashboard/apollo-design-brief.md (5 pages, 439 lines, design system locked)mockups/portfolio-overview.html, mockups/trade-journal.htmldocs/prd/trading-war-room-v1.mddocs/trading-war-room/ROADMAP.md.planning/STATE.md~/Library/CloudStorage/Dropbox/_DevSync/agent-handoff/2026-05-02_hermes-to-plutus_tradingagents-repo-intel.done.mdhttps://github.com/TauricResearch/TradingAgents (v0.2.4, Apache-2.0)constellation-hermes commit 68bf553 → research/tradingagents-investigation-2026-05-02/REPORT.mdStatus: PRD draft complete. Awaiting operator review before downstream Apollo handoff is composed.