Sentinel Suite · Visual guide 1 of 2

Sentinel Architecture Map

A NinjaTrader-native trading OS: bar clocks that decide when a bar exists, sensors that publish opinions into typed seams, a fusion arbiter that turns them into one verdict, two order sources under a shared safety gate, a recorded corpus, and a 53-file offline Python lab whose whole job is to grade the other half honestly. Companion page: Runtime Topology →

Core v1.45.0 Council v1.11.0 Bar clocks 7 Declared voters 25 Context axes 5 AddOn services 22 Python tools 53 Order sources 2 2026-07-29
liverunning / proven on real data builtcompiles clean, not yet loaded or validated auditionrecorded at weight 0 — graded, not trusted specdesigned, nothing built pulledremoved by decision — do not revive
How to read the colour The palette is load-bearing, not decorative. Cyan is the only accent and means live / watching; green is money and direction; amber is caution; red is loss or a closed door. There is no rainbow of per-layer hues here on purpose — see Docs/SENTINEL_DESIGN_SYSTEM.md §0.
1Operator & Agent Access

Every surface is Sentinel-owned — a Sentinel control never lives in an NT dialog, so there is exactly one source of truth per setting.

NT8 Charts live
The real substrate. A chart = one bar clock + its sensors + a Council + optionally the Deck.
Cockpit live
Command surface — re-reads every …State seam: “is my brain alive, and why isn’t it trading?” 5 tabs incl. Helm.
Dashboard live
Account/app-level board: risk, governor, Copy tab, audit rows.
Deck live
On-chart manual trader + SIGNAL ARM. Public testers’ preview shipped.
Grafana · :3000 live
4 boards — trades · health · docs · legacy-node. Ops, not research.
Streamlit · :8501 live
Observatory + Explorer + Tick-Paths + Council-Paths. Research, not ops.
Claude Code + nt8bridge live
NT’s own compiler, headless, in ~15 s with real Roslyn errors. deploy hot-reloads running instances.
SSH / Tailscale → legacy-node live
Remote bake worker driven over PowerShell; lights-out replay baking.
the chart decides when a bar exists
2Bar Clocks — BarsTypes\

Sentinel’s first-class idea: the clock is a hypothesis. Each bar type answers “what unit of market activity deserves one bar?” — and each publishes its own seam under its own scope, so the chart is the bar type. Three axes are represented: distance, signed flow, unsigned cost of movement.

TBars 212201 live
Adaptive HA/Renko. Publishes BrickStateBRK. One “Speed Settings” knob.
TbarsCount 212202 live
Unsigned-activity clock — flow-clocked without direction.
Flux 212203 live
Order-flow imbalance bars. Publishes FluxStateFLUX + absorption damp.
Drift 212204 live
Asymmetric brick, flow-adaptive reversal edge. Publishes ConvictionStateCVB.
Lattice 212205 built
Path-independent Renko — every boundary is an exact lattice line, so determinism is provable. Needs F5 + NT restart.
Effort 212206 built
Closes on displacement or effort — which one fired is the signal (sweep vs absorption). Path-dependent by design.
Tide 212207 live
Cumulative-order-flow clock. Bar height ≈ price impact per unit of aggression (Kyle’s λ). The clock the live range-filter result came in on — which is why the flow-clock hypothesis is being tested first.
sensors read the bars and publish opinions
3Sensors & Voters — 25 declared

A voter never calls the Council. It publishes a typed seam (SetXState) and the Council reads it. That inversion is why a voter can be auditioned at w=0, replaced, or graded standalone without touching fusion. Seams are keyed by scope = <instrument>.<barTag> — one chart’s worth of context.

Price / trend bloc live
Ten voices, all derived from price — and therefore all correlated. This bloc is the one the 07-26 re-test killed.
TRND1.0CCI0.8ADX0.6 ENV0.6STRC0.7AVMA0.6 SPRT0.7PSAR0.5ARCH0.7 VDYA0.5
Triggers & mean-reversion live
One-shot voices. Kind is load-bearing — the conviction denominator counts a TRIGGER differently from a STATE.
CMP0.7WAE0.7GREV0.9 EXH0.5ZSC0.4HARM0.4
Order-flow family the untested family
The only bloc not derived from price. Three of these are published by a bar type, not a study — the clock itself is the sensor.
FLUXbar typeCVBbar type CVD0.?BSPw=0 FLOW0.9
Structural & macro live
BRK comes from the TBars clock; IMKT is instrument-keyed by design (a correlated-market lean does not vary with your bar type).
BRK0.5IMKT0.6
Exploration primitives w = 0
w=0 is a first-class state: recorded in the corpus, zero fusion impact. It is how a voice earns promotion instead of being assumed.
STFalso drives the chop vetoBSP
EYE dead
Settled 2026-07-23. Not a voter, not in the audition, never synced. All four audition rosters carry a DO-NOT-RE-ADD block — the decision is enforced in the artifact, not just remembered.

Alongside the voters: ~30 ported smoother / filter primitives (SMA…ZLEMA, Laguerre, Butterworth, Gaussian, SuperSmoother, Tillson T3, DSMA, LLMA…) that voters build on, plus SentinelWallpaper, SentinelBrickCounter, SentinelBarDump and SentinelTapeRecorder as instrumentation.

4Orthogonal Context Axes

Not voters — modulators and vetoes. They do not add direction; they change how much the direction is worth, or shut the door. Toggled via the Council’s Consult* settings, not Roster.conf lines.

CLOCK live
Session phase · mins-to-close · kill window. Instrument-keyed by design.
PARTIC live
Time-normalised RVOL + climax/dry-up. ⚠ scope-keyed — load on every Council chart.
MTF live
Higher-timeframe consensus ladder; counter-HTF penalty.
LOC veto
VWAP / PDH-PDL / OR / IB levels sitting in the trade’s path.
LIQ veto
Order-flow absorption walls — vetoes an entry into a wall.
Profile · Regime
Volume-profile location and K-means volatility regime feed the conviction/size damps rather than the direction.
one verdict per scope
5Fusion — the Council

The arbiter reads seams and emits one CouncilState per scope. It is an unfitted linear model — hand weights, a conviction floor — and saying so out loud is the point: the Lab exists to fit it, and the 07-26 re-test says the fused direction is currently a coin flip gross.

Council v1.11.0 live
Real fork — remove the old Council from a chart before adding this, or two Councils collide on one scope.
CouncilFusion live
The one shared fuse core, delegated to by the live Council so the offline harness and NT cannot drift.
Declared roster live
Roster.conf + RosterComplete. A roster is declared, not emergent — which is what makes a crashed sensor distinguishable from a quiet one.
Vetoes & damps live
Chop · wall · kill-window vetoes; squeeze / absorption / regime / profile damps. All hang off one seam read — null the read and all three close.
Scope + lane keying live
GC.212201v6x24. Same instrument + bar type on two charts is detected and logged as SCOPE CONTENTION, never silently permitted. Lanes.conf wins over F6.
Chart-derived vs instrument-level enforced
A shared seam is not a licence to influence a chart you are not on. Chart-derived sensors must be attached; instrument-level ones may sit off-chart.
core runtime · every path goes through the gate
6Core Runtime & Safety — SentinelCore v1.45.0

One partial class split across three files for distribution tiering, not for tidiness: the public bundles can ship Foundation without Safety. 22 Sentinel AddOn files in total.

SentinelCore.Foundation live
Settings dir · Log (6 rotation generations) · Ledger · State · the seam stores · Swallow() — the recorded empty catch.
SentinelCore.Safety — L2 live
Feed health · CanEnter · governor · trailing drawdown · profiles · session · sizing · order guards · GateEntry. “The one place sizing math lives.”
SentinelCore.SystemBuilder live
VoterCatalog (tag → class → seam → default weight+kind) · RosterIO · LaneIO — one reader/writer so writer and reader can never drift on format.
Services live
State · Log · LogEngine · Alert · News · Risk · Arc (fleet) · Binds · Lens (analytics) · Skin.
SentinelSkin + drawing framework live
Six themes behind one switch; glass cards; the label remover; the cards.off render kill switch that ended the legacy-node UCEERR floods.
Beacon / generation guard live
An F5 orphans a bar-type seam onto a dead AppDomain. Beacon/BeaconForeign let a consumer say DECOUPLED — restart NT instead of “absent”.
two order sources · one gate
7Execution & Output

The suite has exactly two order sources. Any statement about “all three order paths” is stale — GTrader21 was pulled 2026-07-29.

Deck v0.2.6 — manual live
On-chart trader: brackets, breakeven, trailing, order-line overlay, SIGNAL ARM auto-fire, preview safety band, diagnostics export. Market fills now record slip.
Bridge v0.3.1 — automated live
Consumes CouncilState, records to Ledger, stamps the crossing quote. ⚠ known gap: it sizes itself instead of routing through SizedQuantity().
Helm — interdiction plumbing
Grab the wheel of a running Bridge without disabling the strategy. Never touches an order. Position verbs not yet exercised live.
Copier v0.1.0h built
Headless fill mirror: Primary→Followers, same-provider prop rule, GC→MGC cross-trading, kill/governor/session/Eye gates, manual-assist tickets.
Keel · Hull · Pilot spec
The next rung: Keel = instrumented strategy (must reproduce the frozen control’s trade list exactly), Hull = reusable base, Pilot = strategy publishes a signal, Bridge executes.
GTrader21 pulled
Archived 2026-07-29. Its order-ownership lessons live in memory; the tool does not come back.
everything that happened gets written down
8Recording — the corpus

One writer, one schema, per artifact. Two loadable recorders once meant a workspace could quietly start a second writer emitting contaminated rows — which is why v1_4 was archived rather than left “harmlessly” in the tree.

ExcursionRecorder v2.3.0 live
Schema 1.5 (honest firePx) → Excursions\council\. ⚠ opens only on COUNCIL — so any strategy is currently invisible to the corpus.
CandidateRecorder v1.1.0 live
cand.* + ctick.3 self-describing tick-path headers, so context needs no join.
Ledger live
Orders · actions · fills, with episode + instance context so a fill can be joined to the verdict that caused it.
Tick sidecars live
Tick-true excursion paths. Two independent trackers: MaxFav/AdvTick (5-min window) and MaxMFE/MAE (60-min, uncapped).
BarDump · TapeRecorder · BrickLog live
Raw bar/tape/brick exports — the input side the offline harness needs to prove itself equal to NT.
Replay honesty gate enforced
A seam has no as-of semantics: UpdatedUtc stamps wall-clock even mid-replay. Anything that records must gate on State.Realtime / IsHistorical.
out of NinjaTrader, into Python
9The Python Lab — 53 tools

Never imported by NinjaTrader, never compiled into NinjaTrader.Custom.dll. This is the half that grades the other half. Its governing rule: a measurement you cannot falsify is not a measurement — so the harness ships a test that proves its own gate can fail.

Offline harness — Lab\harness\ 13 files
Run bar types + sensors + fusion outside NT from exported ticks. Nothing here may import NinjaScript.
nrdcsv tick reader · tide (Tide re-implemented in Python) · equivalence the gate the whole harness stands on · selftest_equivalence proves the gate can fail before it is trusted · selfdiff is NT deterministic? · cvddrift localise harness-vs-NT disagreement to the print · l2book · noise_floor measure precision before interpreting a difference · flowscale · pairing_study · regime_study · retest_direction · exp0005 (pre-registered).
Grading & analysis 9 files
pathlab tick-true path & exit-policy analysis with a cost model · sensor_truth grade every voter standalone per bar type · voter_edge confirming or contrarian, and on how much data · council_paths does conviction buy better-shaped trades? · compare_bartypes how much does the clock matter? · limitlab resting limit vs market at the fire · news_overlap · verify_votes + verify_firepx acceptance tests.
ML core — Lab\sentinel_lab\ + train.py 6 files
dataset schema-tolerant JSONL loader · labels barrier labels, censoring, uniqueness weights · cv purged walk-forward with an embargo — never random k-fold · bartag the Python mirror of FriendlyBartag (display only) · train.py the offline trainer.
Data plane 6 files
ingest\ingest.py JSONL → SQLite (the canon path; the analyzer reads the DB, never the files) · snapshot\snapshot.py tiered WORM snapshot ladder · quartermaster\ .nrd catalog/verify/coverage-holes (filenames only — a 100 GB scan is a stat sweep) · sync\replay_sync.py rule-based bake-node sync that auto-includes new Sentinel files.
Visualisation — Lab\viz\ 4 files
observatory.py the analyzer — tree data-group picker + the 3-panel honesty ladder (firePx ceiling → bracket → tick-true real entry, which goes negative) · explorer.py filterable Plotly over the tick corpus · tickpaths.py · pages\1_Council_Paths.py (imports the CLI so the numbers cannot drift).
Health, docs & faults 8 files
health\probe.py NT + suite health · corpus_probe.py is the corpus trustworthy · legacy-node_probe.py read-only over SSH · docs\audit.py drift/link/version audit · docs\facts.py single-sources volatile numbers as {{tokens}} · ident_check.py does every identifier a doc names still exist · grafana\build_*_dashboard.py boards are generated, not hand-edited JSON · lab_faults.py the Lab’s Swallow().
Outside Lab\ 4 files
tools\md2atlas.py renders every doc into the Field-Manual house style · News\ff_scrape.py + sentinel_newsconf.py economic calendar → News.conf event veto · suite-oss\tools\lint_suite.py.
Why the Lab is not optional Every honest negative this project owns came out of here — the fused direction is a coin flip gross, 0/19 voters beat 50%, the whole loss is the crossing cost, conviction is inverted on Flux. A pre-registered holdout is the reason those results are trustworthy rather than a curve someone believed.
measured, then watched
10Observability & Data Plane

Canon pipeline: tools → JSONL → ingester → DB → analyzer. Reading the JSONL directly is an expedient, never the architecture.

sentinel.log live
6 rotation generations. Read the log, not a screenshot.
sentinel.db live
SQLite, WAL, 30 s busy timeout. ⏭ single-node → Postgres.
Grafana :3000 4 boards
trades · health · docs · legacy-node. Tron chrome via native extraThemes.
Streamlit :8501 live
The research surface.
Probes :8502 :8504 :8505 live
health · legacy-node · docs-audit, each single-instance-guarded.
Zero vs dead enforced
Headline counts read the heartbeat table, so 0 means “measured zero just now” — an empty findings table would read 0 for a dead probe.
11Foundation & Infrastructure
NinjaTrader 8.1.7.2 live
One assembly, whole-tree compile — one broken file blocks everything.
cli-nt-bridge v1.1.1
NT’s own compiler headless. Our upstream PR merged.
legacy-node bake worker live
Tailscale · Tradovate replay feed · lights-out via ConnectOnStartup + autorun + autologin.
Conductor v0.1.0h live
Replay transport: seek · queue · checkpoint-resume · autorun. Seven NT/logic bugs found by driving it.
Quartermaster live
.nrd supply: procure → verify → catalog → provision. 90-day rolling feed floor; deep history from the extracted archive.
Snapshot ladder live
Tiered WORM snapshots + scheduled tasks. Guarantees integrity, not correctness.

12The closed loop

The suite is not a signal generator with reporting bolted on — it is a grading loop, and the first thing it graded honestly was its own thesis.

01
Clock
Choose the unit of activity that gets one bar.
02
Sense
Voters publish typed seams under a scope.
03
Fuse
Council → one verdict + a decision vector.
04
Gate
Size, then validate: kill · limits · session · feed.
05
Execute
Deck or Bridge. Record the crossing quote.
06
Record
Row + tick sidecar + Ledger, realtime-gated.
07
Ingest
JSONL → SQLite. One writer, one schema.
08
Grade
Tick-true, cost-aware, on a pre-registered holdout.
09
Decide
Promote from w=0, or retire the tool.

The feedback edge is the whole product. A negative result that survives the holdout retires a tool — that is the loop working, not the loop failing.

13What this map deliberately does not claim

Honest status Nothing is running right now and nothing is broken (nt8bridge compile → errors: []). Lattice and Effort compile clean but have never been loaded. Keel/Hull/Pilot are specs with no code. The fused Council carries no measured directional edge today; the assets that are proven are the risk container and the grading tooling. Before any bake or live run: F5 + full NT restart, remove the old Council before adding v1.11.0, and re-add Deck v0.2.6 — both are real version forks.
One drift found while drawing this Council_v1_11_0.KnownVoters declares 25 voters including CVD, but SentinelCore.SystemBuilder.VoterCatalog holds 24 rows — the CVD row is missing, even though Core’s v1.43.0 header says it was added. The Cockpit and System Builder both read the catalog, so CVD is invisible to them. Cheap fix, and worth doing before the next roster edit.