← dashboard · flowchart · machine · audit · architecture
# Agentic-Loop Architecture Audit — C:\Trading
**Date:** 2026-07-03 (v2 — supersedes the 2026-06-10 audit; resolved findings from v1 are retained below with their fix evidence)
**Framework:** Sovereign Agentic Loops (SAL) — decouple AI reasoning from execution; model output is structured *intent*, validated by a control plane against true system state and policy before execution; maintain a linked evidence chain for replay, diagnosis, and learning.
**Supplementary references (fetched via TwitterAPI.io):**
- Lance Martin, *"Designing loops with Fable 5"* ([x.com/RLanceMartin/status/2064397389189071163](https://x.com/RLanceMartin/status/2064397389189071163)) — verifier sub-agents outperform self-critique because grading happens in an independent context; memory works as an outer loop when it completes the progression *fail → investigate → verify → distill → consult*.
- Addy Osmani, *"Loop Engineering"* ([x.com/addyosmani/status/2064127981161959567](https://x.com/addyosmani/status/2064127981161959567)) — five loop building blocks (automations, isolation, skills, connectors, sub-agents) + external memory; the maker must never grade its own work; "build the loop, but stay the engineer" — a loop running unattended is also a loop making mistakes unattended.
**Scope:** All of `C:\Trading` — base (Coinbase spot+futures, **LIVE**, ~$25K after June drawdown), stocks (IBKR paper), ika (halted via HALTED_BOTS), conductor (Charlie + Atlas), hermes (Herman), shared, dip-booster, docs/roadmaps.
**Method:** Three parallel verification sweeps (conductor/Atlas, base/stocks, shared/hermes) re-checking every v1 finding plus all code added since, with file:line citations. `DIP_LIVE=false` and cron gates verified against the running pm2 environment, not just source.
---
## A. Executive Summary
### What the system is today
A PM2-managed fleet on one Windows machine, eight processes: two trading desks (**base** = Coinbase spot + CFM futures with live money, capital-ceiling-clamped sizing; **stocks** = IBKR paper), an LLM strategist (**conductor/Charlie**: nightly Karpathy experiment loop at 01:00, live-outcome shadow evaluation at 03:00, 4-hourly reflection), a new **research organism (Atlas)** inside conductor (02:30 nightly: 6-source scout → LLM bridge → deterministic 5-stage gauntlet with a sealed one-shot vault), an infrastructure supervisor (**hermes/Herman**, 10 checks + bounded auto-remediation), a **derivatives aggregator** (OKX funding/OI/liquidations into partitioned Supabase tables), a **dip-booster** DCA sidecar (dry-run), and **cloudflared** exposing webhook + dash endpoints. Evidence lives in Supabase (`trade_journal`, `experiments`, `experiment_outcomes`, `strategy_params`, `dsl_strategies`) plus a Walrus append-only memory brain (`brain-v2`, `hypotheses` namespaces). Discord (3 channels, tag-on-critical) is the human interface.
### How close it is to a self-sustaining intelligent loop
**~85% of the SAL shape, up from ~70% on June 10.** The missing 30% identified in v1 — "Validate as a first-class layer" — has been substantially built, just not as a single control plane: parameter writes are now **rejected at write** against a canonical catalog (`conductor/src/supabase/writers.ts:41-54`) *and* **clamped at read** in all three read paths (`shared/src/strategy/param-store.ts:110,240,295`); promotions pass **Welch-t + Benjamini-Hochberg FDR (α=0.10) + a 3-per-night budget + live-shadow TTL renewal/revocation** (`conductor/src/experiments/statistics.ts`, `runner.ts:772-793`, `live-outcomes.ts:203-271`); and LLM-generated strategies face a **fully deterministic gauntlet** (min-sample → purged walk-forward → BH-FDR + PSR≥0.90 vs deflated Sharpe → 200-seed Monte Carlo → sealed out-of-sample vault with a spend-before-read ledger) that can award at most `candidate` status — no live execution path exists (`conductor/src/atlas/gauntlet.ts:308`). The v1 headline risk — *ungated LLM write authority over live trading* — is **closed**: a comprehensive sweep found zero ungated LLM→live paths.
What remains is the difference between *distributed validation* and a *control plane*: there is still no unified intent ledger (rejected proposals are only partially recorded), no human-approval step anywhere in the automatic promotion pipeline (bounded, but fully automatic), and one **new** reasoning→execution bypass introduced since v1 (the dip-booster, currently dry-run).
### Biggest architectural risks (current)
1. **The dip-booster bypasses the execution engine entirely** — direct `trader.createMarketOrder()` with no circuit breaker, balance guard, risk gates, or capital-ceiling awareness (`base/src/dca/dip-booster.ts:330`). Safe only because `DIP_LIVE=false` (verified in the running pm2 env). The day it flips live, the system regains the exact class of ungated execution path v1 was written to eliminate.
2. **Reconciliation is still advisory while ghost positions remain the documented chronic failure** — the system's only autonomous answer to state drift is still to die (phantom breaker fatal exit).
3. **Promotion is bounded-automatic with no human gate.** The statistical gates are now real, but a risk-increasing parameter change and a risk-reducing one follow the identical automatic path. The deferred PR#8 approval gate is the one v1 recommendation in this area not yet built.
### Biggest opportunity
**Atlas is the maker/checker exemplar the rest of the system should converge on.** It implements precisely what both referenced articles argue for: the LLM only *proposes* (bridge, economy tier), a deterministic verifier *judges* (gauntlet — an independent context by construction), memory is an outer loop (Walrus hypothesis ledger records every verdict including failures, closing Lance Martin's fail→investigate→verify→distill→consult progression), and the vault enforces one-shot out-of-sample evaluation so the checker can't be farmed. Generalizing this shape — proposer / deterministic validator / append-only evidence — into a thin `shared/src/control-plane/` for *trade* and *param* intents is now a smaller step than it was in June, because the validators already exist; they just need a shared ledger and a common intent envelope.
---
## B. System Diagram
```mermaid
flowchart TB
subgraph Observe
MD[Market data<br/>Coinbase WS/REST, IBKR,<br/>3-tier price fallback]
DA[derivatives-agg<br/>OKX funding/OI/liqs<br/>partitioned tables + autoroll]
HL[Hyperliquid liq levels<br/>Atlas Phase A source]
SCT[Atlas scout 02:30<br/>RSS/Reddit/Serper/X<br/>$0.15/night cap]
HSW[Hermes sweeps<br/>10 checks, pm2-daemon<br/>as precondition]
end
subgraph "Decide (base bot)"
SM[StrategyManager<br/>5 strategies + confluence]
RR[Risk/Reward sizing<br/>+ CAPITAL_CEILING_USD clamp<br/>loop.ts:2307]
SM --> RR
end
subgraph "Validate (distributed, new since v1)"
PC[param-catalog.ts<br/>46 gates + global bounds<br/>REJECT at write / CLAMP at read]
F2[F2 gates: Welch-t, BH-FDR a=0.10,<br/>3/night budget, holdout,<br/>shadow TTL renew/revoke 21d cap]
GNT[Atlas gauntlet: min-sample,<br/>purged walk-forward, PSR>=0.90,<br/>200-seed MC, SEALED VAULT one-shot]
end
subgraph "Execute (single gate + 1 bypass)"
EE["executionEngine.execute()<br/>engine.ts — breaker, BalanceGuard,<br/>slippage, SL retry-then-FLATTEN"]
DIP["dip-booster (DRY-RUN)<br/>direct createMarketOrder<br/>BYPASSES ENGINE — finding N1"]
CBX[(Coinbase spot+CFM)]
IBX[(IBKR paper :4002)]
EE --> CBX
DIP -.DIP_LIVE=false.-> CBX
end
subgraph "Record"
TJ[(trade_journal + experiments<br/>+ experiment_outcomes)]
SP[(strategy_params<br/>validated, TTL-bounded)]
DSL[(dsl_strategies<br/>hard-stops at 'candidate')]
WAL[(Walrus brain-v2 +<br/>hypotheses ledger, append-only)]
VLT[(vault-spends.json<br/>spend-before-read)]
end
subgraph "Reflect / Learn"
KL[Karpathy loop 01:00<br/>collect -> BH-FDR -> budget]
LO[live-outcomes 03:00<br/>renewOrRevokeShadows]
REFL[Reflection 4-hourly<br/>proposals validated, max 8]
ATL[Atlas 02:30<br/>scout -> bridge -> gauntlet]
XSB[x-scoreboard<br/>author credit ledger]
end
subgraph "Supervise / Human"
CHK[Herman: 10 checks,<br/>bounded auto-fix, 3-strike<br/>chronic-collapse escalation]
HUM[HALT.flag / HALTED_BOTS /<br/>Bearer /kill / Discord 3ch /<br/>IBKR weekly login]
end
MD --> SM
RR -->|TradeProposal| EE
SCT --> ATL
HL --> ATL
ATL -->|LLM proposes| GNT
GNT -->|deterministic verdict| DSL
GNT --> WAL
GNT --> VLT
KL --> F2 -->|survivors only| PC --> SP
SP -->|sync + clamp-at-read| SM
TJ --> KL
TJ --> LO
LO -->|renew/revoke TTL| SP
REFL -->|validated proposals| PC
HSW --> CHK --> HUM
EE --> TJ
```
The v1 red-flag edge (`evaluator → strategy_params → live sizing`, fully automatic and unvalidated) now passes through two validation stages (F2 gates, catalog reject/clamp) and carries a revocation loop (live-shadow deterioration → revoke). The new dotted red edge is `dip-booster → Coinbase`, dormant behind `DIP_LIVE=false`.
---
## C. Current Loop Inventory
| # | Loop | Maturity (Jun 10 → now) | Inputs → Decision → Action | Validation | Key gaps |
|---|------|------------------------|---------------------------|------------|----------|
| 1 | **Market data** | Solid → Solid+ | Coinbase/IBKR + fallbacks → 30s polls → cache | Staleness flags; hermes `derivatives-freshness` (30-min, partition-gap detection) | Liquidations table not covered by the freshness check; no per-feed quality score in sizing |
| 2 | **Signal generation** | Solid | OHLCV + indicators → 5 strategies + confluence → `TradeProposal` | Confidence gate, confluence sizing, min-size floor-up (`manager.ts:699-717`) | Dedup/cooldown rejections still unrecorded (N3) |
| 3 | **Strategy params** | **Dangerous → Guarded-automatic** | Nightly hypotheses → holdout + Welch-t → BH-FDR α=0.10 → 3/night budget → validated write (3-day TTL) → live-shadow renew (21d cap) or revoke on deterioration | Reject-at-write (`writers.ts:41-54`), clamp-at-read (`param-store.ts:110,240,295`), reflection double-validates (`reflection.ts:232-264`) | Still zero human involvement; risk-increasing and risk-reducing changes treated identically (N5) |
| 4 | **Risk management** | Good but inline | Sizing multipliers, daily-loss/drawdown blocks, breaker (3 losses→10min), margin pause, capital ceiling | Enforced inline in engine/orchestrator; catalog bounds for params | Constants hardcoded, policy unversioned (N9); risk decisions not first-class events |
| 5 | **Trade execution** | Good → Good+ | Proposal → dedup/cooldowns → `execute()` → Coinbase | Whitelist, BalanceGuard, idempotency, slippage auto-exit, **SL retry×2 → flatten → fatal-if-flatten-fails** (`engine.ts:815-858`) | No resting TP on futures — trailing stop needs the bot alive (N6); dip-booster bypass (N1) |
| 6 | **Post-trade evaluation** | Strong → Stronger | trade_journal + experiment tags → 03:00 aggregation → `renewOrRevokeShadows()` (min 10 live trades, Sharpe≤0 revokes) | UNIQUE constraints, TTL-only mutation (`live-outcomes.ts:109`) | Slippage/fill quality still not fed back into sizing |
| 7 | **Portfolio exposure** | Missing → Missing | Per-bot only | Per-trade, per-token, ceiling-clamped | No cross-bot aggregate (N4); ghost-position drift from manual fund moves still unreconciled (N2) |
| 8 | **Error recovery** | Mature | Herman 10 checks → single pm2 snapshot/sweep, 3-consecutive-failure promotion → bounded auto-fix or escalate | Restart cooldowns, crash-loop breaker, port-aware orphan kills, fingerprint dedup, chronic-collapse 3-strike | Remediation outcomes still mined manually per incident |
| 9 | **Research discovery (NEW — Atlas)** | — → Strong | Scout (6 sources, cost-capped) → LLM bridge (economy tier, strict route enum) → deterministic gauntlet → `candidate` or killed; every verdict to Walrus ledger + #research-lab digest | Entirely deterministic post-bridge; sealed vault one-shot OOS; LLM never scores | Candidates have no consumer yet (Phase D paper-shadow + Discord confirm unbuilt — by design, but the loop dead-ends) |
| 10 | **Model/agent reflection** | Bounded on one side → Bounded on both | Charlie reflection 4-hourly (memory + 7d stats) → max 8 validated proposals; hermes-side Charlie remains annotate/request-only (`charlie-context.ts`) | Proposal validation + write validation | Prompts/responses still unlogged (N7); reflection and Atlas don't share the Walrus brain yet |
| 11 | **Roadmap/planning** | Docs drift → Docs drift (worse) | ARCHITECTURE.md, ROADMAP.md, wiki | None | ARCHITECTURE.md still lists IKA as active 3-desk live; no Atlas anywhere; flowchart site was dead for ~10 weeks (N10) |
---
## D. Reasoning vs Execution Separation — Audit
### Closed since v1 (verified)
- **R1 (v1's central finding) — CLOSED.** The param promotion path now has: rejection-at-write against the canonical shared catalog with deduped #ops-room alerts on block (`conductor/src/supabase/writers.ts:32-92`); clamp-at-read in all three param-store read paths as defense-in-depth (`shared/src/strategy/param-store.ts:110-113,240-244,295-298`); the F2 statistical stack replacing the Sharpe-Δ>0.01 theater (backtest of the promoter itself: 56 promotions under the old gate → 2 under the new, ~96% cut); and a live-shadow lifecycle where TTL is the only mutable field and deterioration revokes. A comprehensive sweep of every write site (`writeParamAdjustment`, `updateDSLStrategyStatus`, `updateCompositeStatus`, evolution, digest, correlation, Sunday review) found **no ungated LLM→live path**.
- **R2 — CLOSED.** `promoteCandidatesToStaging` / `autoPromoteStaging` / `killUnderperformers` were deleted 2026-06-19 (`dsl-lifecycle.ts:25-40` documents why). The DSL pipeline now hard-stops at `candidate` via the Atlas gauntlet; `active` is unreachable by any automatic path and no live bot has a DSL executor.
- **R3 — CLOSED.** `/api/params` is Bearer-only with the full webhook secret; `?secret=` query auth removed; boot refuses to start when `LIVE_TRADING=true` and the secret is <16 chars (`base/src/webhook/server.ts:79-85,559-568`). The dashboard password now grants read-only GET access only.
### Where reasoning can still reach execution (current findings)
**N1 — the dip-booster is an engine bypass (HIGH, dormant).** `base/src/dca/dip-booster.ts:330` calls `trader.createMarketOrder()` directly when `DIP_LIVE=true` — no circuit breaker, no BalanceGuard, no risk gates, no capital-ceiling awareness, no idempotency key. It has its own sensible internal limits (3-tier dip structure, per-tier cooldowns 12/24/48h, ~$5K per-run cap, $5 min, own state file) but they are *its own*, unshared and unversioned — exactly the duplicated-risk-check pattern SAL flags. Verified dormant: `DIP_LIVE=false` in the running pm2 env. **Fix before any live flip:** route orders through `executionEngine.execute()` (it already supports market orders and would give the breaker, guard, and journal for free), or at minimum wrap the direct call in the shared catalog's global bounds + capital ceiling and journal every order.
**N2 — reconciliation still advisory (HIGH, carried from F5).** `base/src/execution/reconciler.ts:300-325`: adoption code exists (:336-447) but is deliberately never called; everything alerts, nothing corrects. Manual fund moves → ghost positions → phantom breaker fatal exit remains the de-facto remediation. The v1 fix stands: auto-adopt/evict after the same discrepancy on 2 consecutive checks under a size ceiling, logged as first-class events, fatal halt as backstop.
**N3 — intent ledger still missing; rejection recording partial (MEDIUM, carried from F6).** Progress: killed/paused-venue and research-only rejections now persist via `recordRejection()` (`base/src/orchestrator/loop.ts:1587-1609`), and the observability digest detects "all-rejections-one-reason" anomalies. But dedup, cooldown, balance, and breaker rejections are still `log.debug()` only (:1548-1551), `shared/src/control-plane/` does not exist, and there are no `intents`/`intent_decisions` tables. The May 5 lesson (a 9-hour silent no-trade outage invisible because rejections weren't recorded) is only half-addressed.
### Stale-state and config risks
- BalanceGuard 2-minute snapshot age: still enforced (good). Param staleness: `getAgentOverrides()` now carries a 5s timeout + 5-min cache TTL (`param-store.ts:198`), bounding the May-22-class stale-sync failure.
- Config safety: boot-time secret assertion under LIVE_TRADING now exists (v1 gap closed). Remaining unasserted combos: `DIP_LIVE=true` with no engine routing (N1); `FUTURES_ENABLED` with unfunded CFM wallet.
- Base cooldowns are in-memory only — a restart clears live-stop cooldowns (`loop.ts:1544-1556`), so a crash-loop could re-enter positions it was cooling down on. Stocks already persists cooldowns (`stocks/src/orchestrator/loop.ts:195-219`); port the same durable pattern to base (N8).
---
## E. SAL-Inspired Target Architecture
The v1 target stands; this section records what's built vs remaining. The system now has three *validators* but still no *control plane* — validation logic exists at each site, but there is no shared intent envelope, no unified decision ledger, and no human-approval lane.
| SAL element | v1 status | Now | Remaining |
|---|---|---|---|
| Structured intent (trade) | `TradeProposal`, unrecorded on rejection | Same + partial rejection recording | Full intent envelope + ledger row per proposal (N3) |
| Structured intent (param) | Raw `writeParamAdjustment` | Validated write, TTL, provenance, budget — intent-shaped in all but name | Formal `ParamChangeIntent` + `risk_direction` field driving approval lane (N5) |
| Structured intent (strategy) | Dead 48h auto-promote | Gauntlet verdict object → `candidate` hard stop | Phase D: `StrategyPromotionIntent` with mandatory Discord approval into paper-shadow, then live (N11) |
| Deterministic validate() | Absent | param-catalog (reject+clamp), F2 stack, gauntlet — all pure/deterministic, all unit-tested | Extract into `shared/src/control-plane/` with policy versioning so decisions are replayable byte-for-byte |
| Evidence chain | Outcomes strong, intents absent | + Walrus hypothesis ledger (append-only, includes failures), vault spend ledger, rejection partial | intents/intent_decisions tables; LLM transcripts (N7); params-hash on trades |
| Human approval lane | None | None (bounded-automatic everywhere) | Discord button approval for risk-increasing param changes + all strategy activations |
| Kill/brake | HALT.flag, HALTED_BOTS, /kill | Same, hardened (Bearer, boot asserts) | — (never gate the brake; already correct) |
### Automatic vs human-approved (unchanged policy from v1, updated status)
| Action | Disposition | Status |
|---|---|---|
| Trade intents within policy | Automatic | ✅ live |
| Risk-reducing param changes, TTL-bounded | Automatic | ✅ live (ecosystem-health-monitor 0.5×/24h pattern) |
| Risk-increasing param changes within catalog range + F2 evidence | Automatic **with shadow-first** | ⚠️ automatic but shadow = TTL-revocation only, no paper-first, no approval |
| Param change outside range | Reject | ✅ live (reject-at-write + alert) |
| Strategy status → active / new live strategy | Human approval, always | ⚠️ currently *impossible* rather than *approved* — safe, but the loop dead-ends until Phase D builds the approval lane |
| FUTURES_ENABLED, leverage, capital moves, DIP_LIVE | Human approval, always | ⚠️ human-controlled via env, but nothing asserts N1 routing before DIP_LIVE works |
| Kill / halt / risk-off | Automatic always | ✅ live |
---
## F. Prioritized Findings
### Addendum — 2026-07-05 review of the Phase D shipment
Atlas Phase D (paper-shadow + Discord operator gate, conductor `fe82127`) shipped hours after this audit and **resolves N11**: candidates now have a consumer — a nightly deterministic walk-forward re-run (03:45) with pre-registered verdicts and a human button gate into a label-only `staging` status. The isolation contract (no execution/treasury imports, only DB write is `dsl_strategies.status`) is enforced by build-failing static tests (`shadow.test.ts:41-65`) — verified, plus two clean nightly runs (Jul 4/5) and 56/56 tests. It also implements the "human approval for strategy promotion" row of the target table above.
**Finding D1 (HIGH → FIXED 2026-07-05, conductor `91bb894`): the shadow's candidate query was capped at 100 rows while 568 candidates existed.** `getDSLStrategies()` used `.limit(100)` ordered `created_at desc`; the shadow book covered only the newest 100 — 468 legacy weak-gate-era candidates were invisible, and new candidates pushed enrolled ones out of the window mid-shadow, freezing their records in `atlas-shadow.json` unresolved. *Fixed:* paginated complete-or-empty fetch (any page error returns `[]`, and an empty fetch skips the whole cycle so a query failure can never prune the book); departed strategies are pruned with a log line; TTL is now **absolute** (45d ends the audition even for inconclusive ≥5-trade strategies); pending proposals **re-ping the operator every 7 days**. Suite 57/57; isolation contract untouched; deployed via pm2 restart, crons re-verified. *Remaining operator decision:* the 468 pre-gauntlet legacy candidates — bulk-kill them (they never passed a real gate), or do nothing and let the shadow audition all of them (each resolves by demote/propose/45d-TTL automatically, at the cost of ~5× nightly shadow compute and a noisier book).
Also noted 2026-07-05: stocks' stop-cooldown persistence was sitting uncommitted — now committed and pushed (`stocks 95de7d5`). **Correction to this audit:** base already had full cooldown persistence (paper + live maps, TTL-pruned restore at boot — `base/src/orchestrator/loop.ts:310-349,426`, committed `e8dfcdf`, verified live via `data/cooldown-state.json` freshness). The earlier sweep claim that base lacked it was wrong. **N8 is fully closed on both bots.**
### Resolved since v1 (verified this audit)
| v1 | Finding | Resolution evidence |
|---|---|---|
| F1 (Critical) | Ungated LLM param promotion | Reject-at-write `writers.ts:41-54`; clamp-at-read `param-store.ts:110,240,295`; canonical `shared/src/strategy/param-catalog.ts` (46 gates + global bounds); reflection double-gate `reflection.ts:232-264` |
| F2 (Critical) | Statistically unsound gate | `statistics.ts` Welch-t/BH-FDR/PSR/deflated-Sharpe; `runner.ts:772-793` collect→FDR→3-per-night budget; `live-outcomes.ts:203-271` shadow renew (21d cap) / revoke (Sharpe≤0, ≥10 live trades); old gate's 56 promotions → 2 |
| F3 (High) | DSL 48h auto-promote dead code | Deleted 2026-06-19; `dsl-lifecycle.ts:25-40` records rationale; gauntlet replaces it, hard stop at `candidate` |
| F4 (High) | Naked futures on SL failure | Retry×2 → flatten → fatal-if-flatten-fails, `engine.ts:815-858` |
| F7 (High) | Weak param-write auth | Bearer-only, query-param removed, ≥16-char boot assertion, `server.ts:79-85,559-568` |
| F12 (Med) | $2 min position | `ensureMinimumSize()` floors viable signals up, `manager.ts:699-717` |
| (partial) F6 | No rejection records | Killed/paused + research-only rejections now persisted, `loop.ts:1587-1609`; remainder open as N3 |
### Open findings
**HIGH**
**N1. Dip-booster bypasses the execution engine.** *Evidence:* `base/src/dca/dip-booster.ts:330` direct `trader.createMarketOrder()`; own risk limits only; `DIP_LIVE=false` verified in pm2 env. *Why:* the only reasoning→execution path outside the single gate; flipping one env var re-introduces v1's core vulnerability class with zero journal/breaker/guard coverage. *Fix:* route through `executionEngine.execute()`, or boot-assert `DIP_LIVE` requires engine routing; journal every dip order either way. *Verify:* grep-level CI check that `dca/` contains no direct trader order calls; dry-run diff test.
**N2. Reconciliation advisory-only; ghost positions chronic.** *(carried F5, unchanged)* *Evidence:* `reconciler.ts:300-325` ("auto-adoption disabled"), adoption code `:336-447` uncalled. *Fix & verify:* as v1 — 2-consecutive-check auto-adopt/evict under size ceiling, logged; simulated-drift integration test.
**N3. No unified intent ledger; most rejections still invisible.** *(carried F6, partial)* *Evidence:* `loop.ts:1548-1551` debug-only dedup/cooldown/balance/breaker rejections; no `shared/src/control-plane/`; no intents tables. *Fix:* Supabase `intents`/`intent_decisions`; every proposal gets a disposition row. *Verify:* count(proposals)==count(rows) over a test session.
**MEDIUM**
**N4. No cross-bot portfolio exposure loop.** *(carried F8)* Each bot sees only itself. Lower urgency than v1 (IKA halted, venues disjoint) but real capital is 100× April's. Start with a hermes deep-sweep aggregate check (observability only).
**N5. Promotion pipeline has no human lane and no risk-direction awareness.** The F2 gates are real but direction-blind: a promotion that doubles `position_size_multiplier` and one that halves it follow the identical automatic path. *Fix:* `risk_direction` on every param write; Discord button approval for risk-increasing promotions (deferred PR#8); auto-apply stays for risk-reducing/in-range. *Location:* `writers.ts` + `runner.ts` promotion block + conductor Discord client.
**N6. Futures still have no resting take-profit.** SL is now resting + retry→flatten (F4 fixed), but TP remains the in-memory 4-stage trailing stop requiring a live bot every 30s (`engine.ts:861-872,973-978`; `takeProfitOrderIds: []`). A crash during a favorable move abandons the exit. *Fix:* resting reduce-only TP at stage-2 target, replaced as stages advance.
**N7. LLM prompts/responses still unlogged.** *(carried F10)* `experiment-log.ts` tracks cost, not content; `reflection.ts` discards prompts after parsing. Atlas narration likewise. Bad-promotion forensics ends at "the LLM proposed it" with no way to see why. *Fix:* transcript sidecar keyed by experiment_id/reflection run; Walrus `journal` namespace is a natural home.
**N8. Base cooldowns are in-memory; stocks already persists.** `loop.ts:1544-1556` vs `stocks/src/orchestrator/loop.ts:195-219`. Restart clears live-stop cooldowns exactly when a crash-loop makes re-entry most likely. *Fix:* port the stocks durable pattern (shared/durable is already imported ecosystem-wide).
**N9. Risk limits inline and unversioned.** *(carried F11)* Breaker constants (`engine.ts:95-96`), daily-loss/drawdown checks, dip-booster's private limits — correct but scattered; no policy version stamped on decisions. Phase-2 extraction target.
**N10. Doc/dashboard drift became user-visible.** ARCHITECTURE.md still describes IKA as a live 3-desk (halted since April), no Atlas anywhere; conductor/ROADMAP.md Layers 2-4 remain aspirational; **dash.bigclaw.xyz served 502s for ~10 weeks** (cloudflared → :3140 → nothing listening since the dashboard was archived Apr 23) and the flowchart behind it was v3.0/March (OpenClaw-era). *Fixed this session:* new `shared/ops/dash-server.js` + rebuilt flowchart at `docs/ecosystem-flowchart.html` (current + target state). ARCHITECTURE.md rewrite still owed.
**N11. Atlas candidates have no consumer — the research loop dead-ends.** By design (Phase D unbuilt), but the loop currently ends at a Discord digest + `candidate` row. Until Phase D (paper-shadow executor on stocks + Discord confirm gate) exists, gauntlet survivors accumulate without ever being falsified forward. This is the *right* failure mode (safe), but it's also the 3-week kill-test's dependency: the evolution thesis can't prove +EV without a forward path.
**LOW**
**N12.** TradingView webhook signals ingested and stored but consumed by nothing (`signal-store.ts:163-178` methods uncalled) — dead integration; delete or wire as a confluence input. **N13.** Hermes `derivatives-freshness` checks funding/OI/LS but not the liquidations table (`derivatives-freshness.ts:31`) — the one table Atlas's oracle depends on. **N14.** Derivatives data collected but consumed by no trading decision (planned "derivatives→Charlie wiring" not built) — the Jun-29 discovery panel's thesis (real edge = derivatives microstructure) implies this is the highest-value unconsumed dataset in the system. **N15.** Flat breaker cooldowns; archived-repo/root-file clutter (carried).
### Doc-vs-code disagreements
| Doc claim | Reality |
|---|---|
| ARCHITECTURE.md: IKA 3 desks "Active/Live" | HALTED_BOTS default halts all three at boot (`crash-logger.ts:223-240`) |
| ARCHITECTURE.md: no Atlas, no gauntlet, no Walrus | Atlas Phases 0/A/B/C shipped and live (ATLAS_ENABLED=true, 02:30 cron) |
| ROADMAP.md Layers 2-4 (dynamic indicator registry, source discovery, prioritization) | Not started; INDICATOR_CATALOG hardcoded |
| dsl-lifecycle "draft→…→staging→active" | Pipeline hard-stops at `candidate`; `active` unreachable pending Phase D |
| Old flowchart v3.0 (served at dash.bigclaw.xyz) | Showed OpenClaw front desk (archived Apr 6), live IKA, no conductor validation layer — replaced this session |
---
## G. Phased Implementation Plan (updated)
**Phase 0 — Safety baseline: ✅ COMPLETE** (F1, F2, F3, F4, F7, F12 all verified). One regression-guard remains: **N1 dip-booster engine routing before any DIP_LIVE flip.**
**Phase 1 — Evidence chain (in progress, ~40%):** done — partial rejection recording, Walrus hypothesis ledger, vault spend ledger, observability digest fixed. Remaining — `intents`/`intent_decisions` tables (N3), LLM transcripts (N7), `active_params_hash` on trade_journal, human overrides as ledger events.
**Phase 2 — Control plane (next):** extract the three existing validators behind a common envelope in `shared/src/control-plane/` (policy.ts versioned limits absorbing N9; validate.ts pure function; ledger.ts). Add `risk_direction` + Discord approval lane for risk-increasing promotions (N5). Route dip-booster through the engine (N1). Reconciler auto-correct (N2).
**Phase 3 — Evaluation & reflection hardening:** resting futures TP (N6); base cooldown persistence (N8); cross-bot exposure sweep (N4); liquidations-freshness check (N13); promoter-quality dashboard (promoted-experiment live performance vs baseline — data already in `experiment_outcomes`).
**Phase 4 — Controlled self-improvement:** Atlas Phase D — paper-shadow executor for `candidate` strategies on the stocks bot + Discord confirm gate into any live status (N11); derivatives→strategy wiring as gauntlet-tested event studies (N14); conductor proposing *policy* changes as approval-required intents; auto-generated audit report from the intent ledger.
## H. Concrete Next PRs
1. **`dip-booster-engine-routing`** — replace direct `createMarketOrder` with `executionEngine.execute()`; boot-assert if `DIP_LIVE=true` without it. (N1, ~80 LOC)
2. **`intent-ledger`** — `intents`/`intent_decisions` migration; persist every proposal disposition incl. dedup/cooldown/balance/breaker rejections. (N3)
3. **`promotion-approval-gate`** — `risk_direction` on param writes; Discord button for risk-increasing promotions; auto-apply preserved for risk-reducing. (N5, the deferred PR#8)
4. **`futures-resting-tp`** — reduce-only TP at stage-2 target, amended as trailing stages advance. (N6)
5. **`base-cooldown-persistence`** — port stocks' `persistCooldowns`/`restoreCooldowns` to base via shared/durable. (N8, ~60 LOC)
6. **`reconciler-autocorrect`** — 2-consecutive-check adopt/evict under size ceiling, logged as intents. (N2)
7. **`llm-transcript-log`** — persist prompt+response per `complete()` call, keyed to experiment/reflection id (Walrus `journal` ns or Supabase sidecar). (N7)
8. **`liq-freshness-check`** — add liquidations table to hermes derivatives-freshness. (N13, ~20 LOC)
9. **`atlas-phase-d`** — paper-shadow executor for candidates on stocks + Discord confirm gate; unblocks the 3-week kill test. (N11)
10. **`docs-truth-pass`** — ARCHITECTURE.md rewrite (IKA halted, Atlas layer, validation layer); ROADMAP marked aspirational. (N10 remainder)
---
## Appendix: What's already good (preserve these)
- **The single execution gate** — now with SL retry→flatten, still the system's spine. Keep the dip-booster from eroding it.
- **Experiment↔trade linkage + live-shadow revocation** — the evidence chain now not only records outcomes but *acts* on them (TTL revoke on deterioration). This is a real closed loop.
- **The Atlas gauntlet + sealed vault** — deterministic verifier, one-shot OOS, append-only verdict ledger including failures. The maker/checker separation both reference articles call for, already in production. Generalize it; don't dilute it.
- **Hermes's bounded remediation** — 10 checks, daemon-as-precondition, port-aware kills, chronic-collapse escalation: months of scar tissue an autonomous loop needs.
- **The ecosystem-health-monitor pattern** (risk-reducing-only, TTL-bounded auto-writes) — still the correct template for "automatic."
- **Charlie's fenced hermes-side role** — annotate and request, never mutate. The conductor side now approximates the same shape through validation gates; the control plane finishes the job.