<?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>DEV Community: Tatiana Radchenko 🇩🇰</title>
    <description>The latest articles on DEV Community by Tatiana Radchenko 🇩🇰 (@taniacoder).</description>
    <link>https://dev.to/taniacoder</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3999372%2F7af2116b-b834-4900-9ae3-ee2e2d294004.jpg</url>
      <title>DEV Community: Tatiana Radchenko 🇩🇰</title>
      <link>https://dev.to/taniacoder</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/taniacoder"/>
    <language>en</language>
    <item>
      <title>Your LLM didn't get worse. It changed — and nobody told you.</title>
      <dc:creator>Tatiana Radchenko 🇩🇰</dc:creator>
      <pubDate>Thu, 02 Jul 2026 11:22:37 +0000</pubDate>
      <link>https://dev.to/taniacoder/your-llm-didnt-get-worse-it-changed-and-nobody-told-you-4ecl</link>
      <guid>https://dev.to/taniacoder/your-llm-didnt-get-worse-it-changed-and-nobody-told-you-4ecl</guid>
      <description>&lt;p&gt;It's 2am. Your JSON parse errors are up 12%. Latency: normal. Uptime: 100%. Your prompt didn't change. Your code didn't change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is it you, or did the model silently change underneath you?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every team building on third-party LLM APIs eventually hits this moment. And the frustrating part: your monitoring stack can't answer the question. Latency, error rates, uptime — all green. The thing that changed is &lt;em&gt;behavior&lt;/em&gt;, and behavior emits no infrastructure signal.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://github.com/Tania-coder/SEISMOGRAPH" rel="noopener noreferrer"&gt;SEISMOGRAPH&lt;/a&gt; — an open-source, privacy-preserving early-warning network for exactly this failure mode. This post covers what silent drift is, how to detect it without leaking a single prompt, and a reproducible backtest where the detector flags a real, publicly documented provider incident &lt;strong&gt;38 days before the official postmortem&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "silent drift" actually is
&lt;/h2&gt;

&lt;p&gt;Provider APIs do not broadcast behavioral changes. An endpoint that returns 200 can still start producing subtly different outputs: degraded JSON fidelity, shifted response-length distributions, changed reasoning patterns.&lt;/p&gt;

&lt;p&gt;This isn't hypothetical. In September 2025, Anthropic published a detailed postmortem describing &lt;strong&gt;three infrastructure bugs&lt;/strong&gt; that silently degraded Claude output quality across August–September 2025. The most long-lived one: a context-window routing error, introduced on August 5, that misrouted a fraction of Claude Sonnet 4 requests to servers configured for the 1M-token context window. It started at ~0.8% of traffic. On August 29 a load-balancer change escalated it to ~16%. The public postmortem landed on September 17.&lt;/p&gt;

&lt;p&gt;Note what this incident was &lt;em&gt;not&lt;/em&gt;: it was not a model update, and not intentional degradation — Anthropic was explicit about that. It was infrastructure. Which is precisely the point: &lt;strong&gt;the provider's own uptime and latency dashboards stayed green the whole time.&lt;/strong&gt; The signal only existed at the semantic layer.&lt;/p&gt;

&lt;p&gt;So the question I wanted to answer in Phase 0 of the project: would a lightweight behavioral canary have caught it, and how early?&lt;/p&gt;

&lt;h2&gt;
  
  
  The detection idea: canaries + change-point statistics
&lt;/h2&gt;

&lt;p&gt;The approach is deliberately boring:&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;canary suite&lt;/strong&gt; — a fixed, content-addressed set of prompts (≤200, temperature 0) — runs on a schedule against the API you depend on. Deterministic prompts against a deterministic-ish endpoint give you a behavioral baseline. Every suite version is immutably hash-addressed, so a baseline can never silently mutate either.&lt;/p&gt;

&lt;p&gt;Each response is reduced to &lt;strong&gt;distributional features&lt;/strong&gt;: JSON parse success rate, output length, result counts. Not the text — the shape of the behavior.&lt;/p&gt;

&lt;p&gt;Those features feed a &lt;strong&gt;Page-CUSUM change-point detector&lt;/strong&gt; per &lt;code&gt;(model_tuple, metric_name)&lt;/code&gt; stream:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;S+(n) = max(0, S+(n-1) + z(n) - k)    # upward shifts
S-(n) = max(0, S-(n-1) - z(n) - k)    # downward shifts
Alert when S+ or S- &amp;gt; h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CUSUM accumulates small standardized deviations from a learned baseline (mu0, sigma0 from the first 30 observations). That's what makes it good at this job: a 0.8% fault doesn't produce a visible spike on any single day, but it produces a &lt;em&gt;persistent small negative bias&lt;/em&gt; — and CUSUM integrates exactly that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The privacy boundary (the part that makes federation possible)
&lt;/h2&gt;

&lt;p&gt;One org's canary is a smoke detector. A &lt;em&gt;network&lt;/em&gt; of canaries across independent orgs is an early-warning system — if orgs are willing to participate. They're only willing if participation leaks nothing.&lt;/p&gt;

&lt;p&gt;So the probe enforces a hard perimeter. &lt;strong&gt;Raw prompts and outputs never leave your infrastructure.&lt;/strong&gt; What gets transmitted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SHA-256 hash of each response (not the response)&lt;/li&gt;
&lt;li&gt;DP-noised aggregates: &lt;code&gt;json_success_rate&lt;/code&gt;, &lt;code&gt;avg_output_length&lt;/code&gt; (Laplace mechanism, ε = 2.0 per flush)&lt;/li&gt;
&lt;li&gt;the content-addressed canary suite version hash&lt;/li&gt;
&lt;li&gt;a pseudonymous Ed25519 public key — batches are signed, org identity is not disclosed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the alerting side has a matching rule: &lt;strong&gt;a single organization's signal is never promoted to a public alert.&lt;/strong&gt; A quorum scorer requires ≥ 2 independent orgs to agree on the same &lt;code&gt;(model, metric)&lt;/code&gt; drift before anything goes public. That one gate filters probe bugs, network hiccups, and Sybil injection in a single move — and it means your private fleet data stays private fleet data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The proof: a reproducible backtest
&lt;/h2&gt;

&lt;p&gt;Here's the Phase 0 experiment (seeded, fully reproducible — &lt;code&gt;SEED=42&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;Reconstruct the Aug–Sep 2025 incident timeline from the public postmortem as synthetic daily probe traffic:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Dates&lt;/th&gt;
&lt;th&gt;json_success_rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Baseline&lt;/td&gt;
&lt;td&gt;Jul 1 – Aug 4&lt;/td&gt;
&lt;td&gt;0.990&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phase 1 (0.8% misrouting)&lt;/td&gt;
&lt;td&gt;Aug 5 – Aug 28&lt;/td&gt;
&lt;td&gt;0.982&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phase 2 (16% misrouting)&lt;/td&gt;
&lt;td&gt;Aug 29 – Sep 17&lt;/td&gt;
&lt;td&gt;0.840&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Feed it through the exact CUSUM detector from the codebase (&lt;code&gt;h=5.0, k=0.5&lt;/code&gt;). Watch when it fires:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csvs"&gt;&lt;code&gt;&lt;span class="k"&gt;Date&lt;/span&gt;        &lt;span class="k"&gt;Phase&lt;/span&gt;          &lt;span class="k"&gt;rate&lt;/span&gt;    &lt;span class="k"&gt;S&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;      &lt;span class="k"&gt;note&lt;/span&gt;
&lt;span class="ld"&gt;2025-08-03&lt;/span&gt;  &lt;span class="k"&gt;Stable&lt;/span&gt;         &lt;span class="mf"&gt;0.9902&lt;/span&gt;  &lt;span class="mf"&gt;0.000&lt;/span&gt;
&lt;span class="ld"&gt;2025-08-04&lt;/span&gt;  &lt;span class="k"&gt;Stable&lt;/span&gt;         &lt;span class="mf"&gt;0.9881&lt;/span&gt;  &lt;span class="mf"&gt;0.000&lt;/span&gt;
&lt;span class="ld"&gt;2025-08-05&lt;/span&gt;  &lt;span class="k"&gt;Phase&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="mf"&gt;0.9855&lt;/span&gt;  &lt;span class="mf"&gt;0.598&lt;/span&gt;   &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="k"&gt;bug&lt;/span&gt; &lt;span class="k"&gt;introduced&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;
&lt;span class="ld"&gt;2025-08-06&lt;/span&gt;  &lt;span class="k"&gt;Phase&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="mf"&gt;0.9857&lt;/span&gt;  &lt;span class="mf"&gt;1.142&lt;/span&gt;
&lt;span class="ld"&gt;2025-08-07&lt;/span&gt;  &lt;span class="k"&gt;Phase&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="mf"&gt;0.9786&lt;/span&gt;  &lt;span class="mf"&gt;3.309&lt;/span&gt;
&lt;span class="ld"&gt;2025-08-08&lt;/span&gt;  &lt;span class="k"&gt;Phase&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="mf"&gt;0.9877&lt;/span&gt;  &lt;span class="mf"&gt;3.396&lt;/span&gt;
&lt;span class="ld"&gt;2025-08-09&lt;/span&gt;  &lt;span class="k"&gt;Phase&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="mf"&gt;0.9816&lt;/span&gt;  &lt;span class="mf"&gt;4.889&lt;/span&gt;
&lt;span class="ld"&gt;2025-08-10&lt;/span&gt;  &lt;span class="k"&gt;Phase&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="mf"&gt;0.9777&lt;/span&gt;  &lt;span class="mf"&gt;7.278&lt;/span&gt;   &lt;span class="err"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="k"&gt;ALERT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First alert: &lt;strong&gt;August 10&lt;/strong&gt; — five days into the subtle 0.8% window.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;19 days before&lt;/strong&gt; the escalation made the problem visible to users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;38 days before&lt;/strong&gt; the official postmortem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can re-run this yourself: &lt;code&gt;python scripts/anthropic_backtest.py&lt;/code&gt; in the repo regenerates the full report and asserts the result.&lt;/p&gt;

&lt;h3&gt;
  
  
  What this does and doesn't prove
&lt;/h3&gt;

&lt;p&gt;I want to be precise here, because this is where drift-detection claims usually get slippery:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;This is a backtest on synthetic data&lt;/strong&gt;, not a live catch. The timeline and magnitudes come from the public postmortem; real probe noise may differ, and real lead time could be shorter or longer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's a single simulated observer.&lt;/strong&gt; A real public alert requires the ≥2-org quorum.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The simulation omits live DP noise&lt;/strong&gt;, which adds variance and may delay an alert by a few days.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What it &lt;em&gt;does&lt;/em&gt; prove: a metric as cheap as "did the JSON parse?" — measured consistently, at temperature 0, against a fixed suite — carries enough signal to surface a 0.8%-of-traffic infrastructure fault weeks before it becomes user-visible. Conventional monitoring is structurally blind to this class of failure. A $0.10/day canary is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture in one pass
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;your infra                          public
┌─────────────────────────┐
│ probe SDK               │
│  canary suite (≤200,    │
│  temp 0, hash-addressed)│
│  → features + DP noise  │
│  → Ed25519 signature    │
└──────────┬──────────────┘
           ▼
   ingestion gateway (FastAPI)
   schema validation, signature check
           ▼
   CUSUM per (model, metric)  →  local alert (private)
           ▼
   quorum scorer (≥2 orgs)    →  public drift alert
           ▼
   "model weather" dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stack: Python probe SDK (OpenTelemetry GenAI semantic conventions), FastAPI gateway, SQLAlchemy storage, CUSUM + Bayesian online change-point detection, 127 tests, ruff-clean CI. Apache-2.0.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The public &lt;strong&gt;"model weather" dashboard&lt;/strong&gt; (no login) shows live drift status for four production models:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://seismograph-weather.onrender.com/dashboard" rel="noopener noreferrer"&gt;seismograph-weather.onrender.com/dashboard&lt;/a&gt;&lt;/strong&gt; &lt;em&gt;(free host — first load may take ~30s)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Run the probe against your own endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;seismograph-probe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or clone the whole stack — gateway, dashboard, the federated quorum demo (&lt;code&gt;scripts/demo_simulation.py&lt;/code&gt; shows two orgs independently confirming a drift and the public dashboard flipping only when quorum is met):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/Tania-coder/SEISMOGRAPH" rel="noopener noreferrer"&gt;github.com/Tania-coder/SEISMOGRAPH&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you've been burned by a silent model change — I'd like to hear the story. That failure mode is exactly what this network exists to catch, and every additional independent observer makes the signal stronger.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;SEISMOGRAPH is open source (Apache-2.0), archived at &lt;a href="https://doi.org/10.5281/zenodo.21045517" rel="noopener noreferrer"&gt;DOI 10.5281/zenodo.21045517&lt;/a&gt;. Built by &lt;a href="https://github.com/Tania-coder" rel="noopener noreferrer"&gt;Tatiana Radchenko&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>opensource</category>
      <category>observability</category>
    </item>
    <item>
      <title>SEISMOGRAPH now has a live public dashboard</title>
      <dc:creator>Tatiana Radchenko 🇩🇰</dc:creator>
      <pubDate>Wed, 24 Jun 2026 12:04:48 +0000</pubDate>
      <link>https://dev.to/taniacoder/seismograph-now-has-a-live-public-dashboard-nhj</link>
      <guid>https://dev.to/taniacoder/seismograph-now-has-a-live-public-dashboard-nhj</guid>
      <description>&lt;p&gt;When I wrote about detecting silent LLM provider drift, the dashboard was localhost-only — you had to take my word for it. Now it's live and you can touch it:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://seismograph-weather.onrender.com/dashboard" rel="noopener noreferrer"&gt;https://seismograph-weather.onrender.com/dashboard&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It shows real-time "model weather" for 4 production models (GPT-4o, GPT-4o-mini, Claude 3.5 Sonnet, Claude 3 Haiku) — no login. The same engine flagged the Claude 3.5 Sonnet degradation 38 days before the official postmortem on a reproducible backtest.&lt;/p&gt;

&lt;p&gt;Open-source, Apache-2.0, 107 tests.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install seismograph-probe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/Tania-coder/SEISMOGRAPH" rel="noopener noreferrer"&gt;https://github.com/Tania-coder/SEISMOGRAPH&lt;/a&gt; — building in public, feedback welcome.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl327p4l00scdk8ag9fue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl327p4l00scdk8ag9fue.png" alt=" " width="799" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>ai</category>
      <category>opensource</category>
      <category>python</category>
    </item>
    <item>
      <title>I've been building SEISMOGRAPH for 3 weeks. Here's what shipped today</title>
      <dc:creator>Tatiana Radchenko 🇩🇰</dc:creator>
      <pubDate>Tue, 23 Jun 2026 20:42:47 +0000</pubDate>
      <link>https://dev.to/taniacoder/ive-been-building-seismograph-for-3-weeks-heres-what-shipped-today-401h</link>
      <guid>https://dev.to/taniacoder/ive-been-building-seismograph-for-3-weeks-heres-what-shipped-today-401h</guid>
      <description>&lt;p&gt;tl;dr: pip install seismograph-probe — a Python probe that detects silent LLM API drift using CUSUM change-point detection, with privacy-preserving signal aggregation. 103 tests passing. Dashboard live. Open source.&lt;/p&gt;

&lt;p&gt;Three weeks ago I asked a question I couldn't answer:&lt;/p&gt;

&lt;p&gt;"Did GPT-4 just change underneath me, or is it my prompt?"&lt;/p&gt;

&lt;p&gt;No latency spike. No downtime. Just subtly different outputs from the same prompts, same parameters, same everything. I spent days debugging something that wasn't my fault.&lt;/p&gt;

&lt;p&gt;So I built a detector.&lt;/p&gt;

&lt;p&gt;Today I'm shipping it publicly.&lt;/p&gt;

&lt;p&gt;What's actually working right now&lt;br&gt;
This isn't a concept post. Here's what's live:&lt;br&gt;
The probe SDK — on PyPI today&lt;br&gt;
pip install seismograph-probe&lt;/p&gt;

&lt;p&gt;from probe.sdk import ProbeSDK&lt;/p&gt;

&lt;p&gt;sdk = ProbeSDK(provider="openai", model="gpt-4-turbo")&lt;/p&gt;

&lt;p&gt;result = sdk.run_canary_suite()&lt;/p&gt;

&lt;p&gt;print(result.drift_score)  # 0.0 stable → 1.0 significant shift&lt;/p&gt;

&lt;p&gt;The probe runs ≤200 canary prompts at temperature=0 daily. These are semantically stable tasks — deterministic questions, structured reasoning, format-adherence checks. The goal is a reliable behavioral baseline, not a capability benchmark.&lt;/p&gt;

&lt;p&gt;Privacy boundary: raw prompts and model outputs never leave your machine. The probe extracts SHA-256 feature hashes, distributional stats, and DP-noised aggregates. That's all that transmits.&lt;br&gt;
CUSUM change-point detection — running&lt;br&gt;
The correlation engine uses CUSUM (Cumulative Sum) — a sequential statistical test that's sensitive to gradual drift, not just threshold crossings.&lt;/p&gt;

&lt;p&gt;When I backtest against a known LLM behavioral shift event (Aug–Sep 2025):&lt;/p&gt;

&lt;p&gt;Day 0:   CUSUM statistic: 0.12  (stable baseline)&lt;/p&gt;

&lt;p&gt;Day 11:  First elevation detected&lt;/p&gt;

&lt;p&gt;Day 19:  Alert threshold crossed  ← SEISMOGRAPH fires&lt;/p&gt;

&lt;p&gt;Day 57:  Public postmortem published&lt;/p&gt;

&lt;p&gt;38-day lead time. That's the number I keep coming back to.&lt;br&gt;
Ingestion gateway — deployed&lt;br&gt;
FastAPI gateway with:&lt;/p&gt;

&lt;p&gt;Ed25519-signed batch verification (unsigned batches rejected atomically)&lt;br&gt;
Pydantic v2 schema validation&lt;br&gt;
SQLAlchemy ORM + SQLite (ClickHouse migration planned for Phase 2)&lt;br&gt;
Bearer token auth on audit export endpoint&lt;br&gt;
Public dashboard — live at localhost, hosted version coming&lt;br&gt;
Dark-mode model weather dashboard. Polls /v1/weather every 60 seconds. Shows per-model drift status across your fleet.&lt;/p&gt;

&lt;p&gt;GET /v1/weather&lt;/p&gt;

&lt;p&gt;→ [{ "model": "gpt-4-turbo", "status": "STABLE", ... },&lt;/p&gt;

&lt;p&gt;{ "model": "claude-3-5-sonnet", "status": "STABLE", ... }]&lt;br&gt;
Test suite — 103/103 passing&lt;br&gt;
Not "it works on my machine." 103 tests across probe SDK, storage layer, gateway, CUSUM detector, privacy boundary, and auth. Zero ruff violations across 22 Python files.&lt;br&gt;
Provider ToS compliance — checked&lt;br&gt;
Before adding any provider to the canary suite, I verify it doesn't violate their Terms of Service. Done for: OpenAI ✅, Anthropic ✅, Google Gemini ✅, Mistral ✅, Cohere ✅. Documented in docs/PROVIDER_TOS_CHECKS.md.&lt;/p&gt;

&lt;p&gt;What's NOT done yet (being honest)&lt;br&gt;
No hosted gateway yet. The gateway runs locally. Public ingestion endpoint is Phase 1.&lt;br&gt;
No Bayesian online detector yet. CUSUM is running. BayesianOnlineDetector.update() is deferred — it's on the backlog.&lt;br&gt;
No federation yet. Right now it's single-org. The cross-observer agreement scoring that makes it genuinely valuable is Phase 2.&lt;br&gt;
No cloud dashboard. localhost:8000 only for now.&lt;/p&gt;

&lt;p&gt;This is Phase 0: I'm proving the detection logic works before scaling it.&lt;/p&gt;

&lt;p&gt;The architecture in one diagram&lt;br&gt;
Your app&lt;/p&gt;

&lt;p&gt;│  (gen_ai.* OTel spans)&lt;/p&gt;

&lt;p&gt;▼&lt;/p&gt;

&lt;p&gt;ProbeSDK&lt;/p&gt;

&lt;p&gt;│  SHA-256 hashes + DP-noised stats only&lt;/p&gt;

&lt;p&gt;│  Ed25519-signed batch&lt;/p&gt;

&lt;p&gt;▼&lt;/p&gt;

&lt;p&gt;Ingestion Gateway (FastAPI)&lt;/p&gt;

&lt;p&gt;│  signature check → schema validation → store&lt;/p&gt;

&lt;p&gt;▼&lt;/p&gt;

&lt;p&gt;SQLite / ClickHouse&lt;/p&gt;

&lt;p&gt;│&lt;/p&gt;

&lt;p&gt;▼&lt;/p&gt;

&lt;p&gt;CUSUM Detector ──► DriftAlert&lt;/p&gt;

&lt;p&gt;│&lt;/p&gt;

&lt;p&gt;▼&lt;/p&gt;

&lt;p&gt;/v1/weather dashboard&lt;/p&gt;

&lt;p&gt;OTel-native throughout. If you're already emitting gen_ai.* spans, the adapter plugs straight in.&lt;/p&gt;

&lt;p&gt;Why this matters (and why it has to be federated)&lt;br&gt;
A single organization's drift signal is almost useless. Your outputs change because your users change. Your prompts change. Your context windows change.&lt;/p&gt;

&lt;p&gt;But if 15 independent organizations running the same canary suite all see correlated semantic drift on the same day — that's a model change. That's the signal you can act on.&lt;/p&gt;

&lt;p&gt;Single-org signal = private fleet data (yours only).&lt;br&gt;
Multi-org correlated signal = public drift alert.&lt;/p&gt;

&lt;p&gt;That's the design. Federation is Phase 2. The local probe is shippable today.&lt;/p&gt;

&lt;p&gt;Try it / follow along&lt;br&gt;
GitHub: github.com/Tania-coder/SEISMOGRAPH&lt;br&gt;
PyPI: pypi.org/project/seismograph-probe&lt;/p&gt;

&lt;p&gt;If you've been burned by a silent model change — I want to hear about it. Open an issue, or find me on Twitter @tatyanti.&lt;/p&gt;

&lt;p&gt;The probe is Apache 2.0. The gateway will be too.&lt;/p&gt;

&lt;p&gt;Tatiana Radchenko · AI Infrastructure · Aarhus, Denmark&lt;br&gt;
Building in public. Phase 0 of 3.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>python</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
