<?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: Thilo Barth</title>
    <description>The latest articles on DEV Community by Thilo Barth (@scanara).</description>
    <link>https://dev.to/scanara</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%2F4053870%2Fb8982c5a-2307-47f4-a03a-dbcacabcdacf.jpeg</url>
      <title>DEV Community: Thilo Barth</title>
      <link>https://dev.to/scanara</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/scanara"/>
    <language>en</language>
    <item>
      <title>Your AI System Already Has Logging. The EU AI Act Wants Something More Specific Than That.</title>
      <dc:creator>Thilo Barth</dc:creator>
      <pubDate>Mon, 07 Sep 2026 14:23:44 +0000</pubDate>
      <link>https://dev.to/scanara/your-ai-system-already-has-logging-the-eu-ai-act-wants-something-more-specific-than-that-3m80</link>
      <guid>https://dev.to/scanara/your-ai-system-already-has-logging-the-eu-ai-act-wants-something-more-specific-than-that-3m80</guid>
      <description>&lt;p&gt;Your AI system almost certainly has logging. Structured logs, maybe a request ID, maybe already shipped to a log aggregator. If someone asked "do you log what your model does," most engineering teams would say yes without thinking twice.&lt;/p&gt;

&lt;p&gt;Article 12 of the EU AI Act asks a narrower, more specific question than "do you have logs" — and a lot of systems that would confidently answer yes to the first question fail the second one anyway.&lt;/p&gt;

&lt;p&gt;What Article 12 actually requires&lt;/p&gt;

&lt;p&gt;The text: a high-risk AI system must technically support automatic logging of events over its lifetime, to a degree that supports traceability, incident investigation, and post-market monitoring. (Remote biometric identification systems under Annex III point 1(a) have their own additional minimum logging content, which this post doesn't cover.)&lt;/p&gt;

&lt;p&gt;Read that again slowly, because the specific words are doing real work. Not "logging exists somewhere." Automatic — it has to happen without a human remembering to trigger it. Over its lifetime — not just at request time, but across the system's whole operational history. Supports traceability — someone has to be able to reconstruct, after the fact, what the system saw and what it decided. Supports incident investigation — when something goes wrong, the logs need to be the thing that explains why, not a dead end. Supports post-market monitoring — the same logs are supposed to feed the ongoing obligation to watch for problems after deployment, not just exist for debugging.&lt;/p&gt;

&lt;p&gt;Most application logging was built to answer a different question: "is the service healthy, and can I debug a crash." That's a legitimate, useful thing to log for. It is not the same question as "can I reconstruct, six months from now, exactly what this system decided about a specific person and why." A log line that says INFO: scored applicant, result=0.82 answers the first question fine. It's close to useless for the second one — there's no record of what inputs produced that score, no reference to which model version ran, no way to tie it back to a specific decision that got acted on.&lt;/p&gt;

&lt;p&gt;Where this actually shows up in code&lt;/p&gt;

&lt;p&gt;The pattern isn't "no logging." It's logging that exists but doesn't capture the right thing, at the right durability, next to the decision that actually matters. A few shapes this takes in practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A model call wrapped in generic app-level logging (logger.info(f"prediction: {result}")) with no persisted record of the input, the model version, or which downstream action the result triggered.&lt;/li&gt;
&lt;li&gt;Logs that exist but live only in ephemeral container stdout with a short retention window — technically "automatic," but not "over its lifetime" if they age out in a week and the system runs for years.&lt;/li&gt;
&lt;li&gt;A decision-making function that logs that it ran, but not the specific record needed to reconstruct why it produced the output it did for a specific case under investigation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a simplified rule (real Semgrep syntax, not the literal production rule) that looks for the first shape — a high-risk inference call whose output flows into application logic with no accompanying structured, persisted audit record nearby:&lt;/p&gt;

&lt;p&gt;`rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;id: eu-ai-act-example.article-12-missing-audit-log
languages: [python]
severity: WARNING
message: &amp;gt;
  High-risk AI inference result is used without a structured audit-log
  call nearby. EU AI Act Article 12 requires automatic logging that
  supports traceability, incident investigation, and post-market
  monitoring -- not just generic application logging.
metadata:
  article: "12"
  category: record-keeping
patterns:

&lt;ul&gt;
&lt;li&gt;pattern-either:

&lt;ul&gt;
&lt;li&gt;pattern: $RESULT = $MODEL.predict(...)&lt;/li&gt;
&lt;li&gt;pattern: $RESULT = $CLIENT.chat.completions.create(...)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;pattern-not-inside: |
  $RESULT = $MODEL.predict(...)
  ...
  audit_log(...)&lt;/li&gt;
&lt;li&gt;pattern-not-inside: |
  $RESULT = $MODEL.predict(...)
  ...
  persist_decision_record(...)`&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rule isn't looking for the absence of any log call — most of this code already has one, which is exactly the trap. It's looking for the absence of a call that persists a structured decision record: something that could actually answer "what did this system decide, about what input, using which model version, and what happened next" if someone asked that question a year later.&lt;/p&gt;

&lt;p&gt;The passing version doesn't require ripping anything out — usually one additional call next to logic that's already there:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;result = model.predict(applicant_data)&lt;br&gt;
persist_decision_record(&lt;br&gt;
    input_ref=applicant_data.id,&lt;br&gt;
    model_version=model.version,&lt;br&gt;
    output=result,&lt;br&gt;
    timestamp=now(),&lt;br&gt;
)&lt;br&gt;
logger.info(f"prediction: {result}")  # the old logging can stay -- it just isn't sufficient alone&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The generic logger.info line isn't wrong. It's just answering a different question than Article 12 is asking, and a team that only has the first line has reasonable-looking logs and an actual compliance gap at the same time — which is exactly why this one gets missed more than a system with genuinely zero logging would.&lt;/p&gt;

&lt;p&gt;This isn't hypothetical. We've already published one real scan result: a public résumé-screening repo came back with three findings and a 91% score (see our Sept 1 post on this). Gaps shaped like the one above — logging that exists but doesn't persist a reconstructable decision record — are a common contributor to scores landing short of 100, even on codebases that already take logging seriously.&lt;/p&gt;

&lt;p&gt;Why fail-closed matters here specifically&lt;/p&gt;

&lt;p&gt;There's a second trap worth naming: a compliance check that silently passes when it can't actually verify the logging exists (a timeout, a missing permission, a check that errors out and defaults to "OK"). That produces an audit trail claiming something was verified when nothing was. For a requirement whose entire point is "can you reconstruct what happened," a check that fails open is actively worse than no check — it's a false record layered on top of a real gap. Any tooling built against Article 12 specifically needs to fail closed: if the check can't run, that's a finding, not a pass.&lt;/p&gt;

&lt;p&gt;Try it&lt;/p&gt;

&lt;p&gt;Free tier, self-serve, no sales call: &lt;a href="https://scanara.io/en/" rel="noopener noreferrer"&gt;https://scanara.io/en/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where I might be wrong&lt;/p&gt;

&lt;p&gt;I'd like actual pushback on this one. Is "persist a structured decision record next to the inference call" really what auditors will look for under Article 12, or is that my own engineering intuition about what should satisfy traceability, filling in a gap the regulation itself leaves genuinely open? How much retention is actually "over its lifetime" in practice — is there real guidance on this anywhere, or is everyone guessing at a number right now? And for teams already shipping to observability platforms with long retention (Datadog, a data warehouse) — does that already satisfy this, or does the structure of what's captured matter more than where it's stored? Genuinely don't know the answer to that last one myself.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>We scanned public AI repos for EU AI Act compliance. Nearly every one failed.</title>
      <dc:creator>Thilo Barth</dc:creator>
      <pubDate>Thu, 27 Aug 2026 20:10:33 +0000</pubDate>
      <link>https://dev.to/scanara/we-scanned-public-ai-repos-for-eu-ai-act-compliance-nearly-every-one-failed-4nme</link>
      <guid>https://dev.to/scanara/we-scanned-public-ai-repos-for-eu-ai-act-compliance-nearly-every-one-failed-4nme</guid>
      <description>&lt;p&gt;We scanned public AI repos against the EU AI Act's requirements. Nearly every one failed at least one requirement.&lt;/p&gt;

&lt;p&gt;The code wasn't bad. Developers already know how to write structured logging, input validation, and human-oversight checkpoints. Nobody told them these are now legal requirements for high-risk AI systems shipped into the EU.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap
&lt;/h2&gt;

&lt;p&gt;The EU AI Act's high-risk obligations are in force now. The deadline that mattered, Aug 2, 2026, already passed. If your AI system falls under Annex III, you need a risk classification and a technical documentation file: Annex IV, 9 sections, mapping what your system actually does to what the regulation requires. Annex III covers more ground than most teams expect, including credit scoring, CV screening and hiring tools, biometric categorization, insurance underwriting, and exam scoring.&lt;/p&gt;

&lt;p&gt;Most engineering teams don't know if they're in scope. Fewer have the documentation. A team starting from zero needs 3-6 months to produce that documentation package by hand, before anyone's even checked whether the underlying system does what the docs claim.&lt;/p&gt;

&lt;p&gt;The failures we're seeing aren't random. Articles 9 (Risk Management), 12 (Record-Keeping), and 14 (Human Oversight) fail most often. Article 11 (Technical Documentation) has a high pass rate, because developers already write docstrings and type hints, and that habit happens to satisfy most of what Article 11 asks for.&lt;br&gt;
Where good engineering practice already overlaps with the legal requirement, teams pass. Where that connection hasn't been made yet, they fail. It's an awareness gap, not a carelessness one.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why the tools you already have miss this
&lt;/h2&gt;

&lt;p&gt;If your company runs a GRC platform (OneTrust, Vanta, Credo AI), you might assume this is already covered.&lt;br&gt;
It isn't. Those tools read cloud config, identity systems, and questionnaire answers. They don't read your code.&lt;/p&gt;

&lt;p&gt;Say the risk in your AI system lives in application logic: a scoring function, a ranking model, an inference call that feeds a hiring or lending decision. A cloud-config scan can't see any of that. The evidence the EU AI Act actually asks for, what the system does, what oversight exists, what happens when it's wrong, lives&lt;br&gt;
in the code and the documentation next to it. Not in an IAM policy.&lt;/p&gt;

&lt;p&gt;None of this means you should rip out your GRC platform. Evidence from code and attestations from a questionnaire are different kinds of proof, and most teams only have the second kind.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Scanara actually checks
&lt;/h2&gt;

&lt;p&gt;Compliance content usually stays abstract. Here's something concrete instead: one real detection pattern, simplified for this post but built on real Semgrep rule syntax.&lt;/p&gt;

&lt;p&gt;Article 14 requires that high-risk AI systems be designed so a human can effectively oversee their operation. That includes the ability to intervene or override an output before it gets acted on. Here's a simplified version of the kind of rule that checks for it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;eu-ai-act-example.article-14-missing-human-oversight-hook&lt;/span&gt;
    &lt;span class="na"&gt;languages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;python&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;WARNING&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="s"&gt;High-risk AI inference call has no human-oversight hook (approval, override, or review checkpoint) nearby. EU AI Act Article 14 requires human oversight measures for high-risk AI systems before their output is acted on.&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;article&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;14"&lt;/span&gt;
      &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;human-oversight&lt;/span&gt;
    &lt;span class="na"&gt;patterns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pattern-either&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$RESULT = $MODEL.predict(...)&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$RESULT = $CLIENT.chat.completions.create(...)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pattern-not-inside&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;if $APPROVED:&lt;/span&gt;
              &lt;span class="s"&gt;...&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pattern-not-inside&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;$RESULT = require_human_approval(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rule looks for a high-risk inference call, a model prediction or an LLM completion, whose result flows straight into downstream logic with no oversight checkpoint anywhere nearby. No conditional gate. No approval or override call in scope. The code works fine. There's just nothing in it that gives a human the chance to catch a bad output before it gets acted on.&lt;/p&gt;

&lt;p&gt;Flip it around and the passing version of the same code just adds one checkpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;applicant_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;require_human_approval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;finalize_decision&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same model, same prediction. One line of difference, and that line is exactly what Article 14 is asking for.&lt;/p&gt;

&lt;p&gt;This is a simplified example, not the literal production rule. The real ruleset covers more languages, more oversight-hook idioms, and confidence handling not shown here. It's still real Semgrep syntax, checking for a real pattern, mapped to a specific article and paragraph. Every finding traces back to something specific&lt;br&gt;
in the regulation, not a vague "AI governance" checkbox.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fail-closed, on purpose
&lt;/h2&gt;

&lt;p&gt;One pattern kept showing up in other compliance tooling we looked at: middleware that silently lets a request through when a check times out or errors. That's worse than having no check at all. It produces an audit trail that says "compliant" when nobody actually verified anything.&lt;/p&gt;

&lt;p&gt;Scanara's merge gate is fail-closed by design. If the check can't run, the PR gets blocked. It doesn't get silently waved through. A compliance check that quietly allows on failure isn't really a check. It's a false audit trail, and that's worse than an honest gap.&lt;/p&gt;

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

&lt;p&gt;Free tier, self-serve. Connect a repo and see what it actually finds. No sales call, no questionnaire:&lt;br&gt;
&lt;a href="https://scanara.io/en/?utm_source=devto&amp;amp;utm_medium=launch&amp;amp;utm_campaign=post-launch-2026-08" rel="noopener noreferrer"&gt;https://scanara.io/en/?utm_source=devto&amp;amp;utm_medium=launch&amp;amp;utm_campaign=post-launch-2026-08&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I might be wrong
&lt;/h2&gt;

&lt;p&gt;Genuinely interested in pushback here. What's the real false-positive rate on pattern-based detection like this at scale, once you're past the illustrative example above? Does automated evidence like this actually hold up to an auditor, or is it still compliance theater dressed up in YAML? And with enforcement authorities still being designated in most EU member states, how much of this is really "required now" versus "required eventually, once someone's actually checking"? I'd rather hear the hard questions in the comments than pretend there aren't any.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>compliance</category>
      <category>programming</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
