<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Oluwafemi Adedayo</title>
    <description>The latest articles on Forem by Oluwafemi Adedayo (@hallengray).</description>
    <link>https://forem.com/hallengray</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3879006%2Fa2a10efa-7c98-42d1-91e3-eb64617743bf.jpg</url>
      <title>Forem: Oluwafemi Adedayo</title>
      <link>https://forem.com/hallengray</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/hallengray"/>
    <language>en</language>
    <item>
      <title>Signal Independence Audit</title>
      <dc:creator>Oluwafemi Adedayo</dc:creator>
      <pubDate>Thu, 14 May 2026 09:09:40 +0000</pubDate>
      <link>https://forem.com/hallengray/signal-independence-audit-4jdb</link>
      <guid>https://forem.com/hallengray/signal-independence-audit-4jdb</guid>
      <description>&lt;h1&gt;
  
  
  Signal Independence Audit
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Pre-hyperopt screening framework for testing whether a candidate signal adds information over a baseline. Methodology + crypto case study.
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/hallengray/signal-independence-audit" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This is the canonical Markdown source. The published version is hosted at &lt;a href="https://hallengray.github.io/signal-independence-audit/BLOG_POST.html" rel="noopener noreferrer"&gt;https://hallengray.github.io/signal-independence-audit/BLOG_POST.html&lt;/a&gt; (GitHub Pages auto-resolves the relative &lt;code&gt;./case-study/*.md&lt;/code&gt; links to the corresponding Pages-rendered HTML).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Four crypto strategy failures, and the tool that caught one in 3 seconds
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Or: what happens when you bring software-engineering ADR discipline to retail quant research, then watch it produce an honest negative result.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Hook
&lt;/h2&gt;

&lt;p&gt;Most retail quant content reads “I found a strategy that backtests well.” This post is the inverse.&lt;/p&gt;

&lt;p&gt;Over four phases of work on BTC/USDT and ETH/USDT spot at the 4-hour timeframe, four different strategies were tested against pre-committed criteria: Sharpe &amp;gt; 1.0 AND profit factor &amp;gt; 1.4 out-of-sample. All four failed. The bar was never lowered. Across nineteen architecture-decision records the project never said “the threshold was too strict” — it documented why each strategy fell short and moved to the next question.&lt;/p&gt;

&lt;p&gt;One of those four — a funding-rate-conditioned variant of a Donchian breakout — was killed in &lt;strong&gt;~3 seconds of pure-Python testing&lt;/strong&gt;, before any of the planned 1000-epoch × 2-seed × 3-window hyperopt compute ran. The tool that produced that 3-second verdict is what this post ships. It’s a four-screen pre-hyperopt audit called Signal Independence Audit (SIA), published as a standalone repo under Apache 2.0. The methodology applies to any candidate signal on any asset class; crypto was the surface where it was developed and stress-tested.&lt;/p&gt;

&lt;p&gt;The rest of this post walks through each of the four failures in turn, then the literature audit that produced an unexpected corrigendum about how AI-assisted research goes wrong, then what survives the kills as reusable methodology. It ends on what a documented negative result actually is, and what it isn’t.&lt;/p&gt;

&lt;p&gt;If you only read one section, read the Phase 4 SIA-kill section — that’s where the methodology earns its keep.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 2 — MFD (price-only trend-following) → §14 FAIL, Sharpe-bound
&lt;/h2&gt;

&lt;p&gt;The first attempt was a Macro-Filtered Donchian Breakout — MFD. Entries fire when the 4-hour close breaks above an N-period Donchian high, gated by a daily-timeframe filter (daily close above a slow EMA, so trend-following only fires during macro uptrends). Exits are symmetric: a Donchian-low cross. Stops are ATR-scaled. Two seeds, 1000 hyperopt epochs each, top-5 candidates per seed evaluated out-of-sample across four calendar walk-forward windows.&lt;/p&gt;

&lt;p&gt;The headline result: best out-of-sample Sharpe &lt;strong&gt;0.314&lt;/strong&gt;, against a threshold of &amp;gt; 1.0. That’s roughly 3× below the bar. The best per-window Sharpe across the four walk-forward windows was 0.553 (W3, Jan–Jun 2024) — still well short. Profit factor was not the problem: PF cleared 1.4 on multiple candidates, ranging 1.6–2.5 across the windows. The binding constraint was Sharpe.&lt;/p&gt;

&lt;p&gt;The failure mode is informative: returns per unit volatility were too low, but the strategy was profitable on average. Stoploss-firing breakdowns showed ~100% of exits firing as &lt;code&gt;exit_signal&lt;/code&gt; (the Donchian-low cross) with near-zero &lt;code&gt;stop_loss&lt;/code&gt; and zero &lt;code&gt;roi&lt;/code&gt; exits — meaning the per-trade edge depends on exit-signal timing, not on stoploss tightness, and that the edge is real but structurally too small relative to trade-noise variance. Wider ATR stops would not have closed the 3× Sharpe gap; the modal expected outcome had already been flagged before the hyperopt ran (&lt;a href="///signal-independence-audit/case-study/01-mfd-kill-adr-0011.html"&gt;details&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Phase 2’s discipline holds: 0 of 4 walk-forward windows pass, the bar wasn’t softened, and the ADR records exactly what the failure shape was — not “this strategy needs more tuning” but “this strategy class produces edge below threshold on this universe at this timeframe.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 3 — BMR (mean reversion) → §14 FAIL, Sharpe AND PF bound
&lt;/h2&gt;

&lt;p&gt;If trend-following’s per-trade edge was too small, mean reversion was the next obvious thing to try. Phase 3’s strategy was BMR — Bollinger Mean Revert — entries when the 4-hour close drops below a lower Bollinger band, exits when it returns to the middle band, gated by the same daily-EMA macro filter as MFD (to avoid catching falling knives during macro downtrends).&lt;/p&gt;

&lt;p&gt;Three seeds, top-5 hyperopt candidates per seed, 15 candidates evaluated out-of-sample total. Plan Option B was invoked to skip walk-forward — the trigger conditions for that option are conservative (all OOS candidates fail decisively AND multiple §14 criteria bind AND walk-forward would only confirm), and BMR cleared all three triggers.&lt;/p&gt;

&lt;p&gt;Headline: &lt;strong&gt;all 15 out-of-sample candidates were unprofitable&lt;/strong&gt;. Best OOS Sharpe -0.083 (seed=42 epoch=51). Worst -0.361. Mean across the 15: -0.180. Mean PF: 0.59 (best 0.85, worst 0.31). Every candidate was both unprofitable AND low-conviction (&lt;a href="///signal-independence-audit/case-study/02-bmr-kill-adr-0013.html"&gt;details&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This is a structurally worse failure than MFD’s. MFD was profitable-but-inconsistent — its OOS Sharpe was below threshold but its PF cleared on multiple candidates. BMR was unprofitable-AND-inconsistent: all three seeds independently produced negative best in-sample Sharpe (−0.066 to −0.111), and the hyperopt landscape showed degenerate plateaus (638 out of 1000 epochs tied on one seed), which itself suggested the macro filter was dominating entries.&lt;/p&gt;

&lt;p&gt;Two consecutive strategies failed §14 in two different ways. The pattern raised a hypothesis explicitly: maybe the universe + timeframe combination (BTC/ETH spot, 4-hour primary) is too narrow to support strategies with both high enough Sharpe AND high enough profitability to clear the bar. Phase 4 was scoped with that hypothesis on the table.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 4 — FCMFD (funding-rate-conditioned MFD) → SIA-killed pre-hyperopt in 3 seconds (the lead worked example)
&lt;/h2&gt;

&lt;p&gt;By the start of Phase 4 there were two compute-expensive kills in the rearview. Each had cost roughly an hour of cloud hyperopt to produce a verdict that, in retrospect, was visible in the strategy’s design before any hyperopt ran. MFD’s per-trade edge was structurally too small. BMR’s macro filter dominated entries. Both shapes — “candidate signal weaker than expected after accounting for what’s already captured” — should have been catchable cheaply, but the pipeline didn’t have a cheap mechanism for catching them.&lt;/p&gt;

&lt;p&gt;So before spending another hyperopt budget on the natural Phase 4 candidate, I built one.&lt;/p&gt;

&lt;p&gt;The Phase 4 candidate was FCMFD: MFD plus a funding-rate gate. The thesis was direct — when perpetual funding rates are low (longs are not crowded), forward breakouts have more room to run; when funding is high (longs are crowded), breakouts mean-revert. Funding rates are public on-chain data, free, and intuitive. The natural plan was: add a “funding &amp;lt; 60th-percentile of trailing 90-day window” gate to MFD’s entry condition, hyperopt the rolling-window and threshold parameters, evaluate out-of-sample.&lt;/p&gt;

&lt;p&gt;Before any of that, I built the Signal Independence Audit (SIA) — four mechanical screens that test whether a candidate signal adds information over a price-only baseline (&lt;a href="///signal-independence-audit/case-study/03-sia-framework-adr-0014.html"&gt;details&lt;/a&gt;). Screen 1: coverage (does the gate change trade frequency? Does it preserve enough trades for evaluation?). Screen 2: lift (do funding-gated entries’ forward returns exceed price-only-gated entries by ≥ 0.5σ on ≥ 2 of 3 horizons?). Screen 3: Jaccard overlap on a small hyperparameter grid (does the gate’s trade-set stay stable under perturbation?). Screen 4: information-split control (does the candidate signal explain more of the forward-return variance than realised volatility — the obvious confounder — explains?). The four screens run on the training window only, against a frozen rule set, and produce a JSON verdict. They take about 3 seconds.&lt;/p&gt;

&lt;p&gt;The Phase 4 SIA ran and returned exit code 1 (&lt;a href="///signal-independence-audit/case-study/04-fcmfd-sia-kill-adr-0015.html"&gt;details&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;Horizon&lt;/p&gt;

&lt;p&gt;Treatment mean&lt;/p&gt;

&lt;p&gt;Control mean&lt;/p&gt;

&lt;p&gt;Gap σ&lt;/p&gt;

&lt;p&gt;Threshold&lt;/p&gt;

&lt;p&gt;Verdict&lt;/p&gt;

&lt;p&gt;5d&lt;/p&gt;

&lt;p&gt;0.85%&lt;/p&gt;

&lt;p&gt;0.25%&lt;/p&gt;

&lt;p&gt;0.147&lt;/p&gt;

&lt;p&gt;0.500&lt;/p&gt;

&lt;p&gt;FAIL&lt;/p&gt;

&lt;p&gt;10d&lt;/p&gt;

&lt;p&gt;1.83%&lt;/p&gt;

&lt;p&gt;1.20%&lt;/p&gt;

&lt;p&gt;0.098&lt;/p&gt;

&lt;p&gt;0.500&lt;/p&gt;

&lt;p&gt;FAIL&lt;/p&gt;

&lt;p&gt;20d&lt;/p&gt;

&lt;p&gt;3.78%&lt;/p&gt;

&lt;p&gt;3.69%&lt;/p&gt;

&lt;p&gt;0.016&lt;/p&gt;

&lt;p&gt;0.500&lt;/p&gt;

&lt;p&gt;FAIL&lt;/p&gt;

&lt;p&gt;The locked direction was qualitatively right — funding-gated entries outperformed price-only entries on all three horizons. The problem was magnitude: the gap σ values (0.147 / 0.098 / 0.016) sat well below the 0.5σ-pooled-stddev bar. By the 20-day horizon the gap was effectively zero. Screen 4 reinforced the verdict: vol-tercile separation on the 10-day horizon was 6.93 percentage points (low vol 5.14%, mid -1.79%, high 3.95%); funding-tercile separation was 2.45pp (low 3.50%, mid 2.77%, high 1.05%). Vol explained more of the forward returns than funding added.&lt;/p&gt;

&lt;p&gt;The inversion check made the kill cleaner. ADR-0014 reserves a “Phase 4-prime” branch for the case where the &lt;em&gt;inverted&lt;/em&gt; signal direction shows ≥ 1.0σ separation — i.e., maybe the right interpretation is the opposite of what was hypothesised. The inversion cohort (funding &amp;gt; 40th percentile) underperformed control on &lt;strong&gt;all three&lt;/strong&gt; horizons with &lt;strong&gt;negative&lt;/strong&gt; gap σ. The thesis wasn’t wrong about direction; it was right about direction and quantitatively too weak.&lt;/p&gt;

&lt;p&gt;Here’s where SIA earns the post’s title. The Phase 4b 6-month smoke (Jan–Jun 2024) had looked &lt;em&gt;materially better&lt;/em&gt; than anything Phase 2 or 3 ever produced: +16.84% return, Sortino 2.04, daily wallet Sharpe 1.98. Without SIA, that smoke would have justified the full 1000-epoch × 2-seed × 3-window hyperopt run. SIA put the smoke in context: 18 trades over 6 months in a +46% market-change window, with a single March 2024 winner (+174 USDT) driving most of the PnL. Across the full 3.5-year training window — 313 conjunction trades, multiple regimes — the gate doesn’t carry a tradeable lift. The smoke wasn’t fraudulent; it was a small-sample regime-favourable artefact, and SIA was the mechanism that converted it from “promising signal” to “don’t spend the compute” in 3 seconds.&lt;/p&gt;

&lt;p&gt;Phase 2 burned roughly a Hetzner-hour × 4 windows to learn its shape of fact. Phase 3 burned a Hetzner-hour × 2 seeds. Phase 4 burned 3 seconds laptop time. That is the entire value proposition of pre-hyperopt screening.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 5 — literature audit, pre-committed gates, and the routing verdict
&lt;/h2&gt;

&lt;p&gt;After three failures with three different failure modes (Phase 2 Sharpe-bound profitable; Phase 3 unprofitable on both criteria; Phase 4 candidate signal too weak vs vol), the productive next question wasn’t “what strategy class do we try fourth?” It was: &lt;strong&gt;is §14 empirically achievable on this surface at all?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That’s an evidence question, not a strategy-building question. So Phase 5 started with a two-day literature audit of published academic cryptocurrency-trading research, looking for a Tier-1 source reporting Sharpe &amp;gt; 1.0 AND PF &amp;gt; 1.4 OOS on a retail-realistic universe (&lt;a href="///signal-independence-audit/case-study/05-literature-audit.html"&gt;details&lt;/a&gt;). The audit produced a Criterion D verdict — mixed/inconclusive evidence — which routed to a structured next move: test the timeframe hypothesis cheaply (Path C), and gate any expensive follow-up (Path D) on four pre-committed verifications (&lt;a href="///signal-independence-audit/case-study/06-phase-5b-decision-adr-0017.html"&gt;details&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Path C: take Phase 2’s locked MacroDonchian default parameters (no hyperopt, no signal-class change) and re-run them at 8-hour primary timeframe across the same four walk-forward windows. The narrow question: was 4h the binding constraint? Result (&lt;a href="///signal-independence-audit/case-study/07-path-c-kill-adr-0018.html"&gt;details&lt;/a&gt;) — 0 of 4 windows clear §14 at 8h. Per-window Sharpe means: &lt;strong&gt;4h 0.425 vs 8h 0.415&lt;/strong&gt; (a 2% degradation, not an improvement). PF passed comfortably in all four 8h windows (1.45–2.07 range), so the binding constraint stayed exactly where it was at 4h: returns per unit volatility too low for this universe at this strategy class, regardless of candle cadence.&lt;/p&gt;

&lt;p&gt;Path D Gate 1 was the next pre-committed verification: retrieve the full text of Fieberg/Liedtke/Metko/Zaremba 2023 (&lt;em&gt;Quantitative Finance&lt;/em&gt;) — the closest paper in the audit’s Criterion C set — and extract its profit factor figure for the long-only winner portfolio. If PF &amp;lt; 1.4 net of fees, Criterion C strictly fails on §14’s AND-clause and the path forward routes to Path E (stop in-universe; ship the framework).&lt;/p&gt;

&lt;p&gt;The full text was retrieved (via the CC-BY 4.0 published version on &lt;code&gt;open.icm.edu.pl&lt;/code&gt;, after a header-tweaked HTTP request bypassed the JavaScript CAPTCHA — recorded for reproducibility). Findings (&lt;a href="///signal-independence-audit/case-study/08-gate-1-pf-verification.html"&gt;details&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Profit factor is never reported in the paper.&lt;/strong&gt; Anywhere. Zero matches across main results, the long-only robustness section (§4.4.2, Table 13), the liquidity-restricted subsamples (Table 12), the 768-design-choice robustness (Table 8), or the subperiod analysis (Table 5). The paper reports weekly mean return, standard deviation, Newey–West t-statistic, and annualised Sharpe. PF (or any close analogue) is &lt;strong&gt;never computed and never tabulated&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;No transaction-cost analysis exists.&lt;/strong&gt; Zero matches for “transaction cost”, “trading cost”, “fees”, “basis points”, “bps”, or “net of”. All performance figures (Sharpe 1.28 for cross-sectional winners; Sharpe 1.09 for long-only winners minus risk-free; Sharpe 1.43 for the most-liquid-25% best variant) are &lt;strong&gt;gross&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is strictly stronger than the gate’s pre-committed FAIL condition. The metric required to evaluate the AND-clause doesn’t exist in the source; the net-of-fees clause is unsatisfiable because no friction analysis is reported. Criterion E fires. Path D is foreclosed. Path E is routed: stop in-universe; ship the framework artefacts (&lt;a href="///signal-independence-audit/case-study/09-path-e-routing-adr-0019.html"&gt;details&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The routing isn’t a soft conclusion. The gates were written into ADR-0017 &lt;em&gt;before&lt;/em&gt; Path C ran, specifically to prevent the failure mode of “Path C fails → grind on without sharper criteria.” The pre-commit caught a 4–8 week false start (Path D infrastructure build) before it happened. That’s the demonstrable value.&lt;/p&gt;

&lt;h2&gt;
  
  
  The audit corrigendum — what AI-assisted literature research got wrong, and how it was caught
&lt;/h2&gt;

&lt;p&gt;The Path D Gate 1 retrieval surfaced something the audit hadn’t anticipated. The Phase 5a literature audit, working under paywall constraints and time pressure, had attributed two specific phrases to Fieberg et al 2023:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“incurs substantial trading costs”&lt;/p&gt;

&lt;p&gt;“extracts alphas largely from short positions”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These phrases appeared in the audit’s §5.6 verification update as caveats on the paper’s headline Sharpe figures — framing them as evidence that the paper itself flagged friction degradation and short-side alpha capture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Neither phrase — nor its substantive content — appears anywhere in the paper.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The misattribution survived the 2-hour focused audit because the original retrieval relied on search-result snippets and abstracts. The paywall prevented full-text verification at audit time. The narrative looked coherent — search snippets about cross-sectional momentum often &lt;em&gt;do&lt;/em&gt; discuss trading costs and short-side dynamics, and there’s a Liu/Tsyvinski/Wu 2022 paper that addresses exactly those topics for cryptocurrency. It’s the most likely actual source of the attributed phrasing. The audit’s compressed-time retrieval co-mingled snippets across sources and attributed to one paper what another said.&lt;/p&gt;

&lt;p&gt;The error was caught by construction. ADR-0017’s Gate 1 was written into the Path D escalation pipeline for general epistemic-hygiene reasons — literature claims should be verified against full text before they fund infrastructure builds — not because this specific error was anticipated. When Phase 5d executed the gate, the full text didn’t contain the attributed phrases. The pre-committed gate caught the error because the gate &lt;em&gt;was&lt;/em&gt; the verification mechanism (&lt;a href="///signal-independence-audit/case-study/10-audit-corrigendum.html"&gt;standalone teaching artefact&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The systemic lesson is what makes this section load-bearing for the rest of the post:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Paywall reliance + abstract-only summarisation produces attribution errors that survive review.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The mechanism:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; The paywall prevents full-text access at the moment of summarisation.&lt;/li&gt;
&lt;li&gt; Search snippets and abstracts get integrated into the audit’s narrative.&lt;/li&gt;
&lt;li&gt; The narrative reads coherently — fluent summaries that &lt;em&gt;sound&lt;/em&gt; like the source.&lt;/li&gt;
&lt;li&gt; Nothing in the audit’s own evidence chain catches the misattribution.&lt;/li&gt;
&lt;li&gt; The error propagates downstream into the artefacts the audit feeds (here: ADR-0017’s framing of Criterion C’s evidence weight; the §5.6 update).&lt;/li&gt;
&lt;li&gt; Only forcing full-text retrieval for any load-bearing claim catches it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is sharpest when AI-assisted research is in the loop. LLMs do exactly what LLMs do — they produce fluent summaries that integrate sources without preserving granular “this exact phrase came from THIS source” attribution. Without an explicit “verify the load-bearing phrase against the full text” gate, the failure mode is structurally hard to catch. Reading the LLM’s summary feels like reading the source; the prose discipline is indistinguishable. Only the bibliographic discipline is missing, and it’s invisible inside the summary.&lt;/p&gt;

&lt;p&gt;The defence is concrete: &lt;strong&gt;for any load-bearing claim, require full-text verification before that claim drives a decision. Codify this as a pre-committed gate, not a soft norm.&lt;/strong&gt; Soft norms aren’t enforced under time pressure. Gates are.&lt;/p&gt;

&lt;p&gt;The corrigendum strengthens the Path E routing decision rather than weakening it. The original audit reading was “Fieberg et al claims Sharpe 1.28 but acknowledges substantial trading costs and short-side alpha capture.” The corrected reading is “Fieberg et al claims gross Sharpe 1.28 (with 1.09 long-only, 1.43 most-liquid-25%-best-variant) but doesn’t address trading costs at all and doesn’t report profit factor.” The corrected reading makes the paper an even thinner foundation for the original Path D scope. The literature’s PF claim — the AND-clause of §14 — was never verified because the literature never reported it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What survives — the SIA harness and the framework artefacts
&lt;/h2&gt;

&lt;p&gt;Five attempted-or-prevented in-universe phases, four strategy classes empirically tested, one literature foundation empirically verified and found wanting, one corrigendum. The strategy attempts terminate. The methodology survives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The SIA harness&lt;/strong&gt; (&lt;a href="//./sia/"&gt;source&lt;/a&gt;, Apache 2.0) is the publishable artefact. Four mechanical screens that test whether a candidate signal adds information over a baseline, runnable on a laptop in seconds, with explicit pre-committed bar thresholds. Pre-hyperopt screening like this is published-ish in academic settings but operationally rare in retail tooling — most retail backtests either skip the question or answer it implicitly through hyperopt-then-OOS, which costs hours per signal hypothesis. SIA answers it in seconds, before hyperopt. The Phase 4 FCMFD case is the worked example of why that matters; the 16 unit tests + &lt;code&gt;mypy --strict&lt;/code&gt; cleanliness are the polish that lets the harness ship as installable Python.&lt;/p&gt;

&lt;p&gt;Installable via:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install -e git+https://github.com/hallengray/signal-independence-audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The methodology generalises beyond crypto. The four screens are signal-agnostic: any candidate signal-vs-baseline comparison fits the shape, on any asset class with a forward-return horizon and a confounder available for the information-split control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Other framework artefacts&lt;/strong&gt; are catalogued in ADR-0019’s “what we keep” section (&lt;a href="///signal-independence-audit/case-study/09-path-e-routing-adr-0019.html"&gt;details&lt;/a&gt;) but not extracted as separate standalone packages in this Phase 5e (could be later phases):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Deterministic backtest pipeline&lt;/strong&gt;: image-pinned Docker + sha256 data-manifest + cross-machine determinism check (laptop = cloud byte-for-byte). Applies to any Freqtrade-class strategy on any data.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Look-ahead-safe alignment patterns&lt;/strong&gt;: &lt;code&gt;merge_informative_pair(ffill=True)&lt;/code&gt; discipline (for OHLCV joins across timeframes) + a custom non-OHLCV joiner with &lt;code&gt;merge_asof(allow_exact_matches=False)&lt;/code&gt; for cadence-mixing signals like funding rates. Applies to any cadence-mixing problem.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Data manifest tooling&lt;/strong&gt;: schema-validated JSON manifests with sha256 verification + cross-platform LF parity handling.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Pre-committed audit methodology&lt;/strong&gt;: literature audit before strategy attempts, mechanical decision criteria pre-committed in scope docs, audit findings mapped to criteria mechanically rather than via judgment. The Path D escalation gates in ADR-0017 caught a 4–8 week false start — that’s the demonstrable value of the methodology over soft norms.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;ADR + post-mortem discipline&lt;/strong&gt;: numbered ADRs, post-mortem-on-kill pattern, cross-strategy comparison tables, references back to predecessor ADRs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The case-study directory (&lt;a href="///signal-independence-audit/case-study/00-readme.html"&gt;reading guide&lt;/a&gt;) is the receipt — ten curated documents covering every kill, the methodology, the audit, the corrigendum, and the routing. Three reading paths: chronological for the full trajectory, SIA-methodology-focused for readers who want the tool, negative-result-focused for readers who want the disconfirmation story.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this isn’t
&lt;/h2&gt;

&lt;p&gt;Path E is not “the project failed.” It is “the project produced a documented disconfirmation of the in-universe hypothesis, with mechanism, on a surface where the literature itself does not have positive evidence for retail-realistic strategies meeting the criteria.” Those are different statements with different implications.&lt;/p&gt;

&lt;p&gt;The §14 bar (Sharpe &amp;gt; 1.0 AND PF &amp;gt; 1.4) held throughout nineteen ADRs. It wasn’t relaxed when Phase 2 fell short, when Phase 3 fell shorter, when Phase 4 was SIA-killed, when Path C’s timeframe shift turned out flat, or when Path D Gate 1 found the foundational paper didn’t even report the metric. The bar transitions from “contested-theoretical” (a bar someone &lt;em&gt;might&lt;/em&gt; clear if conditions are right) to “documented-empirical” (a bar that wasn’t cleared on this surface across documented attempts under documented discipline). That’s a strictly more useful state for the bar to be in than “contested-theoretical.”&lt;/p&gt;

&lt;p&gt;If anyone wants to deploy capital on BTC/USDT + ETH/USDT 4-hour spot long-only in the future, the case-study directory is the receipt. It names the universe, the strategy classes attempted (price-only trend-following, mean reversion, funding-conditioned trend-following, timeframe-shifted trend-following), the bar, the literature evaluated, the corrigendum, and the routing. A future operator can decide whether to revisit the surface &lt;em&gt;with new evidence&lt;/em&gt; rather than re-running the same experiments blind.&lt;/p&gt;

&lt;p&gt;What this also isn’t: a claim that crypto is unsuitable for systematic trading in general, or that the §14 bar is the right bar for every operator, or that funding-rate signals are categorically uninformative. The negative result is scoped narrowly to a documented universe, a documented bar, and four documented strategy attempts. Outside that scope, the artefact this post ships — the SIA harness — is precisely the tool you’d want to apply to whatever signal hypothesis is in scope for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Footnotes / references
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;The case study&lt;/strong&gt;: &lt;a href="///signal-independence-audit/case-study/00-readme.html"&gt;&lt;code&gt;./case-study/00-readme.md&lt;/code&gt;&lt;/a&gt; — reading guide for the ten curated documents. The audit corrigendum is at &lt;a href="///signal-independence-audit/case-study/10-audit-corrigendum.html"&gt;&lt;code&gt;./case-study/10-audit-corrigendum.md&lt;/code&gt;&lt;/a&gt; as a self-contained teaching artefact about AI-assisted research failure modes.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The SIA harness&lt;/strong&gt;: this repo’s &lt;a href="//./sia/"&gt;&lt;code&gt;./sia/&lt;/code&gt;&lt;/a&gt; directory. Apache 2.0. Python 3.11+. Pandas + numpy hard deps; pytest + mypy as test extras; jupyter + matplotlib as notebook extras. Install: &lt;code&gt;pip install -e git+https://github.com/hallengray/signal-independence-audit&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The source project’s repo&lt;/strong&gt;: private. Scope B of the Phase 5e shipping decision intentionally excludes the strategy implementation code, hyperopt outputs, raw backtest data, and ops playbooks from the public artefact. The case-study directory is the curated evidence chain for the narrative above; the full source project is not part of what’s published.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Maintenance posture&lt;/strong&gt;: this repository is published as a finished research project, not an actively-maintained tool. Issues are welcome and may be responded to selectively; there is no response time commitment. PRs may not be reviewed. Forks are welcome.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Author&lt;/strong&gt;: Femi Adedayo — &lt;a href="mailto:hallengray@gmail.com"&gt;hallengray@gmail.com&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;End of post.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/hallengray/signal-independence-audit" rel="noopener noreferrer"&gt;signal-independence-audit&lt;/a&gt; is maintained by &lt;a href="https://github.com/hallengray" rel="noopener noreferrer"&gt;hallengray&lt;/a&gt;. This page was generated by &lt;a href="https://pages.github.com" rel="noopener noreferrer"&gt;GitHub Pages&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>algotrading</category>
      <category>productivity</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Why Audit Is the Missing Layer in Every Healthcare RAG System</title>
      <dc:creator>Oluwafemi Adedayo</dc:creator>
      <pubDate>Wed, 15 Apr 2026 10:20:15 +0000</pubDate>
      <link>https://forem.com/hallengray/why-audit-is-the-missing-layer-in-every-healthcare-rag-system-34p5</link>
      <guid>https://forem.com/hallengray/why-audit-is-the-missing-layer-in-every-healthcare-rag-system-34p5</guid>
      <description>&lt;p&gt;I've reviewed a lot of healthcare AI builds over the last year. Clinicians love the demos. CTOs love the architecture diagrams. The compliance team gets invited to the meeting in week six, usually right after someone asks "what happens if this is wrong?"&lt;/p&gt;

&lt;p&gt;That question is almost always asked too late.&lt;/p&gt;

&lt;p&gt;In non-regulated industries, a RAG system that hallucinates occasionally is an embarrassment. In healthcare, it is a liability. The difference between those two outcomes isn't the model. It isn't even the retrieval pipeline. It's whether you built audit in from the start, or bolted it on when someone demanded it.&lt;/p&gt;

&lt;p&gt;This post is about why audit is the highest-leverage layer in a healthcare RAG system, what it actually means to build it properly, and why most teams skip it until the regulator asks.&lt;/p&gt;




&lt;h2&gt;
  
  
  The stakes are different in healthcare
&lt;/h2&gt;

&lt;p&gt;In my last post I wrote about the RAG Maturity Model: five levels from naive demo to enterprise-grade system. RMM-3 (Better Trust) and RMM-5 (Enterprise) both include elements of evaluation and drift detection. In healthcare, those aren't maturity features you graduate to. They are table stakes before you go live.&lt;/p&gt;

&lt;p&gt;Here's why. A RAG system retrieving from clinical guidelines, drug databases, or patient records is generating outputs that influence clinical decisions. When it retrieves the wrong passage and the LLM composes a confident answer citing it, a clinician may act on that. The error chain is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Wrong retrieval → confident hallucination → clinical decision → patient outcome&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every step in that chain is invisible unless you've built the instrumentation to catch it. Audit is that instrumentation.&lt;/p&gt;




&lt;h2&gt;
  
  
  What "audit" actually means for a healthcare RAG system
&lt;/h2&gt;

&lt;p&gt;Most teams hear "audit" and think logging: drop a timestamp and a query hash into a table and call it done. That is not audit. That is compliance theater.&lt;/p&gt;

&lt;p&gt;Real audit in a healthcare RAG context has four layers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Input audit&lt;/strong&gt;&lt;br&gt;
Log the exact query, the user who sent it, the timestamp, and the session context. If the system is connected to a patient record, log which record. You need to be able to answer: &lt;em&gt;who asked what, about whom, and when.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Retrieval audit&lt;/strong&gt;&lt;br&gt;
Log every chunk retrieved, its document source, its version, and its retrieval score. This is the layer most teams omit, and it is the most important one. If a clinician later questions a recommendation, you need to replay exactly what the system saw, not just what it said. The EU AI Act and FDA transparency guidelines both require this capability for clinical AI systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Generation audit&lt;/strong&gt;&lt;br&gt;
Log the prompt sent to the LLM (including injected context), the raw model output, and any post-processing applied. This gives you faithfulness traceability. You can verify whether the generated answer actually derived from the retrieved context or whether the model improvised.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Decision audit&lt;/strong&gt;&lt;br&gt;
In high-stakes workflows, log whether the output was acted on, by whom, and what the downstream outcome was. This is the layer that enables feedback loops and model improvement. It's also the layer that protects the organisation when outcomes are questioned.&lt;/p&gt;


&lt;h2&gt;
  
  
  HIPAA, the EU AI Act, and why "best effort" isn't a defence
&lt;/h2&gt;

&lt;p&gt;Healthcare RAG systems operating in the US must comply with HIPAA. That means comprehensive audit trails logging every data access, every query, and every response. It means Business Associate Agreements with every third-party vendor in your pipeline: your vector database provider, your embedding API, your LLM vendor. Each of those is a point of potential PHI exposure, and each requires a contractual audit trail.&lt;/p&gt;

&lt;p&gt;In the EU, the AI Act classifies clinical decision-support systems as high-risk AI. High-risk AI systems are required to maintain logs sufficient for post-market surveillance and for regulators to audit the system's operation retrospectively. "We were doing our best" is not a defence. The log either exists or it doesn't.&lt;/p&gt;

&lt;p&gt;The practical consequence: if your healthcare RAG system doesn't have tamper-evident, timestamped, source-linked audit records for every inference, you are not compliant under either regime, regardless of how good your retrieval precision is.&lt;/p&gt;


&lt;h2&gt;
  
  
  The thing audit reveals that nothing else does
&lt;/h2&gt;

&lt;p&gt;Here is what audit gives you that evaluation metrics alone don't: it tells you what the system is actually being used for.&lt;/p&gt;

&lt;p&gt;Golden sets tell you how the system performs on questions you anticipated. Audit logs tell you how the system performs on questions you didn't.&lt;/p&gt;

&lt;p&gt;In a healthcare deployment I reviewed, the golden set showed 87% faithfulness on clinical guideline queries. A strong result. The audit logs, when finally reviewed six weeks post-launch, showed that 23% of real queries were about medication dosages, a category almost entirely absent from the golden set. Faithfulness on that category, measured retrospectively, was 61%.&lt;/p&gt;

&lt;p&gt;The golden set was honest. The system was fine on the questions the team had anticipated. But the audit revealed a gap that could have caused real harm. It caught the problem before anything serious happened, only because audit was actually running.&lt;/p&gt;

&lt;p&gt;That is what audit is for. Not a compliance checkbox. Not a liability shield. Operational visibility into the real distribution of your system's use.&lt;/p&gt;


&lt;h2&gt;
  
  
  The architecture of an auditable healthcare RAG system
&lt;/h2&gt;

&lt;p&gt;Building audit in from the start looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[User Query]
     ↓
[Input audit log] → tamper-evident store (append-only)
     ↓
[Retrieval pipeline] → [Retrieval audit log: chunks, sources, versions, scores]
     ↓
[LLM generation] → [Generation audit log: prompt, raw output, model version]
     ↓
[PII scan + output filter]
     ↓
[Response to user]
     ↓
[Faithfulness score (async)] → appended to generation audit record
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key design decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Append-only audit store.&lt;/strong&gt; Write-only for the application, with no update or delete path. This is what makes the record tamper-evident.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version-pinned retrieval.&lt;/strong&gt; Your knowledge base must be versioned. The audit record must reference the specific version of each source document retrieved, not just the document name. If the guideline changes next month, you need to know which version was retrieved for each past query.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Async faithfulness scoring.&lt;/strong&gt; Don't put faithfulness scoring in the hot path if you can avoid it. Score asynchronously and append the result to the generation audit record. This gives you the operational data without adding latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PII redaction before logging.&lt;/strong&gt; Log the query, but run PII scanning before writing it to the audit store. You don't want patient names or SSNs in the audit log in cleartext, because that defeats the purpose.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why teams skip this and what happens when they do
&lt;/h2&gt;

&lt;p&gt;The honest answer is that audit feels like overhead when you're trying to ship. The vector store is working. The LLM is producing answers that look right. The demo is good. Audit adds latency, complexity, and cost with no visible benefit on the day you deploy.&lt;/p&gt;

&lt;p&gt;The benefit shows up on the day something goes wrong.&lt;/p&gt;

&lt;p&gt;Without audit, a healthcare organisation that receives a regulatory inquiry or a patient complaint has no way to replay what the system actually did. They cannot demonstrate that retrieval was grounded in approved sources. They cannot prove that PII was handled correctly. They cannot show that the answer given to the clinician derived from evidence rather than from model hallucination.&lt;/p&gt;

&lt;p&gt;At that point, "we were logging queries but not the retrieved context" is the most expensive sentence in AI development.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mapping audit to the RAG Maturity Model
&lt;/h2&gt;

&lt;p&gt;If you've read the RAG Maturity Model post, here's where audit sits:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;RMM Level&lt;/th&gt;
&lt;th&gt;Audit Requirement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RMM-0 to RMM-2&lt;/td&gt;
&lt;td&gt;None mandated, but instrument for it now&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RMM-3 (Better Trust)&lt;/td&gt;
&lt;td&gt;Generation audit and faithfulness scoring active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RMM-4 (Better Workflow)&lt;/td&gt;
&lt;td&gt;Full four-layer audit running, traces queryable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RMM-5 (Enterprise)&lt;/td&gt;
&lt;td&gt;Tamper-evident store, version-pinned retrieval, regulatory export capability&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For non-healthcare RAG, audit is an RMM-5 concern. For healthcare RAG, it's an RMM-3 requirement you cannot skip. The maturity model is a starting point. In regulated domains, you apply your own floor.&lt;/p&gt;




&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Audit is not the last thing you add to a healthcare RAG system. It is the second thing: after you confirm the retrieval pipeline actually works, and before you let anyone act on an answer.&lt;/p&gt;

&lt;p&gt;Build the append-only log. Version your knowledge base. Score faithfulness asynchronously. Run PII scanning before you write anything to storage. Make the retrieval record replayable.&lt;/p&gt;

&lt;p&gt;The regulator who asks "what did your system retrieve before it gave that recommendation?" deserves an answer you can produce in under an hour. So does the clinician. So does the patient.&lt;/p&gt;

&lt;p&gt;If you're building or evaluating a healthcare RAG system right now, which of the four audit layers is the weakest in your current stack? Drop it in the comments.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Femi Adedayo&lt;/strong&gt; builds AI automation systems at Hgray AI and writes about RAG, LLMs, and production AI systems. The RAG Maturity Model and RAG-Forge CLI referenced in this post are available on GitHub.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>healthcare</category>
      <category>llm</category>
    </item>
    <item>
      <title>The 5 Levels of RAG Maturity: How to Know When Your RAG Is Actually Production-Ready</title>
      <dc:creator>Oluwafemi Adedayo</dc:creator>
      <pubDate>Tue, 14 Apr 2026 17:17:11 +0000</pubDate>
      <link>https://forem.com/hallengray/the-5-levels-of-rag-maturity-how-to-know-when-your-rag-is-actually-production-ready-33o5</link>
      <guid>https://forem.com/hallengray/the-5-levels-of-rag-maturity-how-to-know-when-your-rag-is-actually-production-ready-33o5</guid>
      <description>&lt;p&gt;I've watched a lot of RAG projects ship in the last eighteen months. They all follow the same arc.&lt;/p&gt;

&lt;p&gt;Week 1: somebody wires up a vector store, drops in a few PDFs, and demos it to the team. Everyone is impressed. The CEO forwards the Loom video to the board.&lt;/p&gt;

&lt;p&gt;Week 4: the first real users start asking questions. The answers are... fine? Sometimes. Nobody can quite tell.&lt;/p&gt;

&lt;p&gt;Week 8: someone asks "is this actually working?" and the room goes quiet. There's a notebook somewhere with eleven test questions in it. It hasn't been run since week 2.&lt;/p&gt;

&lt;p&gt;Week 12: the project is either quietly shelved or it's still in production and nobody trusts it.&lt;/p&gt;

&lt;p&gt;The problem isn't that RAG is hard to build. &lt;strong&gt;The problem is that RAG is hard to &lt;em&gt;evaluate&lt;/em&gt;, and without evaluation you can't tell the difference between "a demo that sometimes works" and "a system you can put in front of customers."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This post is about a framework I've been using to close that gap: a 0-to-5 maturity model for RAG systems, with concrete exit criteria at each level. Call it RMM — the RAG Maturity Model. By the end you should be able to look at any RAG system, including your own, and answer the question &lt;em&gt;"what level is this, and what's the next thing I need to fix?"&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "production-ready" is a useless phrase
&lt;/h2&gt;

&lt;p&gt;The phrase "production-ready RAG" gets thrown around like everyone agrees what it means. Nobody does.&lt;/p&gt;

&lt;p&gt;For one team it means "the API returns 200s." For another it means "no hallucinations on the golden set." For a regulated team it means "we can prove to an auditor that PII never leaves the perimeter." These are wildly different bars, and conflating them is how projects end up shipping at one bar and getting evaluated at another.&lt;/p&gt;

&lt;p&gt;What we need is the same thing the rest of software engineering figured out years ago: &lt;strong&gt;a maturity model.&lt;/strong&gt; CMMI did it for process. The Well-Architected Framework did it for cloud. DORA did it for delivery. Each of these works because it gives you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A small number of named levels&lt;/li&gt;
&lt;li&gt;Concrete, measurable exit criteria for each level&lt;/li&gt;
&lt;li&gt;A clear ordering — you can't skip rungs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's what that looks like for RAG.&lt;/p&gt;

&lt;h2&gt;
  
  
  The RAG Maturity Model
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Exit Criteria&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RMM-0&lt;/td&gt;
&lt;td&gt;Naive&lt;/td&gt;
&lt;td&gt;Basic vector search works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RMM-1&lt;/td&gt;
&lt;td&gt;Better Recall&lt;/td&gt;
&lt;td&gt;Hybrid search, Recall@5 &amp;gt; 70%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RMM-2&lt;/td&gt;
&lt;td&gt;Better Precision&lt;/td&gt;
&lt;td&gt;Reranker active, nDCG@10 +10%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RMM-3&lt;/td&gt;
&lt;td&gt;Better Trust&lt;/td&gt;
&lt;td&gt;Guardrails, faithfulness &amp;gt; 85%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RMM-4&lt;/td&gt;
&lt;td&gt;Better Workflow&lt;/td&gt;
&lt;td&gt;Caching, P95 &amp;lt; 4s, cost tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RMM-5&lt;/td&gt;
&lt;td&gt;Enterprise&lt;/td&gt;
&lt;td&gt;Drift detection, CI/CD gates, adversarial tests&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let's walk through each level, what it actually feels like, and how you know you're done.&lt;/p&gt;

&lt;h3&gt;
  
  
  RMM-0: Naive
&lt;/h3&gt;

&lt;p&gt;This is the demo. You chunked some docs, embedded them with &lt;code&gt;text-embedding-3-small&lt;/code&gt;, stuffed them into a vector store, and built a retrieval loop that grabs the top-k and pipes them into a prompt. It works on the questions you tested it with. It feels like magic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exit criteria:&lt;/strong&gt; You can answer a question end-to-end and the answer references the right document at least &lt;em&gt;some&lt;/em&gt; of the time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why teams get stuck here:&lt;/strong&gt; They don't. They get stuck &lt;em&gt;moving past&lt;/em&gt; here, because RMM-0 looks so close to working that nobody believes it's only level zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lie RMM-0 tells you:&lt;/strong&gt; that retrieval quality is mostly solved by picking a good embedding model. It isn't.&lt;/p&gt;

&lt;h3&gt;
  
  
  RMM-1: Better Recall
&lt;/h3&gt;

&lt;p&gt;The first thing you discover at RMM-0 is that vector search misses obvious matches. Someone asks a question with a specific product code or acronym, and the right document is sitting in your index, but cosine similarity ranks five irrelevant docs above it because the query and the doc don't share enough semantic surface area.&lt;/p&gt;

&lt;p&gt;The fix is &lt;strong&gt;hybrid search&lt;/strong&gt; — combine dense (vector) and sparse (BM25 or SPLADE) retrieval, then merge the results with reciprocal rank fusion or a similar scheme. Sparse retrieval catches the literal-match cases that embeddings fumble.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exit criteria:&lt;/strong&gt; Recall@5 above 70% on a golden set of at least 50 questions. (Recall@5 = "of the questions where the right answer exists in your corpus, how often is the relevant doc in the top 5 retrieved chunks.")&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The honest signal:&lt;/strong&gt; if you can't measure Recall@5, you're still at RMM-0 regardless of how fancy your retrieval pipeline looks. Measurement is the gate, not the technique.&lt;/p&gt;

&lt;h3&gt;
  
  
  RMM-2: Better Precision
&lt;/h3&gt;

&lt;p&gt;Now retrieval finds the right doc, but it also finds four wrong ones, and the LLM gets confused or hedges. This is where you add a &lt;strong&gt;reranker&lt;/strong&gt;: a second-stage model (Cohere Rerank, BGE Reranker, Voyage, etc.) that takes your top-50 retrieved chunks and reorders them by actual relevance to the query.&lt;/p&gt;

&lt;p&gt;The lift from a good reranker is usually larger than the lift from picking a better embedding model, and it's much cheaper to iterate on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exit criteria:&lt;/strong&gt; nDCG@10 improves by at least 10% versus the RMM-1 baseline on the same golden set. (nDCG@10 captures &lt;em&gt;ordering&lt;/em&gt; quality, not just whether the right doc is in the set.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common mistake:&lt;/strong&gt; skipping straight from RMM-0 to a reranker without ever measuring recall. If your retrieval has 40% recall, a reranker can't save you — it can only reorder what it's given.&lt;/p&gt;

&lt;h3&gt;
  
  
  RMM-3: Better Trust
&lt;/h3&gt;

&lt;p&gt;At this point retrieval is solid, but the LLM still occasionally makes things up. It cites sources that don't say what it claims they say. It answers questions it shouldn't answer. It leaks PII it should redact.&lt;/p&gt;

&lt;p&gt;RMM-3 is about the &lt;em&gt;answer&lt;/em&gt;, not the retrieval. You add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faithfulness scoring&lt;/strong&gt; — does the generated answer actually follow from the retrieved context, or is the model improvising?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guardrails&lt;/strong&gt; — input/output filters for prompt injection, jailbreaks, and policy violations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PII scanning&lt;/strong&gt; — detect and redact names, emails, SSNs, etc., on the way in &lt;em&gt;and&lt;/em&gt; the way out.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exit criteria:&lt;/strong&gt; Faithfulness &amp;gt; 85% on the golden set, plus guardrails and PII scans wired into the request path with measurable block rates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this level matters more than the previous two:&lt;/strong&gt; RMM-1 and RMM-2 fail visibly — users see bad answers and complain. RMM-3 failures are &lt;em&gt;invisible&lt;/em&gt;. The model confidently states something false and the user believes it. These are the failures that destroy trust and end projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  RMM-4: Better Workflow
&lt;/h3&gt;

&lt;p&gt;Your RAG system answers correctly. Now it has to do that &lt;em&gt;fast&lt;/em&gt;, &lt;em&gt;cheaply&lt;/em&gt;, and &lt;em&gt;predictably&lt;/em&gt;, every day, for a year, while the team keeps shipping changes. RMM-4 is the operational layer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Caching&lt;/strong&gt; at the embedding, retrieval, and generation tiers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;P95 latency under 4 seconds&lt;/strong&gt; end-to-end&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost tracking per query&lt;/strong&gt; so the finance team doesn't ambush you in month three&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distributed tracing&lt;/strong&gt; (OpenTelemetry, Langfuse) so you can debug a bad answer two weeks after it happened&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exit criteria:&lt;/strong&gt; P95 &amp;lt; 4s, cost-per-query measured and trending in a dashboard, and a trace for any query you can name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The insight:&lt;/strong&gt; at RMM-4 you stop thinking about RAG as a model problem and start thinking about it as a systems problem. The hardest bugs are no longer prompt bugs; they're cache-invalidation bugs.&lt;/p&gt;

&lt;h3&gt;
  
  
  RMM-5: Enterprise
&lt;/h3&gt;

&lt;p&gt;The final level is the one most teams never need, but the ones who do can't ship without it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Drift detection&lt;/strong&gt; — alerts when the embedding distribution, query distribution, or answer quality shifts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD gates&lt;/strong&gt; — every PR runs the full audit and is blocked if faithfulness, recall, or precision regress beyond a threshold&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adversarial test suites&lt;/strong&gt; — red-team prompts, prompt-injection corpora, and known-bad inputs run on every release&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exit criteria:&lt;/strong&gt; A merge to main triggers an evaluation run, the run posts to a dashboard, and a regression beyond a configured threshold blocks the merge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this really gives you:&lt;/strong&gt; the ability to keep shipping changes to a RAG system without breaking it. Up to RMM-4 you're building. At RMM-5 you're maintaining, and maintenance is where most RAG projects die.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use the model
&lt;/h2&gt;

&lt;p&gt;Three rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Don't skip levels.&lt;/strong&gt; RMM is ordered for a reason. A reranker on top of broken recall is wasted compute. Faithfulness scoring on top of bad retrieval just measures how confidently your model is wrong. Drift detection on a system with no golden set has nothing to drift &lt;em&gt;from&lt;/em&gt;. The exit criteria are gates, not suggestions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Measure before you optimize.&lt;/strong&gt; The biggest difference between teams stuck at RMM-0 and teams climbing the model is whether they have a golden set. Fifty questions with known-good answers is enough to start. You can grow it later. You cannot skip it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Score honestly.&lt;/strong&gt; It is much more useful to know you are an honest RMM-1 than to claim you are an aspirational RMM-3. The model only works if the level is verified, not asserted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying this on your own system
&lt;/h2&gt;

&lt;p&gt;I built a CLI called RAG-Forge that implements this model end-to-end. It scaffolds a RAG pipeline, runs evaluation as a CI gate, and scores any audit report against the RMM levels above. It's MIT-licensed and works against your existing pipeline — you don't need to rewrite anything to score it.&lt;/p&gt;

&lt;p&gt;The scoring command takes a JSON audit report and tells you which level you're at and what's blocking the next one:&lt;/p&gt;

&lt;p&gt;npm install -g @rag-forge/cli&lt;/p&gt;

&lt;p&gt;rag-forge init basic --directory my-rag&lt;br&gt;
cd my-rag&lt;br&gt;
rag-forge index --source ./docs&lt;br&gt;
rag-forge audit --golden-set eval/golden_set.json&lt;br&gt;
rag-forge assess --audit-report reports/audit-report.json&lt;/p&gt;

&lt;p&gt;The assess command output looks roughly like this:&lt;/p&gt;

&lt;p&gt;Current level: RMM-2 (Better Precision)&lt;br&gt;
  ✓ Recall@5: 78% (target: &amp;gt;70%)&lt;br&gt;
  ✓ nDCG@10 lift: +14% (target: +10%)&lt;br&gt;
  ✗ Faithfulness: 71% (target: &amp;gt;85%) — blocking RMM-3&lt;/p&gt;

&lt;p&gt;Next action: faithfulness is below RMM-3 threshold. Add output&lt;br&gt;
verification or a guardrails layer before claiming Better Trust.&lt;/p&gt;

&lt;p&gt;That's the whole point of the model — turning "is our RAG good?" into "we are at level 2 and the specific thing blocking level 3 is faithfulness below 85%." You can put that on a roadmap. You can argue about it in a PR review. You can show it to a non-technical stakeholder and they will understand it.&lt;/p&gt;

&lt;p&gt;Even if you don't use the tool, you can use the framework. Print the table at the top of this post, score your own system honestly, and you will already know more about where your RAG stands than 90% of teams shipping RAG today.&lt;/p&gt;

&lt;p&gt;Where the model breaks down&lt;br&gt;
No maturity model survives contact with reality unmodified, so here are the cases where RMM bends.&lt;/p&gt;

&lt;p&gt;Agentic RAG with multi-hop retrieval doesn't fit cleanly into per-query &lt;a href="mailto:Recall@5"&gt;Recall@5&lt;/a&gt;. You usually need to score each hop separately and roll up.&lt;br&gt;
Conversational RAG (long chat history) needs a faithfulness metric that accounts for the running context, not just the latest retrieval.&lt;br&gt;
Domain-specific RAG (legal, medical, code) often needs custom precision targets — 85% faithfulness is too low for a legal brief and too high for a brainstorming assistant.&lt;br&gt;
The model is a starting point, not a constitution. If you find a place where it doesn't fit your system, the right move is to write down why and what you replaced it with — that's still better than no framework at all.&lt;/p&gt;

&lt;p&gt;The takeaway&lt;br&gt;
If you remember nothing else from this post: build a golden set, score yourself honestly, and stop shipping RAG without knowing what level you're at.&lt;/p&gt;

&lt;p&gt;The tools to do this are free. The framework is published. The reason most RAG projects don't have evaluation isn't that it's hard — it's that nobody insists on it. Be the person who insists.&lt;/p&gt;

&lt;p&gt;If you try the model on your own system, I'd love to hear where it broke down for you. Drop a comment with your level and what's blocking the next one, or open a discussion in the RAG-Forge repo. The model gets better the more systems we score against it.&lt;/p&gt;

</description>
      <category>rag</category>
      <category>llm</category>
      <category>ai</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
