# apps2/robot — ARCHITECTURE MAP (v1 draft, for LLM review)

Companion to `PLAN.md` (read that first). Everything below was MEASURED live on 2026-08-29
against the running VPS (167.71.169.206, docroot `/var/www/bitcoin-fund-manager.com`), not
assumed. Canonical: `https://bitcoin-fund-manager.com/robot/ARCHITECTURE-MAP.md`.

---

## 1. DATA FLOW — the whole system on one screen

```
                     SINGLE-WRITER STORES (this app writes NONE of them)
  apps2/stocks/data/daily/*.json   split-adjusted daily store   (writer: stocks fetcher, 3×/day)
  apps2/stocks/data/hourly/*.json  hourly store                 (same writer)
  hourality 10-min tape store      10-min blocks, 80 names      (writer: hourality fetch.php)
  (timeportal.pro advice API        SEPARATE BUILD — owner 2026-08-29; C5 slot fail-open until it exists)

  UPSTREAM ENGINES (reused, never re-implemented)
  apps2/obv/engine.js       OBVX: rsi/obv/detect/aggBars — THE divergence engine
  apps2/shared/signals.js   A2.divergences / A2.rsi / A2.obv / A2.pctlRank
  apps2/dayality/data.php   10-slot week frame (log bp, holiday-flagged)      88 names
  apps2/hourality/data.php  per-ticker 10-min block board                     80 names
  apps2/obv/data.php        21-asset OHLCV lane, mtime cache + warm cron
  apps2/ostium/data/snapshot.json   universe.entries → the 71 Ostium tickers

                                      │
            ┌─────────────────────────┼──────────────────────────────┐
            ▼                         ▼                              ▼
   HTTP same-host GETs        node divscan.mjs                tp_advice() adapter, URL unset
   (dayality, hourality)      imports ../obv/engine.js +      until the separate timeportal
   C1 + C2                    ../shared/signals.js VERBATIM   build lands — C5 fail-open
            │                 C3 + C4 (confirmed+provisional)        │
            └─────────────────────────┼──────────────────────────────┘
                                      ▼
                    apps2/robot/lib.php  (join on ticker, coverage flags, cache)
                                      │
                    apps2/robot/data.php ──── ?machine=1 ────▶  THE BOT (hub spokes /
                                      │                         plan builder — later order)
                                      ▼
                    apps2/robot/index.html + app.js  (visual confirmation lane)
                                      │
                    hidden copyable ASCII panel (LLM read path, same computed objects)
```

Single-writer chains declared: `basics/stocks → dayality → robot`, `hourality-tape →
hourality → robot`, `basics → obv → robot(divscan)`, `(future) timeportal.pro advice API → robot`.
robot is a LEAF READER on every chain.

## 2. FILE LAYOUT (target)

```
apps2/robot/
  PLAN.md               this plan (mirror; canonical at /robot/PLAN.md)
  ARCHITECTURE-MAP.md   this file (mirror)
  HANDOFF.md            EXISTING hub-and-spoke handoff — kept, linked from the page
  USER-REQUESTS.md      verbatim orders + decisions (finalized from PLAN.md at build end)
  index.html            dashboard (replaces the current HANDOFF renderer)
  app.js                UI: board, detail canvases, ASCII panel
  engine.js             client-side reshape only (no rival math; divergences come precomputed)
  lib.php               joins, coverage, cache, advice read, machine block assembly
  data.php              endpoint (page shape + ?machine=1)
  divscan.mjs           node: OBVX/A2 verbatim import, confirmed+provisional lanes, JSON out
  fixtures.php          synthetic tapes incl. the planted ≤1-candle divergence
  tests.php / tests_node.js   suites — child-process data.php execution, both green before deploy
  data/                 cache dir (writable, gitignored like every sibling)
```

## 3. UPSTREAM CONTRACTS — measured shapes this app depends on

| endpoint | measured 2026-08-29 | fields robot uses |
|---|---|---|
| `dayality/data.php?machine=1` | `{ok,v:1,dow:{…}}`, 88 names, floor-version note on the wire | per-name 10-slot sums, n, n_hol/n_miss, window |
| `hourality/data.php?win=1y` | 1.0 MB, `board` = 80 rows: `tkr,n,overall,bestK,worstK,bestRun,worstRun,rz,…`; `slots` = 10-min grid incl. pre/post segments | per-name per-block sums + ranks; `no_tape` list |
| `obv/data.php?win=…&res=…` | `assets` = 21 keys: gold,btc,spx,oil,nas100,rty,djia,us30y,nvda,tsla,aapl,msft,amzn,googl,meta,qqq,semis,skhyus,ewy,skhykr,usdkrw | OHLCV per asset for divscan; res set 30m…1wk |
| `ostium snapshot.json` | `universe.entries` 329 rows; venue tags `O`/`O:*` ⇒ 71 Ostium tickers; `cls` ∈ rth-equity·23h-futures·24-7-crypto·24-5-fx | the universe + class + leverage |
| timeportal advice API | SEPARATE BUILD (owner 2026-08-29) — expected vocabulary: pause_all/longs/shorts + per-ticker (2026-08-27 hub order) | C5 via the `tp_advice()` adapter; fail-open + `cov:false` until built |

Ticker mapping C3/C4: obv keys ↔ Ostium tickers (`spx↔SPX, nas100↔NDX, gold↔XAU, oil↔CL,
btc↔BTC, nvda↔NVDA, …`); a literal map table in lib.php, tested. obv assets with no Ostium
listing (rty, us30y, qqq, semis, skhy*, usdkrw, ewy) are NOT board rows (universe is Ostium's).

## 4. FRESHNESS + LOOK-AHEAD POLICY (per condition)

| cond | page shows | machine serves | why |
|---|---|---|---|
| C1 dow | full-window stats, live-inclusive, labeled | **prior-session-complete stats only**, window stamped | bot reproducibility; trend-app `as_of: prior_close` precedent (PLAN Q4/ask 4) |
| C2 10-min | live session profile + historical block ranks | same prior-complete policy for ranks; `cur_block` reading marked live | same |
| C3/C4 div | both lanes drawn, PROVISIONAL badged, survival rate printed | `lane` field distinguishes; `age_bars`; fire time = endpoint + look_right | ≤1-candle order without look-ahead |
| C5 advice | "not built — separate build, fail-open (no pauses)" until the timeportal API exists; then its row verbatim | `cov:false` + defaults + `fail_open:true` until built | owner 2026-08-29 |

## 5. CACHING

- `data.php` caches the assembled payload per (win, res, machine) on disk under `data/`.
- Cache key = sha1 of the FULL SET of (path, mtime, size) over: every upstream file read,
  the upstream endpoints' `built_at` stamps, AND this app's own
  lib.php/divscan.mjs/data.php (trend defect #2: a max-of-mtimes key serves stale answers).
- divscan runs are cached per (asset, res, bars-hash); the warm path is obv's existing
  every-minute cron keeping obv/data.php hot — robot adds no cron in v1 (a warm script is a
  P4 option if measured build time exceeds the E3-style threshold).
- C5 is never cached beyond the request (one adapter call; a no-op while unbuilt).

## 6. FAILURE MODES — each degrades alone, stated on the wire

| failure | behavior |
|---|---|
| dayality/hourality HTTP error | that condition `cov:false, why:"upstream <code>"`, rest live |
| obv/data.php error or node absent | C3/C4 `cov:false`, board still renders C1/C2/C5 |
| timeportal API absent/erroring | C5 serves the fail-open defaults + `cov:false` + why (mirrors the spoke's own fail-open advice rule) |
| ticker missing upstream | per-cell `no tape` / `not in obv universe`, counted in fine print |
| stale cache suspicion | `built_at` + upstream stamps on every payload; `?nocache=1` honored |

## 7. SECURITY / COUPLING NOTES

- robot has NO hub coupling: it does not read hub.sqlite or call the spoke-auth'd api.php.
  C5 arrives only through the `tp_advice()` HTTP adapter once the separate timeportal build
  exists (owner 2026-08-29).
- The machine endpoint is public-read like every apps2 data.php (no secrets in the payload:
  biases, divergences, pause flags). If the owner later wants it gated, the hub API-key
  mechanism is the natural gate — out of scope v1.
- robot never holds wallets, keys, or order paths (hub-and-spoke law: only spokes touch Ostium).

## 8. WHAT THE BOT DOES WITH THIS (context, not scope)

The hub-and-spoke system (`/robot` hub, `trade.git` spokes) currently trades from
`ostium/data/snapshot.json` plans + owner advice. The weighing/sizing/placement algorithm is a
FUTURE BUILD (owner 2026-08-29, verbatim: *"algo for bet sizing and placement is future
build"*); when built it will consume `apps2/robot/data.php?machine=1` — five per-ticker
readings with coverage flags — to gate trade/no-trade and size. Nothing in this app precomputes
that weighting; it guarantees the five inputs are honest, fresh (≤1 candle for divergences),
versioned (floor-check), and joined on one ticker key.

## 9. KNOWN GAPS, STATED UP FRONT

- 27 of the 71 Ostium tickers (futures/FX/crypto) have NO house tape ⇒ C1–C4 `cov:false` for
  them in v1; C5 still applies. Getting them tape means a new fetcher = a new owner order.
- SP10 absent from the Ostium inventory (PLAN Q1).
- Provisional divergences are revisable by construction; the survival rate is printed so the
  reader (and the bot's algorithm) can discount them with a measured number.
- C2 pre/post-market blocks exist in hourality's frame; v1 ranks RTH + the overnight legs the
  upstream serves, mixing nothing (law 6) — each block states its own duration.
