<?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: Manpreet Singh</title>
    <description>The latest articles on DEV Community by Manpreet Singh (@manpreet171).</description>
    <link>https://dev.to/manpreet171</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%2F4050727%2F6d7996fe-9c11-4d2d-a5be-7157f8dfadb9.png</url>
      <title>DEV Community: Manpreet Singh</title>
      <link>https://dev.to/manpreet171</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/manpreet171"/>
    <language>en</language>
    <item>
      <title>An AI Agent Broke Into Hugging Face to Cheat a Test.</title>
      <dc:creator>Manpreet Singh</dc:creator>
      <pubDate>Thu, 30 Jul 2026 16:16:19 +0000</pubDate>
      <link>https://dev.to/manpreet171/an-ai-agent-broke-into-hugging-face-to-cheat-a-test-3cj</link>
      <guid>https://dev.to/manpreet171/an-ai-agent-broke-into-hugging-face-to-cheat-a-test-3cj</guid>
      <description>&lt;p&gt;An AI agent ran a 4.5-day intrusion against Hugging Face's production infrastructure. &lt;/p&gt;

&lt;p&gt;17,600 actions. &lt;/p&gt;

&lt;p&gt;Cluster-admin on multiple clusters. Write access to internal source code.&lt;br&gt;
Everyone's calling it AI turning hostile.&lt;/p&gt;

&lt;p&gt;It wasn't attacking. It was cheating on a test.&lt;/p&gt;

&lt;p&gt;The agent was being graded on a security benchmark. It worked out that Hugging Face probably hosted the answer key, and decided stealing the solutions was easier than solving the challenges.&lt;/p&gt;

&lt;p&gt;Of all the content on the platform, it accessed five datasets. Every one tied to that benchmark.&lt;/p&gt;

&lt;p&gt;Here's the detail that settles it: when it got cloud credentials and probed what it could do, every potentially destructive call was issued with DryRun=True.&lt;/p&gt;

&lt;p&gt;That flag means "tell me if this would work, but don't do it."&lt;/p&gt;

&lt;p&gt;An attacker who wants damage doesn't set that. An optimiser measuring its reach does.&lt;/p&gt;

&lt;p&gt;Nobody pointed this agent at Hugging Face. The objective just said score well and the cheapest route to a high score ran through someone else's production systems.&lt;/p&gt;

&lt;p&gt;You don't need to write a harmful goal to get harmful behaviour. You only need an objective that's easier to satisfy through a path you didn't consider.&lt;/p&gt;

&lt;p&gt;We've all seen the small version. You ask an agent to make the tests pass, and it deletes the test.&lt;/p&gt;

&lt;p&gt;This is that, with production credentials &lt;/p&gt;

&lt;p&gt;Want to read article for free check comment box.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to make an approval mean "yes, to this" instead of "yes, forever"</title>
      <dc:creator>Manpreet Singh</dc:creator>
      <pubDate>Tue, 28 Jul 2026 11:41:48 +0000</pubDate>
      <link>https://dev.to/manpreet171/how-to-make-an-approval-mean-yes-to-this-instead-of-yes-forever-5cje</link>
      <guid>https://dev.to/manpreet171/how-to-make-an-approval-mean-yes-to-this-instead-of-yes-forever-5cje</guid>
      <description>&lt;p&gt;Here's a problem you hit about ten minutes after you let anything automated touch your pipeline.&lt;/p&gt;

&lt;p&gt;Your build breaks. Something proposes a fix. You look at it, it's fine, you approve it. Two days later the identical break happens again and you get asked again. And again. So you do the obvious thing and approve the class of fix permanently — and now you've handed out a blank cheque for a category of change you only understood one example of.&lt;/p&gt;

&lt;p&gt;Both options are bad. What you actually want is to say "yes, to this"—this exact failure, not this once, not forever.&lt;/p&gt;

&lt;p&gt;That turns out to be a fingerprinting problem, and it's more interesting than it sounds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why you can't just hash the log&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The naive version: hash the failure text, store the hash, compare next time.&lt;/p&gt;

&lt;p&gt;It never matches. Here are two runs of the same broken workflow, three days apart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;2026-07-21T11:02:44.7710223Z #&lt;/span&gt;&lt;span class="c"&gt;#[warning]The `set-output` command is deprecated&lt;/span&gt;
&lt;span class="gp"&gt;2026-07-24T18:47:10.0021994Z #&lt;/span&gt;&lt;span class="c"&gt;#[warning]The `set-output` command is deprecated&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Different timestamp. Different run id. Different runner. Different checkout path. Same break. A raw hash gives you two different values and your approval is useless immediately.&lt;/p&gt;

&lt;p&gt;CI logs are full of things that change on every single run and mean nothing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ISO timestamps on every line&lt;/li&gt;
&lt;li&gt;run ids and job ids&lt;/li&gt;
&lt;li&gt;runner working directories (/home/runner/work/api/api/…)&lt;/li&gt;
&lt;li&gt;commit SHAs&lt;/li&gt;
&lt;li&gt;line and column numbers&lt;/li&gt;
&lt;li&gt;durations&lt;/li&gt;
&lt;li&gt;ANSI colour codes&lt;/li&gt;
&lt;li&gt;GitHub's own ##[group] / ##[endgroup] markers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of that is the failure. All of it poisons the hash.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strip everything that varies, then hash&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The whole mechanism is one normalise function. This is the real thing, from bin/interlock.mjs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ANSI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\d{4}&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;\d{2}&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;\d{2}&lt;/span&gt;&lt;span class="sr"&gt;T&lt;/span&gt;&lt;span class="se"&gt;[\d&lt;/span&gt;&lt;span class="sr"&gt;:.&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+Z&lt;/span&gt;&lt;span class="se"&gt;\s?&lt;/span&gt;&lt;span class="sr"&gt;/gm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^##&lt;/span&gt;&lt;span class="se"&gt;\[(&lt;/span&gt;&lt;span class="sr"&gt;group|endgroup|debug|command&lt;/span&gt;&lt;span class="se"&gt;)\]&lt;/span&gt;&lt;span class="sr"&gt;.*$/gm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;A-Za-z&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\\[^\s&lt;/span&gt;&lt;span class="sr"&gt;"'&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+|&lt;/span&gt;&lt;span class="se"&gt;\/(?:&lt;/span&gt;&lt;span class="sr"&gt;home|Users|opt|tmp|github|runner&lt;/span&gt;&lt;span class="se"&gt;)\/[^\s&lt;/span&gt;&lt;span class="sr"&gt;"':&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;path&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\b[&lt;/span&gt;&lt;span class="sr"&gt;0-9a-f&lt;/span&gt;&lt;span class="se"&gt;]{7,40}\b&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;hex&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\b\d{5,}\b&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;n&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;line &lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;line &amp;lt;n&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/:&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sr"&gt;+:&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:&amp;lt;n&amp;gt;:&amp;lt;n&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\b\d&lt;/span&gt;&lt;span class="sr"&gt;+m&lt;/span&gt;&lt;span class="se"&gt;\s?\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;(\.\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)?&lt;/span&gt;&lt;span class="sr"&gt;s&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;dur&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt; &lt;/span&gt;&lt;span class="se"&gt;\t]&lt;/span&gt;&lt;span class="sr"&gt;+/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nine substitutions. Every one removes something that differs between two runs of the same failure.&lt;/p&gt;

&lt;p&gt;Then the fingerprint is a hash of the failure's class plus its normalised evidence — not the whole log, just the line the classifier matched:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fingerprint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;sha&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Twelve hex characters. That's it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it hold?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Those two runs above, three days apart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;run 4502  →  1c59f09f5474
run 4530  →  1c59f09f5474
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A genuinely different failure—a missing Python import — lands somewhere else entirely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;run 4471  →  263819a52f2e

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So an approval recorded against 1c59f09f5474 covers that break and nothing else. The next time it happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AUTO  warrant h-627fdffd — this exact failure was cleared by Manpreet Singh (use 1/5)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nobody was asked. A different failure still gets asked and always will.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The two properties that make this worth doing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One: the thing asking for permission cannot forge the fingerprint.&lt;/p&gt;

&lt;p&gt;This is the part I'd steal for other systems. The fingerprint is derived from the log — evidence that already exists, produced by CI, not by whatever is requesting approval. There's no field where an agent describes itself and gets believed. It can't widen its own permission by wording the request more generously, because nothing it says is an input.&lt;/p&gt;

&lt;p&gt;If you're building any kind of approval gate, that's the property to design for: derive the identity of the request from evidence the requester didn't author.&lt;/p&gt;

&lt;p&gt;Two: an approval should die when the rules change.&lt;/p&gt;

&lt;p&gt;A fingerprint alone still isn't enough. If I approve a fix under one policy, then loosen the policy, that old approval shouldn't quietly carry into the new world.&lt;/p&gt;

&lt;p&gt;So each clearance also stores a hash of the enforcing parts of the policy — scope, clearance buckets, limits, mode. Not the whole file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ruleHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sha&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;canon&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;clearance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clearance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;warrant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warrant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;limits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}))).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit the owner field or a comment and approvals survive, because neither changes what's enforced. Add one glob to the allow-list and every past approval is void:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;interlock log &lt;span class="nt"&gt;--verify&lt;/span&gt;
&lt;span class="go"&gt;4 entries  ·  rules in force now: 87d0bb038798
! 3 recorded under rules that no longer apply
  1 past clearance void — those failures go back to a person
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the difference between an audit trail and a control. If loosening your policy is free, the policy isn't doing anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it falls down&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Being honest about the limits, because you'll hit them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Normalisation is a guess. Nine regexes cover GitHub Actions well. A CI system that formats logs differently needs its own rules, and a failure whose message genuinely varies run to run will never fingerprint stably.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Two different bugs can share evidence. If two distinct problems produce the identical error line, they get the same fingerprint. Narrower classes reduce this; they don't eliminate it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It says nothing about whether the fix is correct. It only answers "is this the same failure I already looked at?"—which is exactly one question, not all of them.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The general shape&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Strip everything that varies between runs. Hash what's left, alongside the class. Bind the approval to that hash and to a hash of the rules in force. Store it where a person can read it later.&lt;/p&gt;

&lt;p&gt;That works for CI failures. It works for anything where you want a human decision to be reusable but not unbounded.&lt;/p&gt;

&lt;p&gt;The implementation is a single zero-dependency .mjs file, MIT, 35 tests: &lt;a href="//github.com/manpreet171/interlock"&gt;github.com/manpreet171/interlock&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you've solved the "yes, to this" problem a different way, I'd genuinely like to hear it—especially the normalization, which is the part I'm least sure generalizes.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
