<?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: ZEUS LAB</title>
    <description>The latest articles on DEV Community by ZEUS LAB (@zeuslab).</description>
    <link>https://dev.to/zeuslab</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%2F4239921%2F9b88f54c-0e8d-4cd9-9ede-117c1eef0069.png</url>
      <title>DEV Community: ZEUS LAB</title>
      <link>https://dev.to/zeuslab</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zeuslab"/>
    <language>en</language>
    <item>
      <title>A 60% win rate can still halve the account: risk of ruin and the Kelly fraction over 5,000 seeded paths</title>
      <dc:creator>ZEUS LAB</dc:creator>
      <pubDate>Sat, 14 Nov 2026 01:09:47 +0000</pubDate>
      <link>https://dev.to/zeuslab/a-60-win-rate-can-still-halve-the-account-risk-of-ruin-and-the-kelly-fraction-over-5000-seeded-44l8</link>
      <guid>https://dev.to/zeuslab/a-60-win-rate-can-still-halve-the-account-risk-of-ruin-and-the-kelly-fraction-over-5000-seeded-44l8</guid>
      <description>&lt;p&gt;A 60% win rate, and the account can still lose half of itself on the way.&lt;/p&gt;

&lt;p&gt;That is not a rare streak of bad luck. With a 60% win rate, an average win of 0.8 times the average loss, and 10% of the balance at stake on every trade, 36.860% of 5,000 simulated paths touched half of the starting balance at least once within 200 trades. The 10% is not arbitrary: it is the Kelly fraction for those inputs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Every number below is a calculation from our free in-browser calculator, with the same formulas and the same random seed. It is not a forecast and not trading advice. The model is deliberately simple: every trade is independent, wins b units per unit risked with probability p and loses 1 unit with probability 1 − p, with p and b fixed. So a win multiplies the balance by (1 + b × stake) and a loss by (1 − stake). No fees, no slippage, no correlation between trades.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The win rate does not decide where the balance goes
&lt;/h2&gt;

&lt;p&gt;At least three numbers do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;p&lt;/strong&gt;, the win rate;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;b&lt;/strong&gt;, the payoff ratio: average win ÷ average loss;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;r&lt;/strong&gt;, the stake: what one trade can lose, as a share of the balance at that moment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At p = 0.6 and b = 0.8 the edge per trade is small: 0.6 × 0.8 − 0.4 × 1 = 0.08 units per unit risked. The same edge can take very different roads depending on r, because the balance compounds. After a 50% fall, getting back to the start takes a 100% gain.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Kelly fraction: the growth-maximising stake
&lt;/h2&gt;

&lt;p&gt;The Kelly criterion gives the stake that maximises the expected logarithmic growth of the balance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;f* = p − (1 − p) / b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For p = 0.6 and b = 0.8, f* = 0.6 − 0.4 / 0.8 = 0.10, so 10% of the balance. For p = 0.5 and b = 1.5 it is 16.667%. The formula assumes b &amp;gt; 0 and a stake between 0 and 1; a negative result only means the inputs carry no edge in this model, and it is not a signal of any kind.&lt;/p&gt;

&lt;p&gt;The same formula also marks where more stake starts to hurt. Expected log growth per trade, g(f) = p·ln(1 + b·f) + (1 − p)·ln(1 − f), at p = 0.6 and b = 0.8:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stake&lt;/th&gt;
&lt;th&gt;g(f) per trade&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2%&lt;/td&gt;
&lt;td&gt;+0.00144&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5%&lt;/td&gt;
&lt;td&gt;+0.00302&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10% (Kelly)&lt;/td&gt;
&lt;td&gt;+0.00403&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;td&gt;+0.00299&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20%&lt;/td&gt;
&lt;td&gt;−0.00021&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For these inputs, growth peaks at 10% and falls on both sides; at a 20% stake the expected log growth is negative in this model. Half-Kelly (5%) keeps about 75% of the peak growth, again for these inputs.&lt;/p&gt;

&lt;p&gt;One more assumption sits inside the formula: it treats p and b as known. In practice they are estimates from a limited trade log. If the estimate leans optimistic, the "optimal" stake is already too large.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Risk of ruin: look at the spread of paths, not the average
&lt;/h2&gt;

&lt;p&gt;Kelly maximises long-run growth. An account does not live the average, though; it lives one path. So we ran 5,000 seeded paths with the same inputs and counted the share whose balance fell to 50% of the starting balance at least once within 200 trades. The table also shows the median maximum drawdown, the largest peak-to-trough decline on each path (in brackets: the 95% Wilson interval for the share, inside this model):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stake&lt;/th&gt;
&lt;th&gt;Touched −50%&lt;/th&gt;
&lt;th&gt;95% interval&lt;/th&gt;
&lt;th&gt;Median max drawdown&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2%&lt;/td&gt;
&lt;td&gt;0.020%&lt;/td&gt;
&lt;td&gt;0.004% – 0.113%&lt;/td&gt;
&lt;td&gt;17.582%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5%&lt;/td&gt;
&lt;td&gt;7.300%&lt;/td&gt;
&lt;td&gt;6.611% – 8.054%&lt;/td&gt;
&lt;td&gt;39.769%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10% (Kelly)&lt;/td&gt;
&lt;td&gt;36.860%&lt;/td&gt;
&lt;td&gt;35.533% – 38.207%&lt;/td&gt;
&lt;td&gt;67.596%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At exactly the Kelly stake, more than one path in three fell to half of the starting balance along the way, and the 95th percentile of the maximum drawdown was 89.595%. Among the three stakes tested, the growth-maximising 10% also had the largest median maximum drawdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Losing streaks under independent outcomes
&lt;/h2&gt;

&lt;p&gt;Losses come in runs even at 60%. The probability of at least one run of 8 losses within 200 trades is 7.356% (exact, by dynamic programming). At a 50% win rate it is 31.970%.&lt;/p&gt;

&lt;p&gt;The table compares fixed stakes only; a stake that changes after losses was not simulated. In this model, a larger fixed stake sits further down the table, where the spread of paths is wider.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Reproduce it
&lt;/h2&gt;

&lt;p&gt;This is the core of the calculator in Python, with the same seeded generator. It prints the Kelly fraction and the three ruin shares above, to the last digit:&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="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mulberry32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# the same small seeded generator the browser calculator uses&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xFFFFFFFF&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;nonlocal&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mh"&gt;0x6D2B79F5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xFFFFFFFF&lt;/span&gt;
        &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xFFFFFFFF&lt;/span&gt;
        &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;^=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;61&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xFFFFFFFF&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xFFFFFFFF&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xFFFFFFFF&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4294967296&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rnd&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ruin_share&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trades&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;floor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paths&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20261114&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="s2"&gt;"""Share of paths whose balance touches (1 - floor) of the start at least once."""&lt;/span&gt;
    &lt;span class="n"&gt;rnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;win&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mulberry32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log1p&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log1p&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log1p&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ruined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trades&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;win&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;rnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;loss&lt;/span&gt;
            &lt;span class="n"&gt;ruined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ruined&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;1e-12&lt;/span&gt;
        &lt;span class="n"&gt;hits&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;ruined&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;paths&lt;/span&gt;

&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"Kelly fraction: {p - (1 - p) / b:.3%}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"risk {r:.0%}: {ruin_share(p, b, r):.3%} of 5,000 paths touched -50%"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Kelly fraction: 10.000%
risk 2%: 0.020% of 5,000 paths touched -50%
risk 5%: 7.300% of 5,000 paths touched -50%
risk 10%: 36.860% of 5,000 paths touched -50%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Working in log space turns compounding into addition and makes the threshold test a single comparison; it is still floating-point arithmetic, hence the small tolerance.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. A limit you decided is easier to keep when a machine keeps it
&lt;/h2&gt;

&lt;p&gt;A limit set by calculation is only as good as whatever enforces it. One design hands that job to a small local process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the numbers come first: daily loss limit, per-strategy limit, what to do when the data feed goes stale;&lt;/li&gt;
&lt;li&gt;when a configured limit is detected as touched, it tries to stop the process you named, without asking you in the moment;&lt;/li&gt;
&lt;li&gt;every stop is written to a log chained with SHA-256, so an edit to one entry breaks the chain on the next check (rewriting the whole chain is only caught against a copy kept elsewhere);&lt;/li&gt;
&lt;li&gt;configuration files are read, not used, to check the permission settings declared there and to flag secrets written into them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A tool built this way places no trades and predicts nothing. Stopping a process does not close open positions or cancel resting orders, and it is not a guaranteed loss ceiling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it with your own numbers
&lt;/h2&gt;

&lt;p&gt;The calculator runs in the browser: win rate, payoff, stake, number of trades, the drawdown you treat as ruin and the streak length. Your inputs are not sent anywhere. The same page shows the local audit-and-stop kit, its file list and its tests:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://zero-trace-ship-kit.zeus-lab-global.workers.dev/quant/?utm_source=devto" rel="noopener noreferrer"&gt;https://zero-trace-ship-kit.zeus-lab-global.workers.dev/quant/?utm_source=devto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Calculation only. These are formula values and seeded simulation values, not a forecast, and not a recommendation of any trade, broker or product.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was drafted with AI assistance (Claude) and checked against the measured results and the published files. Product links lead to our own paid kits.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>fintech</category>
      <category>science</category>
    </item>
    <item>
      <title>Architecting a Solo Multi-AI Studio: Separation of Powers and Deterministic Pre-Release Gates</title>
      <dc:creator>ZEUS LAB</dc:creator>
      <pubDate>Fri, 13 Nov 2026 22:08:03 +0000</pubDate>
      <link>https://dev.to/zeuslab/architecting-a-solo-multi-ai-studio-separation-of-powers-and-deterministic-pre-release-gates-21o4</link>
      <guid>https://dev.to/zeuslab/architecting-a-solo-multi-ai-studio-separation-of-powers-and-deterministic-pre-release-gates-21o4</guid>
      <description>&lt;p&gt;One chat window, one model, every job: the idea, the structure, the code and the final check. Sooner or later, though, the same model can quietly approve its own mistake, and a leaked key, an unmeasured number or a broken link goes out with your name on it.&lt;/p&gt;

&lt;p&gt;What changed things for us was not a better prompt. It was a separation of powers. The model that builds never grades its own work. A second reader sees only the finished text and the rules. And the checks that must never be argued with are done by plain code at commit time. In our own test, that commit-time leak gate stops 32 of 32 planted samples and raises 0 false alarms on 17 clean look-alikes. That is a figure for planted samples, not a detection rate for real folders.&lt;/p&gt;

&lt;p&gt;Here is the setup, piece by piece, so you can rebuild it with the models you already use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why one window degrades
&lt;/h2&gt;

&lt;p&gt;Three things go wrong when one conversation does everything.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Context mixing.&lt;/strong&gt; The brainstorm, the pricing math and the deployment details share one window. Every new answer is shaped by everything before it, including the parts that were wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-grading.&lt;/strong&gt; A model asked to review what it just wrote tends to agree with itself. It has read its own reasoning, so it walks the same path to the same blind spot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nobody says no.&lt;/strong&gt; There is no role whose job is to refuse. Speed wins every argument.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of this is a model problem. It is an organisational one, and the old fix applies: split the powers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four roles, one decision-maker
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Owns&lt;/th&gt;
&lt;th&gt;Does not&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Vision&lt;/td&gt;
&lt;td&gt;the story, the market, the plan&lt;/td&gt;
&lt;td&gt;write the final code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structure&lt;/td&gt;
&lt;td&gt;math, prices, edge cases, a second opinion&lt;/td&gt;
&lt;td&gt;approve its own numbers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution&lt;/td&gt;
&lt;td&gt;code, files, film, the scheduled runs&lt;/td&gt;
&lt;td&gt;grade its own output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blind audit&lt;/td&gt;
&lt;td&gt;reading the artifact against the rules&lt;/td&gt;
&lt;td&gt;see the maker's reasoning&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Above them sits one person who decides: execute, abort, or mutate with a stated reason. As one example of an assignment, in our setup a long-context model takes vision, a second vendor's model takes structure and, in a separate session, the blind review, and a coding agent executes. The assignment matters less than the rule that no role holds two powers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handoffs that keep the powers apart
&lt;/h2&gt;

&lt;p&gt;Separation only works if the handoffs are clean.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Write a brief, not a chat.&lt;/strong&gt; Inputs, constraints, a definition of done, and what must not be touched. A brief can be read cold by a model that saw nothing else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seal your own answer first.&lt;/strong&gt; Before asking for review, the maker writes its proposal and seals it. Then the reviewers are asked in parallel, and neither sees the other's answer. Disagreement is the signal you are paying for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review in an empty room.&lt;/strong&gt; Run the reviewer in an empty temporary folder and hand it only the text to read and the rules to check against. No repository, no history, no maker's notes. A reader that never sees the reasoning is less likely to inherit its blind spot.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deterministic gates: what AI must not judge
&lt;/h2&gt;

&lt;p&gt;Some questions are not up for debate: did a secret leak, does a number have a source, does every link answer. These run as plain code, at commit time and again before release.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Leaks:&lt;/strong&gt; personal names, machine paths that reveal a login name, secret keys, private contacts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claims:&lt;/strong&gt; figures with no measurement behind them, and wording that overstates what was measured.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Links:&lt;/strong&gt; every public link must answer; a retired page counts as alive only while a real redirect exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;House rules:&lt;/strong&gt; the palette, public times in UTC, loudness and frame rate for any film.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The wiring is ordinary. A sketch, with placeholder file names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;# .git/hooks/pre-commit: a non-zero exit stops the commit&lt;/span&gt;
python3 tools/check_leaks.py --staged &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; 1
python3 tools/check_claims.py --staged &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent gets a seat belt too. Claude Code can run a hook before every tool call; exit code 2 refuses the call, and the message on standard error goes back to the agent. This one, from our MIT starter, refuses a commit that tries to skip the checks:&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="no"&gt;SKIP_FLAG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"--no-"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"verify"&lt;/span&gt;
&lt;span class="no"&gt;RULE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="s2"&gt;"git\s+(commit|push)\b[^\n;|&amp;amp;]*"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SKIP_FLAG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s2"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"tool_name"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"tool_input"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"command"&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="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;RULE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"guard: refused. Fix the cause instead.\n"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"python3 \"$CLAUDE_PROJECT_DIR/hooks/guard_min.py\""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]&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;It is a text match on the command, nothing more. Treat it as a seat belt, not a sandbox. Exceptions raised inside the hook are caught and let the call through, so a bug in its logic does not block your work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping the budget fixed
&lt;/h2&gt;

&lt;p&gt;Metered API keys are the quiet way a solo studio overspends. The same hook pattern can refuse a tool call that touches a metered-key variable, so the agents work through the flat-rate tools you are already signed into. The cost follows the plan you chose rather than a per-call meter.&lt;/p&gt;

&lt;h2&gt;
  
  
  A rhythm that runs without you
&lt;/h2&gt;

&lt;p&gt;The studio wakes on a schedule (four times a day for us), catches up once after the machine has slept, and picks up any new directive the founder leaves in a shared folder. What reaches the human is one card: what changed, what was measured, and one decision, with execute, abort or mutate.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this does not do
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It does not replace judgment. It moves judgment to the one place it belongs.&lt;/li&gt;
&lt;li&gt;The gates catch only what they are written to catch. "No findings" is not proof that nothing is there.&lt;/li&gt;
&lt;li&gt;A blind reviewer can be wrong. Two reviewers who disagree are still cheaper than one who agrees with everything.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;The pre-release leak check runs in your browser, and the files you scan are not uploaded: &lt;a href="https://zero-trace-ship-kit.zeus-lab-global.workers.dev/?utm_source=devto" rel="noopener noreferrer"&gt;https://zero-trace-ship-kit.zeus-lab-global.workers.dev/?utm_source=devto&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The MIT starter with the hook above: &lt;a href="https://github.com/ZEUSLABZERO/zeus-harness-starter" rel="noopener noreferrer"&gt;https://github.com/ZEUSLABZERO/zeus-harness-starter&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The full blueprint (role directives, gates, a budget ledger and a self-check), if you want it ready-made: &lt;a href="https://zero-trace-ship-kit.zeus-lab-global.workers.dev/studio/?utm_source=devto" rel="noopener noreferrer"&gt;https://zero-trace-ship-kit.zeus-lab-global.workers.dev/studio/?utm_source=devto&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was drafted with AI assistance (Claude) and checked against the measured results and the published files. Product links lead to our own paid kits.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>automation</category>
      <category>security</category>
    </item>
    <item>
      <title>Before you publish an MCP server: seven checks for stdio and for secrets</title>
      <dc:creator>ZEUS LAB</dc:creator>
      <pubDate>Thu, 12 Nov 2026 23:27:23 +0000</pubDate>
      <link>https://dev.to/zeuslab/before-you-publish-an-mcp-server-seven-checks-for-stdio-and-for-secrets-48i2</link>
      <guid>https://dev.to/zeuslab/before-you-publish-an-mcp-server-seven-checks-for-stdio-and-for-secrets-48i2</guid>
      <description>&lt;p&gt;An MCP server that connects and then drops at once. Or one that works, gets published, and carries a real key in its example configuration. Both are the kind of flaw you can find in the few minutes before release. Here are seven checks for a local MCP server on the stdio transport.&lt;/p&gt;

&lt;p&gt;The reference MCP servers repository shows v2.4.0 dated 2026-11-11, and MCP servers are prominent among this week's new public repositories (we read the headlines and dates only and verified nothing behind them). More people are writing and publishing their own, so this is where to look first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The full checklist in English and Japanese, with questions and answers: &lt;a href="https://zero-trace-ship-kit.zeus-lab-global.workers.dev/guides/mcp-server-stdio-secrets-checklist/?utm_source=devto" rel="noopener noreferrer"&gt;https://zero-trace-ship-kit.zeus-lab-global.workers.dev/guides/mcp-server-stdio-secrets-checklist/?utm_source=devto&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Standard output carries protocol messages only
&lt;/h2&gt;

&lt;p&gt;The specification (Transports, version 2025-06-18; read on its page on 2026-11-12 UTC) says for stdio:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server must not write anything to its standard output that is not a valid MCP message.&lt;/li&gt;
&lt;li&gt;Messages are delimited by newlines and must not contain embedded newlines.&lt;/li&gt;
&lt;li&gt;The server may write logs to standard error.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A debugging &lt;code&gt;print&lt;/code&gt;, a library banner, a progress line: any one of them on standard output is read by the client as a message, and the session breaks. Send every log line to standard error.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Try the handshake by hand
&lt;/h2&gt;

&lt;p&gt;The first thing on standard output should be one JSON-RPC response to the &lt;code&gt;initialize&lt;/code&gt; request: the same &lt;code&gt;id&lt;/code&gt;, and a &lt;code&gt;result&lt;/code&gt;. Anything before it is a bug.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"check","version":"0"}}}'&lt;/span&gt; \
  &lt;span class="o"&gt;|&lt;/span&gt; python3 your_server.py 2&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null &lt;span class="o"&gt;|&lt;/span&gt; head -c 400
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a quick look, not a conformance test. The protocol version is one published version; use the one your server supports.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. No secrets in committed configuration
&lt;/h2&gt;

&lt;p&gt;Keep keys out of &lt;code&gt;.mcp.json&lt;/code&gt; and out of example configurations. Read them inside the server from its environment, and commit a placeholder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;"example"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"python3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"server.py"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;"API_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"${API_TOKEN}"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&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;Whether a &lt;code&gt;${VAR}&lt;/code&gt; placeholder is expanded depends on the client, so check yours. In our count of 100 public AI and MCP repositories on 2026-11-12, 7 had a &lt;code&gt;.mcp.json&lt;/code&gt; at the root (existence only; we did not read the contents).&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Ignore what stays local
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;.gitignore&lt;/code&gt; and, if you ship an image, in &lt;code&gt;.dockerignore&lt;/code&gt; as well. &lt;code&gt;.gitignore&lt;/code&gt; does not keep a file out of &lt;code&gt;docker build&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.env
.env.*
.claude/settings.local.json
*.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Tool descriptions say what the tool does, nothing else
&lt;/h2&gt;

&lt;p&gt;A description is read by the model as text. Do not put instructions to the model, credentials or internal addresses in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Default to reading, make writing explicit
&lt;/h2&gt;

&lt;p&gt;A server that only reports is easier to trust and to review. If a tool changes something, say so in its name and description.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Scan the folder you are about to publish
&lt;/h2&gt;

&lt;p&gt;Last, look at the folder itself. A free MIT-licensed command lists supported leak patterns in a folder's text files, with the values masked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 zero_trace_lite.py .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our independent test it detected 32 of 32 planted samples (a figure for planted samples, not a detection rate for real folders). None found is not proof that nothing is there. The command reports only; it does not stop a commit.&lt;/p&gt;

&lt;p&gt;To try it in a browser, there is a scanner you drop a folder onto (the check runs in the browser and the files under inspection are not sent): &lt;a href="https://zero-trace-ship-kit.zeus-lab-global.workers.dev/?utm_source=devto" rel="noopener noreferrer"&gt;https://zero-trace-ship-kit.zeus-lab-global.workers.dev/?utm_source=devto&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  In short
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If it connects and drops, suspect standard output first.&lt;/li&gt;
&lt;li&gt;Keys come from the environment; the repository carries a placeholder.&lt;/li&gt;
&lt;li&gt;Scan the folder once, right before release.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was drafted with AI assistance (Claude) and checked against the measured results and the published files. Product links lead to our own paid kits.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>security</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>We counted ignore rules in 100 public AI and MCP repositories (no names, script included)</title>
      <dc:creator>ZEUS LAB</dc:creator>
      <pubDate>Thu, 12 Nov 2026 14:20:10 +0000</pubDate>
      <link>https://dev.to/zeuslab/we-counted-ignore-rules-in-100-public-ai-and-mcp-repositories-no-names-script-included-418p</link>
      <guid>https://dev.to/zeuslab/we-counted-ignore-rules-in-100-public-ai-and-mcp-repositories-no-names-script-included-418p</guid>
      <description>&lt;p&gt;A missing line in &lt;code&gt;.gitignore&lt;/code&gt; is not a leak. But it is the first thing between a secret and a public history, so we wanted to know how often the basic lines are actually there.&lt;/p&gt;

&lt;p&gt;On 2026-11-12 we took 100 active public AI and MCP repositories and counted. We did not judge, and we name no repository here or in any file we publish. Nothing was reported to anyone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The page with every count, the method and the script: &lt;a href="https://zero-trace-ship-kit.zeus-lab-global.workers.dev/report/?utm_source=devto" rel="noopener noreferrer"&gt;https://zero-trace-ship-kit.zeus-lab-global.workers.dev/report/?utm_source=devto&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What we counted
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Have a &lt;code&gt;.gitignore&lt;/code&gt; at the root&lt;/td&gt;
&lt;td&gt;97 / 100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Have an &lt;code&gt;.env&lt;/code&gt; pattern in it&lt;/td&gt;
&lt;td&gt;76 / 100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Have a &lt;code&gt;.claude&lt;/code&gt; / &lt;code&gt;settings.local.json&lt;/code&gt; pattern in it&lt;/td&gt;
&lt;td&gt;59 / 100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Have a key-file or secrets pattern in it&lt;/td&gt;
&lt;td&gt;26 / 100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Have a &lt;code&gt;Dockerfile&lt;/code&gt; at the root&lt;/td&gt;
&lt;td&gt;30 / 100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...of those, have a &lt;code&gt;.dockerignore&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;28 / 30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...of those, keep &lt;code&gt;.env&lt;/code&gt; out of the build context&lt;/td&gt;
&lt;td&gt;18 / 30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...of those, keep &lt;code&gt;.git&lt;/code&gt; out of the build context&lt;/td&gt;
&lt;td&gt;21 / 30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keep Claude Code settings in the repository (&lt;code&gt;.claude/&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;12 / 100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...of those, ignore the local settings file&lt;/td&gt;
&lt;td&gt;11 / 12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Have a file named &lt;code&gt;.env&lt;/code&gt; on the default branch (existence only)&lt;/td&gt;
&lt;td&gt;1 / 100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Have &lt;code&gt;.claude/settings.local.json&lt;/code&gt; on the default branch (existence only)&lt;/td&gt;
&lt;td&gt;2 / 100&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The one that stood out to us
&lt;/h2&gt;

&lt;p&gt;The Docker rows. A &lt;code&gt;.dockerignore&lt;/code&gt; is nearly always present when there is a &lt;code&gt;Dockerfile&lt;/code&gt; (28 of 30), yet 12 of those 30 have no &lt;code&gt;.env&lt;/code&gt; pattern in it. &lt;code&gt;.gitignore&lt;/code&gt; keeps a file out of Git; it does not keep it out of &lt;code&gt;docker build .&lt;/code&gt;. If an image built on a developer machine is pushed, a local &lt;code&gt;.env&lt;/code&gt; can travel inside a layer. Whether it does depends on the &lt;code&gt;COPY&lt;/code&gt; lines, which we did not read.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to read these numbers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An absent rule is not a leak. The file may not exist, a global ignore may cover it, or habit may.&lt;/li&gt;
&lt;li&gt;"Pattern present" is a line-by-line match of common spellings, not a full implementation of gitignore semantics.&lt;/li&gt;
&lt;li&gt;For the two "existence only" rows we asked whether the path exists on the default branch. We never downloaded the contents, so a file named &lt;code&gt;.env&lt;/code&gt; may be a harmless placeholder.&lt;/li&gt;
&lt;li&gt;The sample moves daily. A later run gives somewhat different counts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The sample and the method
&lt;/h2&gt;

&lt;p&gt;Public, non-archived, non-fork repositories with at least 50 stars, pushed to within 30 days, from GitHub's public search for four topics (mcp, ai-agents, claude-code, llm), merged in turn until 100. For each repository two public files are read (&lt;code&gt;.gitignore&lt;/code&gt;, &lt;code&gt;.dockerignore&lt;/code&gt;) and five paths are checked for existence. No key, no account: Python standard library plus curl. The script is on the report page; run it and you get today's counts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check your own folder
&lt;/h2&gt;

&lt;p&gt;Three lines cover most of it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .gitignore
.env
.env.*
.claude/settings.local.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the same &lt;code&gt;.env&lt;/code&gt; lines in &lt;code&gt;.dockerignore&lt;/code&gt;. To see what a folder would give away before you publish it, the free scanner runs in the browser (the files are not uploaded) and the command-line edition is MIT: &lt;a href="https://zero-trace-ship-kit.zeus-lab-global.workers.dev/?utm_source=devto" rel="noopener noreferrer"&gt;https://zero-trace-ship-kit.zeus-lab-global.workers.dev/?utm_source=devto&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was drafted with AI assistance (Claude) and checked against the measured results and the published files. Product links lead to our own paid kits.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>opensource</category>
      <category>security</category>
    </item>
    <item>
      <title>Wiring gates, a second-AI review and a live board into Claude Code work (the hooks, and three things an audit made us fix)</title>
      <dc:creator>ZEUS LAB</dc:creator>
      <pubDate>Thu, 12 Nov 2026 11:30:38 +0000</pubDate>
      <link>https://dev.to/zeuslab/wiring-gates-a-second-ai-review-and-a-live-board-into-claude-code-work-the-hooks-and-three-2fo1</link>
      <guid>https://dev.to/zeuslab/wiring-gates-a-second-ai-review-and-a-live-board-into-claude-code-work-the-hooks-and-three-2fo1</guid>
      <description>&lt;p&gt;It is late, and the agents are still working. You wait for a reply, carry it to another AI, then check what comes back. Between those handoffs, the hours you meant to spend creating quietly unravel.&lt;/p&gt;

&lt;p&gt;This post shows the wiring we use to take those handoffs out of our hands: four small pieces around Claude Code, each a short standard-library Python script. It also shows what went wrong when we pointed the same wiring at our own product page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The board, running with sample data, and the diagram: &lt;a href="https://zero-trace-ship-kit.zeus-lab-global.workers.dev/harness/?utm_source=devto" rel="noopener noreferrer"&gt;https://zero-trace-ship-kit.zeus-lab-global.workers.dev/harness/?utm_source=devto&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&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%2Fod70a2w9isfyfckuc7is.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%2Fod70a2w9isfyfckuc7is.png" alt="The whole setup on one sheet: nine stages, every box a real file, page, command or listing" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Every box is something that exists in the repository or at a public address; the lines show stage-to-stage flow. Zoomable version on the page above.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The four pieces
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Piece&lt;/th&gt;
&lt;th&gt;Claude Code mechanism&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tool guard&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PreToolUse&lt;/code&gt; hook&lt;/td&gt;
&lt;td&gt;Refuses a call before it runs when its text matches a rule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stop report&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Stop&lt;/code&gt; hook&lt;/td&gt;
&lt;td&gt;Runs the commit checks when the agent stops and says which one does not pass yet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Review relay&lt;/td&gt;
&lt;td&gt;a script, no hook&lt;/td&gt;
&lt;td&gt;Hands a text to a second AI's CLI and saves the answer as a file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event board&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PostToolUse&lt;/code&gt; / &lt;code&gt;SubagentStop&lt;/code&gt; hooks&lt;/td&gt;
&lt;td&gt;Writes one line per event; a local page shows them&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The hook entries in &lt;code&gt;.claude/settings.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Bash|Edit|Write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"python3 \"$CLAUDE_PROJECT_DIR/hooks/guard.py\""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Edit|Write|Bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"python3 \"$CLAUDE_PROJECT_DIR/hooks/log_event.py\""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;"Stop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"python3 \"$CLAUDE_PROJECT_DIR/hooks/commit_gate.py\" --report"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]&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;h2&gt;
  
  
  The guard is a text match, and that is all it is
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;PreToolUse&lt;/code&gt; hook receives the tool name and its input as JSON on stdin. Exit code 2 refuses the call and the message on stderr goes back to the agent. Ours checks two things: a metered API key variable being given a value or read, and a commit command that carries the flag for skipping checks.&lt;/p&gt;

&lt;p&gt;It does not follow what a script does once it runs. Other ways around a check are not matched. It is a seat belt for the agent's own commands, not a sandbox. We say this plainly because the first version of our page did not (see below).&lt;/p&gt;

&lt;p&gt;It also stops its author. While writing this article, the guard refused our own shell command, because the command contained an example of the very phrase it looks for. We narrowed that rule to shell commands, so prose in a file no longer trips it, and wrote the fix with the phrase split in two. Loosening the rule to get through was not an option we allow ourselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Only names are logged
&lt;/h2&gt;

&lt;p&gt;The event hook writes the kind of event, a name (tool, check or agent), a state and a duration. Tool inputs and outputs are never written. A test plants a value in a command and asserts that the log does not contain it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Relay: a second AI reads it, a guard runs first
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;relay/cross_ai_audit.sh&lt;/code&gt; runs the commit checks, then passes the text to each configured CLI in turn (for us: a Gemini CLI, then a Codex CLI, both under flat-rate sign-ins — no metered API key is set or read), then writes a packet for a detached auditor role. Before anything is sent, a guard command runs on the text; if it fails, nothing is sent. What the second AI's service does with the text is governed by that service's terms, under your account.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the reviews found in our own listing
&lt;/h2&gt;

&lt;p&gt;We ran the relay on the store text of this kit before publishing. Both reviewers returned FIX. Then a detached auditor role unpacked the packages and ran their tests. The findings we acted on:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A stale license inside the bundle.&lt;/strong&gt; It still said "two kits" after a third was added. The checksums were right and the tests passed; only a reader could catch it. Fixed, version raised.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Per year" next to "one-time".&lt;/strong&gt; An allowance that covers 12 months from purchase read like a yearly renewal. Reworded to "within 12 months of purchase, 25 in total".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Enforced hooks".&lt;/strong&gt; The auditor tried four ways around the guard and they passed. The word came out, and the page now says exactly what is matched.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of these were caught by a mechanical check. All three were about words.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measured, on our machine
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;10 tests in the kit: install, merge and removal of settings; no input in the log; the guard; the stop report; a real blocked commit; the relay with its guard; the cross-AI audit; the board server.&lt;/li&gt;
&lt;li&gt;36 files including the checksum list. Python standard library only.&lt;/li&gt;
&lt;li&gt;Tested on macOS with Python 3.14 and git 2.x. Windows has not been tested.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limits
&lt;/h2&gt;

&lt;p&gt;A passing check is not a guarantee. The board shows the events it receives; it does not watch any AI service by itself. Nothing here promises results. AI subscriptions are separate from the kit and are yours.&lt;/p&gt;

&lt;p&gt;The whole set — hooks, roles, skills, relay, board, diagram generator and installer — is the ZEUS LAB Autonomous Harness &amp;amp; Telemetry Kit, $149 one-time: &lt;a href="https://zero-trace-ship-kit.zeus-lab-global.workers.dev/harness/?utm_source=devto" rel="noopener noreferrer"&gt;https://zero-trace-ship-kit.zeus-lab-global.workers.dev/harness/?utm_source=devto&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was drafted with AI assistance (Claude) and checked against the measured results and the published files. Product links lead to our own paid kits.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>automation</category>
      <category>claude</category>
      <category>python</category>
    </item>
    <item>
      <title>Counting which comes first, the loss limit or the profit target: an in-browser Monte Carlo, cross-checked by a Python twin</title>
      <dc:creator>ZEUS LAB</dc:creator>
      <pubDate>Thu, 12 Nov 2026 07:27:44 +0000</pubDate>
      <link>https://dev.to/zeuslab/counting-which-comes-first-the-loss-limit-or-the-profit-target-an-in-browser-monte-carlo-53b9</link>
      <guid>https://dev.to/zeuslab/counting-which-comes-first-the-loss-limit-or-the-profit-target-an-in-browser-monte-carlo-53b9</guid>
      <description>&lt;p&gt;Late at night, you open the trade history again. After all those days of work, one large loss holds your attention. Before closing the screen, you want to look once more at the limits you chose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The calculator runs in your browser, free, with zero network requests: &lt;a href="https://zero-trace-ship-kit.zeus-lab-global.workers.dev/quant/?utm_source=devto" rel="noopener noreferrer"&gt;https://zero-trace-ship-kit.zeus-lab-global.workers.dev/quant/?utm_source=devto&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Take an account with rules such as "lose no more than 5% in a day, no more than 10% in total, done when profit reaches 8%". With the same win rate and payoff ratio, &lt;strong&gt;changing only the risk per trade reorders which rule you hit first&lt;/strong&gt;. This post implements a small Monte Carlo that counts it in plain JavaScript, then writes the same model again in Python and uses agreement between the two as the check.&lt;/p&gt;

&lt;p&gt;Everything here is arithmetic on numbers you enter. It is not a recommendation to trade, not a forecast and not investment advice, and it names no firm or product.&lt;/p&gt;

&lt;h2&gt;
  
  
  The model
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A trade is &lt;code&gt;+payoff × risk&lt;/code&gt; with probability &lt;code&gt;p&lt;/code&gt;, otherwise &lt;code&gt;−risk&lt;/code&gt;. Trades are independent.&lt;/li&gt;
&lt;li&gt;Risk is a constant share of the start balance. The balance starts at 100.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;n&lt;/code&gt; trades a day for &lt;code&gt;D&lt;/code&gt; days.&lt;/li&gt;
&lt;li&gt;Rules are checked &lt;strong&gt;after every trade&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;maximum loss: balance at or below &lt;code&gt;100 × (1 − M)&lt;/code&gt; (from the peak when trailing)&lt;/li&gt;
&lt;li&gt;daily loss limit: fall from the day's opening balance of at least &lt;code&gt;opening × d&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;profit target: balance at or above &lt;code&gt;100 + T&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The first one to happen &lt;strong&gt;stops the path&lt;/strong&gt;. The balance stays at its value from then on, and the maximum drawdown is measured up to that moment. The balance cannot go below 0.&lt;/li&gt;
&lt;li&gt;A trade that reaches the daily limit and the maximum loss together is counted separately as "both".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Real trading has variable outcomes, streaks, fees and slippage. The model has none of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation (JavaScript, no dependencies)
&lt;/h2&gt;

&lt;p&gt;The generator is the seedable &lt;code&gt;mulberry32&lt;/code&gt;. &lt;code&gt;Math.random()&lt;/code&gt; cannot be seeded, so a result could not be reproduced.&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;mulberry32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&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="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;|=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mh"&gt;0x6D2B79F5&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;4294967296&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;simulate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;rnd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;mulberry32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;win&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;win&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;gain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;risk&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;risk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;bDaily&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;bMax&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;bBoth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="n"&gt;bal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;peak&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;dayStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tpd&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;bal&lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="nf"&gt;rnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;win&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="n"&gt;gain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;bal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bal&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;peak&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;peak&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;hitMax&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trail&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="n"&gt;peak&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maxl&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;hitDaily&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dayStart&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bal&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="n"&gt;dayStart&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;daily&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hitMax&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;hitDaily&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// same trade reaches both&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hitMax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hitDaily&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bal&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;                                        &lt;span class="c1"&gt;// the path stops at the first event&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;bDaily&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;bMax&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;bBoth&lt;/span&gt;&lt;span class="o"&gt;++&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="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;runs&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;bDaily&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;runs&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;bMax&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;runs&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;both&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;bBoth&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;runs&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&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;h2&gt;
  
  
  Results (calculated values)
&lt;/h2&gt;

&lt;p&gt;Win rate 45%, payoff 1.5, 3 trades a day, 30 days, daily limit 5%, maximum loss 10% from the start balance, target 8%, 5,000 paths, seed 1. Only the &lt;strong&gt;risk per trade&lt;/strong&gt; changes.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Risk per trade&lt;/th&gt;
&lt;th&gt;Target first&lt;/th&gt;
&lt;th&gt;Daily limit first&lt;/th&gt;
&lt;th&gt;Maximum loss first&lt;/th&gt;
&lt;th&gt;Both at once&lt;/th&gt;
&lt;th&gt;None&lt;/th&gt;
&lt;th&gt;Median max drawdown&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0.5%&lt;/td&gt;
&lt;td&gt;46.8%&lt;/td&gt;
&lt;td&gt;0.0%&lt;/td&gt;
&lt;td&gt;1.3%&lt;/td&gt;
&lt;td&gt;0.0%&lt;/td&gt;
&lt;td&gt;51.9%&lt;/td&gt;
&lt;td&gt;3.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1%&lt;/td&gt;
&lt;td&gt;76.9%&lt;/td&gt;
&lt;td&gt;0.0%&lt;/td&gt;
&lt;td&gt;12.8%&lt;/td&gt;
&lt;td&gt;0.0%&lt;/td&gt;
&lt;td&gt;10.3%&lt;/td&gt;
&lt;td&gt;5.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2%&lt;/td&gt;
&lt;td&gt;52.1%&lt;/td&gt;
&lt;td&gt;46.9%&lt;/td&gt;
&lt;td&gt;0.2%&lt;/td&gt;
&lt;td&gt;0.8%&lt;/td&gt;
&lt;td&gt;0.0%&lt;/td&gt;
&lt;td&gt;5.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3%&lt;/td&gt;
&lt;td&gt;46.3%&lt;/td&gt;
&lt;td&gt;52.9%&lt;/td&gt;
&lt;td&gt;0.1%&lt;/td&gt;
&lt;td&gt;0.7%&lt;/td&gt;
&lt;td&gt;0.0%&lt;/td&gt;
&lt;td&gt;5.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The maximum drawdown is measured up to the moment the path stops. A "0.0%" in the table means it did not occur in 5,000 paths, not that it cannot happen.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At 0.5% the rules are almost never touched, but half the paths do not reach the target in 30 days either.&lt;/li&gt;
&lt;li&gt;At 2%, "daily limit first" jumps from 0.0% to 46.9%. The reason is plain: three losses in a day are &lt;code&gt;3 × 2% = 6%&lt;/code&gt;, past the 5% limit. At 1%, three losses are 3%, so in this model the daily limit cannot be reached at all.&lt;/li&gt;
&lt;li&gt;So under these inputs the shape of the result is set by whether &lt;strong&gt;trades per day × risk per trade&lt;/strong&gt; crosses the daily limit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Across seeds 1, 2 and 3 the "target first" share at 1% risk was 76.9%, 76.4% and 75.5%. With 5,000 paths the sampling error of a share &lt;code&gt;q&lt;/code&gt; is &lt;code&gt;√(q(1−q)/5000)&lt;/code&gt;, about ±0.6 points at 77% (one standard error, from the formula). That covers the random draw only, not errors in the assumptions.&lt;/p&gt;

&lt;p&gt;With a trailing maximum loss, "maximum loss first" at 1% risk goes from 12.8% to 20.4%.&lt;/p&gt;

&lt;h2&gt;
  
  
  The check: write the model twice
&lt;/h2&gt;

&lt;p&gt;A simulation produces plausible numbers even when it is wrong. So the same model is written independently in Python and compared with the JavaScript result. &lt;code&gt;mulberry32&lt;/code&gt; is 32-bit integer arithmetic; in Python that is done with masks.&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="no"&gt;M&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0xFFFFFFFF&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mulberry32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="no"&gt;M&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;nonlocal&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mh"&gt;0x6D2B79F5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="no"&gt;M&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="no"&gt;M&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;61&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="no"&gt;M&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="no"&gt;M&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="no"&gt;M&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4294967296&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rnd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the same random sequence and the same order of floating-point operations, the two must agree to the last bit. The page writes its results as JSON when opened with &lt;code&gt;?selftest=1&lt;/code&gt;; headless Chrome opens it and a script compares.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;quant model: AGREE — 5 cases · largest difference 0.00e+00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five cases of 5,000 paths each, largest difference zero (Python 3.14, Chrome, an Apple Silicon Mac, one run). A difference stops the release. This catches slips such as swapping the order of the rule checks on one side.&lt;/p&gt;

&lt;p&gt;There is also a boundary test. With a 20% win rate, a payoff of 0.3, 5% risk and 120 days, nearly every path is disqualified within days. An implementation that keeps computing after disqualification piles up fixed-size losses and reports a maximum drawdown far above 100% (our first version had this mistake). With the stop in place, the largest maximum drawdown in the sample under these inputs is 13.95%, inside "maximum loss 10% + one 5% risk". The check stops the release if that bound is exceeded. It does not catch a misunderstanding shared by both implementations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use and limits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Calculator: &lt;a href="https://zero-trace-ship-kit.zeus-lab-global.workers.dev/quant/?utm_source=devto" rel="noopener noreferrer"&gt;https://zero-trace-ship-kit.zeus-lab-global.workers.dev/quant/?utm_source=devto&lt;/a&gt; — move a slider and it recomputes. Nothing you enter is sent anywhere; the page's CSP forbids network requests.&lt;/li&gt;
&lt;li&gt;Account rules differ between providers and change. Enter the numbers from the current official rules.&lt;/li&gt;
&lt;li&gt;Win rate and payoff are assumed numbers.&lt;/li&gt;
&lt;li&gt;This counts how often the loss limits are reached under the conditions and rules you enter. It does not forecast sudden market moves and does not reproduce any firm's actual rule evaluation.&lt;/li&gt;
&lt;li&gt;A tool that does the same counting on your own trade-history CSV, resampling whole days, is in the paid kit described on the same page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Advertisement (affiliate link): if you sign up through this link, ZEUS LAB may receive compensation. It leads to TradingView's Black Friday information page; check the link for availability, eligible plans, prices and conditions. You do not need any subscription to use this calculator. &lt;a href="https://www.tradingview.com/black-friday/?aff_id=1174521" rel="noopener noreferrer"&gt;TradingView&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once more: this is arithmetic and says nothing about what or how to trade. Trading can lose all of the money you commit.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was drafted with AI assistance (Claude) and checked against the measured results and the published files. Product links lead to our own paid kits.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>fintech</category>
      <category>javascript</category>
      <category>python</category>
    </item>
    <item>
      <title>Claude Code's "Don't ask again" can retain secrets. Check your files before pushing.</title>
      <dc:creator>ZEUS LAB</dc:creator>
      <pubDate>Wed, 11 Nov 2026 22:24:52 +0000</pubDate>
      <link>https://dev.to/zeuslab/why-agent-bills-grow-with-the-square-of-the-steps-and-a-pre-commit-gate-that-ships-zero-personal-5dej</link>
      <guid>https://dev.to/zeuslab/why-agent-bills-grow-with-the-square-of-the-steps-and-a-pre-commit-gate-that-ships-zero-personal-5dej</guid>
      <description>&lt;p&gt;Your hand pauses over Publish. Hours of careful work are ready to leave your machine—but did something private find its way in? The screen goes dark, and the question follows you.&lt;/p&gt;

&lt;p&gt;Clicked "Don't ask again" in Claude Code? Check the settings file before publishing.&lt;/p&gt;

&lt;p&gt;Research published in November 2026 showed that approved commands can leave embedded secrets in &lt;code&gt;.claude/settings.local.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The file is normally excluded from Git, but adding an already tracked file to &lt;code&gt;.gitignore&lt;/code&gt; does not untrack it.&lt;/p&gt;

&lt;p&gt;Start by scanning the folder you plan to publish, including its AI configuration files.&lt;/p&gt;

&lt;p&gt;The free Zero-Trace Ship Kit scanner processes selected files inside your browser. The files you scan are not uploaded to a server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://zero-trace-ship-kit.zeus-lab-global.workers.dev/?utm_source=devto#scanner" rel="noopener noreferrer"&gt;Scan your files for free&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With the full kit's Git hooks installed and enabled, supported findings stop the commit or push.&lt;/p&gt;

&lt;p&gt;$39 for an individual, or $149 for Team use by up to 10 people in one organization. One-time purchase.&lt;/p&gt;

&lt;p&gt;"No findings" means no supported pattern was detected; it does not prove that the files contain no secrets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demo (25 seconds): found → commit stopped → fixed → passed.&lt;/strong&gt; The full kit run in a real git repository with made-up data planted in it, exit codes included. &lt;a href="https://zero-trace-ship-kit.zeus-lab-global.workers.dev/?utm_source=devto#demo" rel="noopener noreferrer"&gt;https://zero-trace-ship-kit.zeus-lab-global.workers.dev/?utm_source=devto#demo&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you get&lt;/th&gt;
&lt;th&gt;Free&lt;/th&gt;
&lt;th&gt;Full kit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Browser scanner&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text-file report command&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blocks commit and push automatically&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Checks ZIPs inside ZIPs, photo GPS, PDF and Office authors&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cleaned distribution ZIP with a SHA-256 manifest&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Carry this check into your next commit. Automate pre-commit and pre-push scanning with the full kit.&lt;br&gt;
Enabled hooks block supported findings. Cleaned distribution ZIP generation is included.&lt;br&gt;
Individual $39 / Team $149 for up to 10 people in one organization. One-time purchase. No subscription.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://zeuslab.gumroad.com/l/zero-trace-ship-kit?utm_source=devto" rel="noopener noreferrer"&gt;Buy Individual — $39 / Team — $149, up to 10 people&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  1. What is happening
&lt;/h2&gt;

&lt;p&gt;When you approve a command in Claude Code with "don't ask again", the approved command is saved &lt;strong&gt;as a literal string&lt;/strong&gt; under &lt;code&gt;permissions.allow&lt;/code&gt; in the project's &lt;code&gt;.claude/settings.local.json&lt;/code&gt;. If the command carried a password or token inline, that is saved too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Bash(PGPASSWORD=... psql -h db.example.com -U app)"&lt;/span&gt;&lt;span class="p"&gt;]&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;The file is personal, and Git ignores it when Claude Code creates it. If it was created by hand, or added with &lt;code&gt;git add&lt;/code&gt; before the ignore rule existed, it stays tracked and gets pushed. A tracked file is not removed by adding it to &lt;code&gt;.gitignore&lt;/code&gt; later.&lt;/p&gt;

&lt;p&gt;Several write-ups described this in November 2026 (&lt;a href="https://labs.cloudsecurityalliance.org/research/csa-research-note-claude-code-settings-secret-leak-20261108/" rel="noopener noreferrer"&gt;Cloud Security Alliance research note&lt;/a&gt;, &lt;a href="https://blog.gitguardian.com/claude-code-settings-local-json-secrets-docker-hub/" rel="noopener noreferrer"&gt;GitGuardian&lt;/a&gt;). The figure of 2,539 widely quoted from Apiiro's November 5 report counts secrets gathered from settings files, Git history and agent transcripts; not all of them are keys confirmed to be valid. For current copies of &lt;code&gt;settings.local.json&lt;/code&gt; alone, the reported figure is 1,122 of 104,185 public files (about 1.1%). These are the company's findings as reported; we have not verified them independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Check your own repository
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# list tracked AI configuration files&lt;/span&gt;
git ls-files .claude

&lt;span class="c"&gt;# was it ever committed?&lt;/span&gt;
git log --oneline --all -- .claude/settings.local.json

&lt;span class="c"&gt;# stop tracking it (the file stays on disk)&lt;/span&gt;
git rm --cached .claude/settings.local.json
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".claude/settings.local.json"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A credential that reached history or a remote cannot be taken back by deleting the file. Revoke it at the issuer and create a new one.&lt;/p&gt;

&lt;p&gt;For the contents, dropping the folder into the browser scanner is the quick way. It detects secrets inside the permission rules of &lt;code&gt;.claude/settings.local.json&lt;/code&gt; and &lt;code&gt;.claude/settings.json&lt;/code&gt;, the &lt;code&gt;.claude/.credentials.json&lt;/code&gt; file, inline passwords such as &lt;code&gt;PGPASSWORD=...&lt;/code&gt;, &lt;code&gt;MYSQL_PWD=...&lt;/code&gt;, &lt;code&gt;REDISCLI_AUTH=...&lt;/code&gt; and &lt;code&gt;--password=...&lt;/code&gt;, and database URIs that carry a password. Obvious placeholders such as &lt;code&gt;&amp;lt;password&amp;gt;&lt;/code&gt; or &lt;code&gt;$DB_PASS&lt;/code&gt; are not reported.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Traces that end up in what you publish
&lt;/h2&gt;

&lt;p&gt;When an agent works on your machine, logs, configs, error output and test results pick up details of that machine. Typical leftovers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Kind&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Absolute home paths&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/Users/&amp;lt;login&amp;gt;/...&lt;/code&gt;, &lt;code&gt;/home/&amp;lt;login&amp;gt;/...&lt;/code&gt;, &lt;code&gt;C:\Users\&amp;lt;login&amp;gt;\...&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keys&lt;/td&gt;
&lt;td&gt;service API keys, anything starting &lt;code&gt;BEGIN ... PRIVATE KEY&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E-mail addresses&lt;/td&gt;
&lt;td&gt;a contact line in a README, the commit identity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image metadata&lt;/td&gt;
&lt;td&gt;JPEG EXIF (can include GPS), PNG text chunks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Document authors&lt;/td&gt;
&lt;td&gt;PDF &lt;code&gt;Author&lt;/code&gt;, Office creator fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OS residue&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.DS_Store&lt;/code&gt;, &lt;code&gt;__MACOSX/&lt;/code&gt;, &lt;code&gt;Thumbs.db&lt;/code&gt;, &lt;code&gt;.env&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Git identity&lt;/td&gt;
&lt;td&gt;commits made under a personal name and address&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ZIPs are where this gets missed: people review the working folder, not the archive they built from it, and certainly not a ZIP inside that ZIP.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The gate
&lt;/h2&gt;

&lt;p&gt;The policy is one line: if anything is found, the commit or push fails.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 Known shapes: regular expressions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="no"&gt;HOME_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="s2"&gt;"(?&amp;lt;![\w.])(?:/Users/|/home/|[A-Za-z]:\\{1,2}Users\\{1,2})([A-Za-z0-9][A-Za-z0-9._-]*)"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The leading &lt;code&gt;(?&amp;lt;![\w.])&lt;/code&gt; keeps URLs such as &lt;code&gt;https://example.com/home/dashboard&lt;/code&gt; from matching. Placeholder and CI names such as &lt;code&gt;you&lt;/code&gt; or &lt;code&gt;runner&lt;/code&gt; are allowed.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Your own name: read at run time, never stored
&lt;/h3&gt;

&lt;p&gt;A scanner with your real name written in it is itself a leak. The gate reads the login name, the full name registered with the OS, and the global git identity from the machine on each run, matches in memory only, and masks them in its output.&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;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getpass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getuser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;full&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"-F"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# macOS: registered full name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terms are searched as UTF-8 and as UTF-16, because Office files and some binaries store names that way.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 Archives: open them all the way down
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;scan_archive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;zipfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ZipFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BytesIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;zf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;zf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;infolist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"{where}!/{info.filename}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;scan_bytes&lt;/code&gt; calls &lt;code&gt;scan_archive&lt;/code&gt; again when a member is itself a ZIP. &lt;code&gt;.docx&lt;/code&gt; and &lt;code&gt;.xlsx&lt;/code&gt; are ZIPs too, so creator fields are read through the same path.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.4 Images: walking segments is enough
&lt;/h3&gt;

&lt;p&gt;Walk the JPEG segments; an &lt;code&gt;APP1&lt;/code&gt; segment that starts with &lt;code&gt;Exif&lt;/code&gt; means EXIF is present, and tag &lt;code&gt;0x8825&lt;/code&gt; inside it means GPS data. No imaging library needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.5 Hooks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .git/hooks/pre-commit&lt;/span&gt;
&lt;span class="nb"&gt;exec&lt;/span&gt; python3 gatekeeper/zero_trace_check.py --staged --identity

&lt;span class="c"&gt;# .git/hooks/pre-push&lt;/span&gt;
&lt;span class="nb"&gt;exec&lt;/span&gt; python3 gatekeeper/zero_trace_check.py --all --identity --history --remote --path dist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pre-commit scans exactly what is staged. Pre-push scans the whole tree, commit authors and messages, the remote URL, and the git-ignored folder where distributable ZIPs are built. The same check runs as the first job in GitHub Actions; publishing jobs depend on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Verification
&lt;/h2&gt;

&lt;p&gt;A checker grading itself proves nothing, so a separate program builds a throw-away corpus of planted leaks and clean look-alikes and runs the gate over it. Planted values are assembled at run time, so the probe itself passes the gate.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Files with a planted leak&lt;/td&gt;
&lt;td&gt;32 of 32 detected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clean look-alikes&lt;/td&gt;
&lt;td&gt;0 false positives out of 17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time&lt;/td&gt;
&lt;td&gt;about 12 to 18 ms across two runs (Python 3.14, Apple Silicon Mac, single run)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The planted set: inline passwords (the environment-variable form and &lt;code&gt;--password=&lt;/code&gt;), a database URI with a password, AI settings files (&lt;code&gt;settings.local.json&lt;/code&gt; itself, a token inside a permission rule of a shared &lt;code&gt;settings.json&lt;/code&gt;, &lt;code&gt;.credentials.json&lt;/code&gt;), BIP-39 recovery phrases (12 and 24 words), an EVM private key assigned to a key name, a Solana keypair file, an exchange API secret, absolute paths for three operating systems, four kinds of key and private key, an e-mail address, a hard-coded password, a JPEG with EXIF and GPS, a PNG with a text chunk, &lt;code&gt;.DS_Store&lt;/code&gt;, &lt;code&gt;.env&lt;/code&gt;, a PDF with an author, a ZIP containing a path, a ZIP inside a ZIP containing a key, a ZIP with &lt;code&gt;__MACOSX&lt;/code&gt;, a truncated ZIP, a &lt;code&gt;.docx&lt;/code&gt; with a creator, a binary holding a private term in UTF-16, and a file name containing a private term.&lt;/p&gt;

&lt;p&gt;The clean set includes database documentation that holds placeholders only (&lt;code&gt;&amp;lt;your-password&amp;gt;&lt;/code&gt;, &lt;code&gt;$DB_PASS&lt;/code&gt;, &lt;code&gt;changeme&lt;/code&gt;, &lt;code&gt;user:password@localhost&lt;/code&gt;), a &lt;code&gt;.claude/settings.json&lt;/code&gt; without secrets, a SHA-256 value (the same 64 hex digits as a key), ordinary prose that happens to use wordlist words, placeholder paths like &lt;code&gt;/Users/you/&lt;/code&gt;, &lt;code&gt;actions/checkout@v4&lt;/code&gt;, &lt;code&gt;logo@2x.png&lt;/code&gt;, &lt;code&gt;example.com&lt;/code&gt; addresses, a URL containing &lt;code&gt;/home/&lt;/code&gt;, and code that only reads credentials from the environment.&lt;/p&gt;

&lt;p&gt;The raw result is published as is: &lt;a href="https://zero-trace-ship-kit.zeus-lab-global.workers.dev/evidence/selftest.json" rel="noopener noreferrer"&gt;https://zero-trace-ship-kit.zeus-lab-global.workers.dev/evidence/selftest.json&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Limits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Free-form information is not detected. A real name or address in prose is only caught if it is a term read from the machine or one you added to the local deny-list.&lt;/li&gt;
&lt;li&gt;Key detection covers common formats only.&lt;/li&gt;
&lt;li&gt;Text visible inside an image is not read.&lt;/li&gt;
&lt;li&gt;"Zero findings" means none of the shapes this gate knows are present. It is not a proof of safety.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The whole setup
&lt;/h2&gt;

&lt;p&gt;The arrangement that moves the check from attention to a gate on your own machine, on one page. Numbers are measured in our own tests.&lt;/p&gt;

&lt;p&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%2F08z9u1tg4ffqkvukhofq.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%2F08z9u1tg4ffqkvukhofq.png" alt="One conductor, AI-free gates, independent audit" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A command approved with "don't ask again" is stored as a literal string. Do not approve commands that carry a secret inline.&lt;/li&gt;
&lt;li&gt;A tracked file is not removed by &lt;code&gt;.gitignore&lt;/code&gt;. Check with &lt;code&gt;git ls-files .claude&lt;/code&gt;, untrack with &lt;code&gt;git rm --cached&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A credential that got out is revoked, not deleted.&lt;/li&gt;
&lt;li&gt;Do the check at commit and push, not by attention. Open archives all the way down.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The browser scanner and a lite command-line report for text files are free: &lt;a href="https://zero-trace-ship-kit.zeus-lab-global.workers.dev/?utm_source=devto" rel="noopener noreferrer"&gt;https://zero-trace-ship-kit.zeus-lab-global.workers.dev/?utm_source=devto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The free edition (lite command and local MCP server) is on GitHub: &lt;a href="https://github.com/ZEUSLABZERO/zero-trace-lite" rel="noopener noreferrer"&gt;https://github.com/ZEUSLABZERO/zero-trace-lite&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The full kit with the gate that blocks commit and push and the ZIP packager: &lt;a href="https://zeuslab.gumroad.com/l/zero-trace-ship-kit?utm_source=devto" rel="noopener noreferrer"&gt;https://zeuslab.gumroad.com/l/zero-trace-ship-kit?utm_source=devto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The full kit also includes a local MCP server so that Claude Desktop or Cursor can call the gate. It has no external package dependencies, but that does not remove supply-chain risk as a whole (Corelight reported in November 2026 that MCP server dependencies can change between rebuilds). Check the SHA-256 of what you download each time it is updated.&lt;/p&gt;

&lt;p&gt;The estimate of cumulative input cost for agents, and the guard that refuses a call before it runs, now live in a separate post.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was drafted with AI assistance (Claude) and checked against the measured results and the published files. Product links lead to our own paid kits.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
      <category>security</category>
    </item>
  </channel>
</rss>
