The Morning Run
One oracle artifact — analysts ground on it, the heat map renders it, the client receives it.
ANALYSTSHEAT MAPCLIENT SLACK
The fundstate oracle emits one payload per statement day, focused on performance
between IBKR statements — newest book vs previous book, plus live drift since the newest
book. All three consumers read the same stored database row. The heat map gets the
live, asset-level feed the removed heatmap surface has waited on since 06-28 — data-backed,
never the sketch. d7/d30 windows ride along from viska.fund-positioning/0.1 (shipped, 2f5aef9).
Payload skeleton
{
"schema": "viska.morning-run/0.1",
"status": "fresh | stalled", // stalled = book did not advance by deadline
"basis": "invested_capital (broker marks, IBKR sleeve); USD only",
"clocks": { "book_as_of", "prev_book", "fx_as_of", "quotes_as_of" },
"fx": { "rates", "as_of", "source" }, // ONE fx clock per document
"statement_window": { // the between-snapshots leg
"delta_usd", "trade_flow_usd", "market_contribution_usd",
"transitions": { "added": [], "dropped": [], "resized": [] }
},
"live_drift_usd", // null if any line unquoted — never 0, never stale
"heatmap": [ // exactly what a tile needs
{ "ticker", "theme", "weight_pct", // invested_capital basis
"d1_pct", // prev_book -> book, broker marks
"live_pct", // book -> live quotes
"unrealized_pct" } // vs cost basis — total the fund sits on
],
"windows": { "d7": {…}, "d30": {…} }, // from viska.fund-positioning/0.1
"totals": { "invested_capital_usd", "unrealized_usd" },
"gaps": [] // oracle gaps + stalled-clock entries, passed through
}
The chain — producer computes, N8N transports, the row is the truth
- Oracle (STRAT) grows one authenticated read route
/api/morning-run— a composition over legs the server already serves. All figure logic in the producer with a--check; the oracle stays a read-only publisher. - N8N triggers on statement arrival, not wall clock: poll
/api/latestforbook_as_ofadvance (observed at the ~14:00Z capture), deadline ~16:00Z. On deadline without advance the run fires anyway withstatus: stalled— a first-class state, never a silent absence. - DB (ViskaDB) owns
morning_run_payloads(PKreport_date) — the provendaily_analysis_payloadsposture verbatim:authenticatedSELECT, noanon, handle scan on the LOAD path, a row IS the grant. - Slack renders from the STORED row, not the API response — render-after-persist, so the client reads exactly what seats and agents query. English copy, every figure basis-labelled, no handles, no third-party prose.
Analysts ground on the same row: the day's dispatch packet cites report_date, and
orientation starts from a payload nobody hand-composed.
Why this is cheap
No new data source anywhere — every figure already exists in oracle snapshots. The producer is a composition (~1 day with checks; the d1/live/counterfactual math shipped this week). The DB table is a sibling of a proven pattern. The N8N leg is a poll, two HTTP calls and a Slack post on existing credential lanes. FE's heat map is the one genuinely new render, and it reads one table.
Schedule posture
Born HAND-RUN. Earns its timer per every-leg-earns-a-schedule-once-proven:
scrubbed scheduler env · idempotent re-run per report_date · exit codes distinguishing
fresh / stalled / blocked / failed. Terminal state SCHEDULED+ALERTING — a failed
morning run reaches a human, not a log.
Seat cut
| seat | owns |
|---|---|
| STRAT | payload contract, producer + --check, route |
| DB | DDL, grants, loader, handle scan |
| N8N | trigger, transport, Slack render, alerting |
| FE | heat map render from the row |
| viska-pm | contract registration (one step, one contract) |
Amendment 14b — embed the built heatmap (adopted)
AssetHeatmap.tsx is already live: treemap, tile area = share of invested capital,
colour = 24h market change or unrealized P&L, grouped by the four majors. The morning report
embeds this component — FE's slice shrinks to a data adapter.
Same component, two data modes: dashboard stays on live feeds; the morning report runs
payload-driven from the stored morning_run_payloads row, so the page
shows the frozen morning state analysts ground on and the client received.
Basis discipline: the payload's d1_pct is broker-mark book-to-book; the live view is
feed-clocked. The embedded views are therefore relabelled from the envelope —
"Statement · d1" and "P&L" — never an unlabelled mix of clocks
on one surface. Slack cannot mount React: the delivery carries compact figures plus a link to the
report page; PNG export is out of scope v0.1.