Viska Capital · Internal Technical Report
Mímir is Viska's AI research analyst. A simple Slack surface sits on a production system: Mastra agent framework, Supabase vector store, a separate n8n ingestion pipeline, ~1,200 passing tests.
Mímir is positioned to become the single conversational interface to the entire Viska data estate — research corpus, the live $15.5M IBKR book, the 19-theme strategic grid, the quant conviction engine, and trading intelligence — from Slack and the web, for the whole team, with each person seeing only what they should.
Mastra (@mastra/core) is an open-source TypeScript framework for production AI agents. It provides the scaffolding we'd otherwise hand-build:
| Primitive | What it gives us | In Mímir |
|---|---|---|
| Agent | LLM + system prompt + tool set; runs the tool-calling loop | mimirAgent, gpt-5.2, ≤5 tool-calls/turn |
| Tools | Typed, schema-validated functions the LLM can invoke | 20 tools (Zod schemas) |
| Memory | Per-user working memory + per-thread history → Postgres | working (per Slack user) + thread (last 20) |
| Storage / Observability | Postgres-backed store + span tracing | shared PostgresStore, mastra_ai_spans |
| RequestContext | Per-request state through the tool chain, no globals | Slack handle, user, citations, face/JWT |
Model-agnostic. Mímir uses OpenAI today — gpt-5.2 (reasoning), gpt-4o-mini (lightweight synthesis), text-embedding-3-small (1536-dim embeddings) — but is not locked to it.
Runtime: Node 24, Fastify HTTP, Slack Bolt in Socket Mode (outbound WebSocket — no public inbound URL). Boots observability → advisory connectivity checks → routes → Slack. Every dependency degrades gracefully.
feature/* ──local──► develop ──auto──► mimir-staging (Dev Slack, #viska-agent-dev)
│ verify
main ◄── PR merge ─┘
│
auto-deploy (branch-tracked)
▼
mimir (PROD ← Viska Capital Slack)viska-mimir-agent · services mimir (prod←main) and mimir-staging (←develop).develop→main is the deploy. Local railway up to prod is forbidden.deploy:check.last30days shipped PR #45 (develop→main) → prod deploy 95440512-… SUCCESS @ a81dfa5987a3, 11:06Z.Slack events · the agent loop · real-time query embedding · vector search (hybrid_search_v3, RRF) · sector scoring · prefs · App Home · the SSE chat endpoint · error alerts.
Dropbox docs → chunk → embed → Supabase · the 6-stage daily report · error triage · a 15-min error-monitor cron that calls back into Mímir.
Live DB schema is owned by the ViskaDB repo's migrations. Embedding model + dimension are locked to match ingestion.
┌──────────────────────────── SURFACES ────────────────────────────┐
│ Slack (@mention, /mimir, App Home) viska.gg /chat (next) │
└──────────────┬────────────────────────────────────┬──────────────┘
│ Bolt Socket Mode │ HTTPS/SSE (CF Worker — deferred)
▼ ▼
┌──────────────────── MÍMIR (Mastra service · Railway) ────────────────────┐
│ Fastify + Slack Bolt │
│ mimirAgent (gpt-5.2, maxSteps 5) │
│ ├─ 20 tools (thin adapters → shared core) │
│ │ corpus search · live market/crypto · reports · prefs · Icelandic │
│ │ sector sentiment · web search · custom HTML report │
│ └─ MODULE SYSTEM (registry → manifest SSOT → 3 surfaces + self-explain)│
│ └─ last30days (BASE: reddit/hn/github/web · │
│ FINANCE: edgar/gdelt/polymarket/+dormant tier-2) │
│ Memory: working (per user) + thread RequestContext (per call) │
│ RAG: embed → hybrid_search_v3 (vec .7 + kw .3, RRF) → synthesise │
│ Face layer (built, not yet consumed): face-jwt · face-resolver │
└──────┬──────────────────────────┬───────────────────────────┬────────────┘
▼ ▼ ▼
┌──────────┐ ┌────────────────────────┐ ┌──────────────────┐
│ OpenAI │ │ Supabase (pgvector+PG) │ │ n8n pipeline │
│ LLM/embed│ │ embeddings · mimir_* │ │ ingest · reports │
└──────────┘ │ viska_tickers · memory │ │ error triage │
┌──────────┐ │ sector scores · errors │ └────────┬─────────┘
│ Binance/ │ └────────────────────────┘ Dropbox (docs) ◄┘
│ DeFi/ │
│ Perplex/ │
│ Miðeind │
└──────────┘Four layers: external data sources → durable state (Supabase + n8n ingest) → the Mímir runtime (Mastra + tools + modules + RAG) → user surfaces. The face layer is the seam that lets one agent safely serve many surfaces.
Capabilities used to be 20 flat, hand-registered, inconsistently-named tools. No concept of "a skill," no slash commands, no guide, no self-explanation. The module system (live on prod) replaced that with a rail.
A module is a folder src/modules/<id>/ declaring everything through one manifest (the single source of truth). A central registry wires that one manifest into three user surfaces plus a docs plane:
/mimir <skill> <args> (one umbrella command; Slack configured once, forever).explain_skill meta-tool reads the same help text.A module's prose lives in one place; the dashboard guide, /mimir help, and self-explain all read it. A conformance test fails the build on drift — docs can't go stale.
One broken module can't crash startup. Chat and slash both call a shared logic/ core, so surfaces never diverge.
last30days: "what was said about a topic in the last ~30 days, engagement-ranked, with sources." Fans out to many sources in parallel (one dead source never fails the call), normalises per-source, synthesises one cited brief. Finance mode adds SEC EDGAR, GDELT news, prediction-market odds, and auto-detects fund tickers. ~1,197 tests green, live on prod.Net for Viska: adding an analyst capability is now a bounded, repeatable task — write a module, get three surfaces + a guide + tests for free.
One Mímir, multiple surfaces. A face controls what data the agent may see and do — enforced in three layers of increasing trust.
| Artifact | Status |
|---|---|
| JWT minter, Slack-lane resolver, identity bridge | LIVE on prod |
Web chat endpoint (/api/chat SSE) | LIVE (PR #42) |
| Slack↔web memory carry-over | VERIFIED |
| Gap | What's missing | Owner |
|---|---|---|
| G2 | Request-scoped client factory. Today 17 places use the service key (RLS off). Goes first; gates everything. | ViskaMimir |
| G1 | The face JWT is minted but never consumed — tools still run on service key. The no-bleed gate. | ViskaMimir |
| G3 | Formalise three ingress lanes (slack / web / internal). | ViskaMimir |
| G4 | Tenancy schema + identity unification + RLS. | ViskaDB |
| G5 / G6 | Shard-namespaced memory IDs · purge 2 dead memory backends. | ViskaMimir |
Order: G2 → G1 → G4 (ViskaDB) → viska.gg edge-auth (ViskaFront) → G5 → G6. Feeds the larger per-user tenancy epic (board #7), gated on ViskaDB identity-unification (Slack text-IDs vs Supabase UUIDs) + OpSec JWT-revoke. Operator-review standby — not started unprompted.
Viska is a $15.5M macro fund (21 live positions — metals/mining, energy, grid/power, semis) run against a 19-theme × 16-quarter strategic grid (Gold 22.1% NAV, Copper 15%, Energy Infrastructure 14.6%, Aluminium 11.6%…). The edge is regime-aware theme rotation — so the scarcest resource is early, synthesised signal on theme shifts, not raw data volume.
Mímir is the interface that turns the fund's data estate into that signal, conversationally:
last30days (filings, news, prediction-market odds, fund-ticker detection).| Board | Governs | Headline |
|---|---|---|
| #7 Mimir Agent | runtime, migration, sharding, tenancy | #44 module system + last30days + face-chat DONE; #26 EPIC "One Agent, Many Faces"; per-user tenancy epic |
| #9 Council | agent-ensemble, quant engine | council substrate + face-agents in progress; engine v0 done |
| #10 Ops | OpSec / credential hardening | JWT-revoke + key hygiene — gates the tenancy RLS work |
| #8 Trading Intel | IBKR/Flex ingest + read path | #35 Mímir read-only trading tool todo |
| #4 Dashboard | viska.gg surfaces | #40 Mímir Assistant on viska.gg (proposed) |
Shipped (30d): migration to Viska org · sharding foundation S1/S3/S4/S5/S6/S8/S9 · module system M1–M5 · last30days finance-signal · prod ship today.
Active blockers: Phase-C cutover (drifted Phase-A apply) · sharding S2/S7 (needs operator relaunch + CI-gate call) · Tier-2 finance keys (dormant) · face/tenancy build (greenlight-gated).
last30days finance mode → attest. (Review Railway log level first — these APIs put keys in the URL.)robotization test to close out last30days verification.