<?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: TECHDEV</title>
    <description>The latest articles on DEV Community by TECHDEV (@techdevfun).</description>
    <link>https://dev.to/techdevfun</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4188359%2Fe5cd62cb-6e0b-44dd-b1d7-3934d6cf3ef0.webp</url>
      <title>DEV Community: TECHDEV</title>
      <link>https://dev.to/techdevfun</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/techdevfun"/>
    <language>en</language>
    <item>
      <title>Build headlines said 92x faster. I measured my own crate first.</title>
      <dc:creator>TECHDEV</dc:creator>
      <pubDate>Mon, 02 Nov 2026 15:18:29 +0000</pubDate>
      <link>https://dev.to/techdevfun/build-headlines-said-92x-faster-i-measured-my-own-crate-first-5epn</link>
      <guid>https://dev.to/techdevfun/build-headlines-said-92x-faster-i-measured-my-own-crate-first-5epn</guid>
      <description>&lt;p&gt;A few weeks ago the Rust corner of my feed lit up: a new toolchain promising dramatically faster builds, one benchmark, one headline number. I have seen this movie before: a number from somebody else's crate, somebody else's machine, and then a week of arguing about whether it applies to mine.&lt;/p&gt;

&lt;p&gt;I did not want to argue about toolchains. I wanted a ruler: something that tells me, on &lt;em&gt;my&lt;/em&gt; crate and &lt;em&gt;my&lt;/em&gt; machine, where the seconds actually go (per crate, per build script, per link step), then tells me which fix is cheapest, and lets me verify the delta after I apply it.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://github.com/techdevfun/cargoscope" rel="noopener noreferrer"&gt;cargoscope&lt;/a&gt;. It measures. It recommends. It verifies. It never touches your toolchain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three commands
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;cargo cargoscope measure [--json out.json]&lt;/code&gt;&lt;/strong&gt; shells out to &lt;code&gt;cargo build --timings&lt;/code&gt; twice. The first build is timed; the second is a no-change rebuild. That second run is the trick: cargo omits fresh-skipped units from the timing report, so a build-script-run unit that still shows real duration in the no-change rebuild was &lt;em&gt;genuinely executed again with no source changes&lt;/em&gt;. Its freshness is broken: typically a script that rewrites its own &lt;code&gt;rerun-if-changed&lt;/code&gt; inputs. That is measured, not guessed from the script source.&lt;/p&gt;

&lt;p&gt;The report shows wall time, top crates by compile time, build scripts that re-run on every build, and the codegen+link share of the final binary. On stable, the report is honest about what it cannot separate: "link step not separable on stable — the bin unit's duration covers codegen+link together."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;cargo cargoscope recommend&lt;/code&gt;&lt;/strong&gt; ranks fixes cheapest-first with exact config snippets: &lt;code&gt;cargo check&lt;/code&gt; for the edit loop, fixing re-running build scripts, mold/lld linker, sccache, cranelift (gated behind &lt;code&gt;--nightly-ok&lt;/code&gt;). It changes nothing. The snippets are yours to review and apply.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;cargo cargoscope compare before.json after.json&lt;/code&gt;&lt;/strong&gt; prints the measured delta and a verdict line. Only measured numbers; nothing is promised.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real demo, honest numbers
&lt;/h2&gt;

&lt;p&gt;The animated demo below is a real terminal recording on the repo's small fixture crate (verbatim transcript also in the repo). Watch the loop: measure flags a build script with &lt;code&gt;RE-RUNS ON EVERY BUILD&lt;/code&gt;, recommend ranks the &lt;code&gt;rerun-if-changed&lt;/code&gt; fix, I apply it, measure again, compare:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgz7dft5mpof4zc1wj7da.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgz7dft5mpof4zc1wj7da.gif" alt="cargoscope demo (real terminal recording): measure flags a re-running build script, recommend ranks the fix, compare verifies -6.6%" width="800" height="328"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wall:   0.7s -&amp;gt; 0.7s  (-6.6%)
units:  5 -&amp;gt; 4
verdict: -6.6% wall time faster (0.7s -&amp;gt; 0.7s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Small crate, small numbers, no hype. The re-run flag is gone; the per-crate deltas are printed, wins and losses. That is the whole point: the tool's job is to make the &lt;em&gt;second&lt;/em&gt; measurement possible, not to impress you with the first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug the nightly CI caught
&lt;/h2&gt;

&lt;p&gt;To separate the link step, cargoscope parses nightly &lt;code&gt;-Ztime&lt;/code&gt; stderr. While dogfooding on a real nightly toolchain I found that rustc 1.101-nightly &lt;strong&gt;removed &lt;code&gt;-Ztime&lt;/code&gt; entirely&lt;/strong&gt; (&lt;code&gt;unknown unstable option&lt;/code&gt;). The old code set &lt;code&gt;RUSTFLAGS=-Ztime&lt;/code&gt; whenever it detected nightly, and cargo forwards RUSTFLAGS into its own internal rustc probe, so &lt;code&gt;measure&lt;/code&gt; broke completely on new nightlies (cargo exit 101).&lt;/p&gt;

&lt;p&gt;The fix: probe first (&lt;code&gt;rustc -Ztime --print=sysroot&lt;/code&gt;; &lt;code&gt;--version&lt;/code&gt; short-circuits before &lt;code&gt;-Z&lt;/code&gt; validation, so it cannot be the probe), set the flag only when the probe passes, and fall back gracefully otherwise. A nightly CI job now runs &lt;code&gt;measure&lt;/code&gt; on a real nightly toolchain every night and asserts the graceful fallback. Finding your tool broken on a toolchain you claimed to support is the best possible use of a nightly job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the rivals beat us
&lt;/h2&gt;

&lt;p&gt;Honesty section, as always with my tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/indexable-inc/blaze" rel="noopener noreferrer"&gt;cargo-blaze&lt;/a&gt;&lt;/strong&gt; wins if in-place recompilation fits your workflow: it actually makes rebuilds faster instead of measuring them. It is a young, invasive bet (replaces the compiler driver). cargoscope complements it: it can measure blaze's own delta.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cargo build --timings&lt;/code&gt;&lt;/strong&gt; wins on zero install. The raw HTML timeline has everything cargoscope parses. cargoscope adds ranking, re-run detection, and before/after verification on top.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cargo-bloat&lt;/code&gt;&lt;/strong&gt; answers a different question (binary size, not build time).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Honest limits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It does not speed up your builds. It measures. If you want the build itself to go faster without measuring first, this is the wrong tool.&lt;/li&gt;
&lt;li&gt;Timing numbers are your machine's, your crate's, your profile's. A −6.6% here is not a claim about any other setup.&lt;/li&gt;
&lt;li&gt;Link-step timing needs a nightly toolchain whose rustc still accepts &lt;code&gt;-Ztime&lt;/code&gt; (removed by 1.101-nightly; support is probed, never assumed).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cargo --timings&lt;/code&gt; output format is not a stable API. The parser is defensive, records the tested cargo version, and fails loudly instead of guessing.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The npm package is not published yet, so install from source for now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/techdevfun/cargoscope.git
&lt;span class="nb"&gt;cd &lt;/span&gt;cargoscope &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build
&lt;span class="c"&gt;# then, inside any Rust crate:&lt;/span&gt;
npx cargo-cargoscope measure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Requires Node 20+ and a Rust toolchain (stable is fine). Zero runtime dependencies, MIT licensed.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/techdevfun/cargoscope" rel="noopener noreferrer"&gt;github.com/techdevfun/cargoscope&lt;/a&gt;. Issues and PRs welcome; please include your &lt;code&gt;measure&lt;/code&gt; output (redacted paths are fine) when reporting wrong numbers. Also listed in the &lt;a href="https://techdev.fun" rel="noopener noreferrer"&gt;TECHDEV catalog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>typescript</category>
      <category>cli</category>
      <category>devops</category>
    </item>
    <item>
      <title>My agent burned $31 in a week. I asked which tool ate it - so I built the receipt.</title>
      <dc:creator>TECHDEV</dc:creator>
      <pubDate>Mon, 02 Nov 2026 12:11:09 +0000</pubDate>
      <link>https://dev.to/techdevfun/my-agent-burned-31-in-a-week-i-asked-which-tool-ate-it-so-i-built-the-receipt-2olh</link>
      <guid>https://dev.to/techdevfun/my-agent-burned-31-in-a-week-i-asked-which-tool-ate-it-so-i-built-the-receipt-2olh</guid>
      <description>&lt;p&gt;I can tell you my AI bill to the cent. Ask me which tool call caused it and I used to shrug.&lt;/p&gt;

&lt;p&gt;Last week I stared at my provider dashboard: a clean per-key total, zero line items. Which MCP tool ate the budget? Which framework surface did the agent re-read forty times? Was that "heavy" framework actually heavy &lt;em&gt;for my workload&lt;/em&gt;? The dashboard had no answers. Hacker News had opinions - 215 points on ChatGPT/Codex limit merges, 291 on the AI cost of JavaScript frameworks. One dev.to author cut their MCP server from 15 tools to 4 by hand-counting tokens with tiktoken. All vibes. No receipts.&lt;/p&gt;

&lt;p&gt;So I built the receipt: &lt;strong&gt;aibill&lt;/strong&gt;, a small CLI that reads your real agent session transcripts and attributes tokens and estimated USD to every tool call.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Four commands, one idea: measure first, cut second, verify third.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aibill scan ~/.claude/projects/my-project/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TOOL                   CALLS   MEAN TOK   P95 TOK   TOTAL TOK       USD*
------------------------------------------------------------------------
mcp__doc_lookup           30     23,265    26,947     697,938     $16.55
mcp__codebase_search      24     15,427    18,889     370,241      $7.81
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;aibill scan&lt;/code&gt; parses Claude Code session JSONL (or a generic MCP tool-call JSONL you feed it) and prints per-tool rows - call counts, mean and p95 tokens, totals, USD - plus per-model and per-framework-surface breakdowns. &lt;code&gt;aibill cut --top 3&lt;/code&gt; then suggests cut candidates: duplicated calls with identical inputs, tools whose huge outputs were never referenced again, costly one-shot tools. Candidates, not autopilot - it changes nothing on its own.&lt;/p&gt;

&lt;p&gt;Then the part I actually wanted: &lt;code&gt;aibill diff --before old.jsonl --after new.jsonl&lt;/code&gt; measures the before/after. On the demo run: 137 calls, 1,425,208 tokens, $31.68 estimated, one doc-lookup tool eating 52% of the bill. After trimming the obvious waste: &lt;strong&gt;-49.0% tokens, -52.3% USD - measured on the two transcripts, not promised about your future.&lt;/strong&gt; &lt;code&gt;aibill budget --max-usd 20&lt;/code&gt; exits non-zero when projected spend exceeds the cap, so the same check runs in CI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F67m9pgvyd6yrh6agi37p.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F67m9pgvyd6yrh6agi37p.gif" alt="aibill demo" width="799" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(The GIF runs on synthetic seeded fixtures - run &lt;code&gt;scan&lt;/code&gt; on your own transcripts for real numbers.)&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest fine print
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;USD is an &lt;strong&gt;estimate from public rate cards&lt;/strong&gt; (verified 2026-11-02, sources in &lt;code&gt;src/pricing.ts&lt;/code&gt;), not your invoice. Discounts, caching, tiers and regional multipliers change the real number. Unknown models price at $0 and get flagged, never guessed. Bring contracted rates with &lt;code&gt;--pricing-file&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Claude Code logs token usage per &lt;em&gt;message&lt;/em&gt;, so when one message carries several tool calls the usage is split evenly and flagged as shared attribution in the report.&lt;/li&gt;
&lt;li&gt;aibill &lt;strong&gt;measures&lt;/strong&gt;. It does not reduce tokens, rewrite prompts, or compress anything. Any "% saved" you see from &lt;code&gt;diff&lt;/code&gt; was measured on the two transcripts you gave it.&lt;/li&gt;
&lt;li&gt;It reads &lt;strong&gt;local transcripts only&lt;/strong&gt; - never calls billing APIs, never touches credentials, never sends anything anywhere. Transcripts contain your proprietary code; the tool is 100% offline by design. Zero runtime dependencies, Node 20+.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where rivals beat us
&lt;/h2&gt;

&lt;p&gt;The AWS "MCP Token Cost" piece measures something aibill does not: the &lt;strong&gt;upfront tool-schema/context tax&lt;/strong&gt; - 55k tokens just to &lt;em&gt;describe&lt;/em&gt; GitHub MCP's 93 tools before a single call happens. aibill attributes per-&lt;em&gt;call&lt;/em&gt; tokens from transcripts; it is blind to that schema tax. The two are complementary: use their number for "should this MCP server be connected at all", use aibill for "now that it is, which 3 calls are eating the bill." Provider dashboards win on authoritative totals; hand-rolled tiktoken audits win on nothing except proving the demand is real.&lt;/p&gt;

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

&lt;p&gt;Install from source for now (&lt;code&gt;aibill-cli&lt;/code&gt; isn't on npm yet - publish pending):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/techdevfun/aibill.git
&lt;span class="nb"&gt;cd &lt;/span&gt;aibill &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
aibill scan &amp;lt;your-transcript.jsonl&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point was never the $31. It's that every cost conversation about coding agents is currently vibes - headline benchmarks measured on someone else's workload, gut-feel tool cuts, surprise limit changes. A receipt won't make the bill smaller by itself. But at least now you can argue with it.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/techdevfun/aibill" rel="noopener noreferrer"&gt;https://github.com/techdevfun/aibill&lt;/a&gt; - MIT, issues and redacted-transcript bug reports welcome.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>ai</category>
      <category>cli</category>
      <category>devops</category>
    </item>
    <item>
      <title>Before you rent the GPUs, check the map: I built a CLI that picks your LLM serving engine</title>
      <dc:creator>TECHDEV</dc:creator>
      <pubDate>Sun, 01 Nov 2026 12:07:21 +0000</pubDate>
      <link>https://dev.to/techdevfun/before-you-rent-the-gpus-check-the-map-i-built-a-cli-that-picks-your-llm-serving-engine-11e8</link>
      <guid>https://dev.to/techdevfun/before-you-rent-the-gpus-check-the-map-i-built-a-cli-that-picks-your-llm-serving-engine-11e8</guid>
      <description>&lt;p&gt;DeepInfer collected ~19k stars in a single week (GitHub Trending scan 2026-10-28). The headlines say "production inference engine". My weekend said: it serves exactly one model (DeepSeek-V4.1-Flash), needs SM100+ or Ascend950 silicon, and runs offline-only with no KV cache system, no PD disaggregation, no load balancing. Checked against the upstream repo 2026-11-01.&lt;/p&gt;

&lt;p&gt;That is not a knock. That is the gap a tool should close: before you rent GPUs or rewire your stack, you need the map — what you actually have, what the model actually costs in memory, and which engines are actually viable on that combination.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://github.com/techdevfun/servefit" rel="noopener noreferrer"&gt;servefit&lt;/a&gt;: a zero-dependency Python CLI that inventories your hardware, does the memory math, checks a dated engine-support matrix, and recommends an engine with reasons. Two minutes, no account, and the offline path needs no network at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;servefit inventory&lt;/code&gt; — parses &lt;code&gt;nvidia-smi&lt;/code&gt; (GPUs, VRAM, compute capability with name fallback), AMD via &lt;code&gt;rocm-smi&lt;/code&gt;, CPU/RAM. Apple Silicon is reported as detected-with-limits, never guessed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;servefit fit --model Qwen/Qwen3-8B&lt;/code&gt; — weights + KV cache + overhead math with every assumption printed, then viable engines from a curated 6-engine matrix. Every row carries a check date (all 2026-11-01); CI fails if any row goes 90 days stale, so staleness is loud, not silent. One recommendation, one starter config. Missing production components (load balancing, disaggregation) are listed per engine, never hidden.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;servefit probe --endpoint &amp;lt;url&amp;gt;&lt;/code&gt; — OpenAI-compatible SSE probe measuring TTFT and approximate tokens/sec on your hardware.&lt;/li&gt;
&lt;li&gt;Every command also writes &lt;code&gt;--json&lt;/code&gt; manifests for automation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A real run
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8oy7u23o9aqipsdzojpa.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8oy7u23o9aqipsdzojpa.gif" alt="servefit demo" width="800" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fit&lt;/code&gt; on a Qwen3-8B lookup from the Hugging Face Hub: pulls the real arch (36 layers, 8 kv-heads, 128 head-dim) and the 8.19B param count from safetensors metadata, computes 14.9 GiB weights + 4.5 GiB KV cache at 32k context = 20.4 GiB total, and recommends SGLang; DeepInfer is ruled out with the chip and model reasons printed. The demo transcript labels what is synthetic (the inventory runs against a synthetic RTX 4090 profile) and what is scripted (the probe runs against a local fake OpenAI-compatible server); the timing it measures is this machine's, honestly labeled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method, stated honestly
&lt;/h2&gt;

&lt;p&gt;Memory math: weights = params × bytes-per-param; KV cache = 2 × layers × kv_heads × head_dim × context × bytes-per-param; plus a stated overhead line. Every assumption is printed, so you can argue with it. Hand-computed fixtures in the test suite (8B bf16: 14.9 GiB weights, 4.5 GiB KV @ 32k, 20.4 GiB total) all match; 37 tests, all green, CI across Python 3.10–3.13.&lt;/p&gt;

&lt;p&gt;The engine matrix is the opinionated part: a preference order (SGLang &amp;gt; vLLM &amp;gt; TensorRT-LLM &amp;gt; DeepInfer &amp;gt; llama.cpp &amp;gt; Ollama) gated by chip, model-family, and memory rules. Verdicts are never silent — every "viable" or "non-viable" comes with its reasons.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is NOT
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It does &lt;strong&gt;not&lt;/strong&gt; replace profiling. Memory math gets you to the right engine; real throughput comes from measuring your workload.&lt;/li&gt;
&lt;li&gt;Apple Silicon: GPU VRAM is reported unknown by design; recommendations lean on llama.cpp/Ollama with a manual RAM check note.&lt;/li&gt;
&lt;li&gt;Probe token counts are a whitespace-split approximation, labeled in the output — not the model's tokenizer.&lt;/li&gt;
&lt;li&gt;The matrix covers 6 engines checked 2026-11-01. New engines and hardware appear as rows with fresh check dates, or they fail CI.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where rivals beat us
&lt;/h2&gt;

&lt;p&gt;Honest comparison, checked 2026-11-01:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hugging Face's model pages and hub calculators&lt;/strong&gt; win on data freshness and model coverage: every new model's arch is there the day it drops. servefit does its own lookup but only maintains a curated engine matrix.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-engine docs (vLLM, SGLang, TensorRT-LLM)&lt;/strong&gt; win on depth: the full config surface, performance tuning, and multi-node guides. servefit gives one starter config, not a manual.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeepInfer&lt;/strong&gt; wins on the exact thing it was built for: serving DeepSeek-V4.1-Flash fast on SM100/Ascend hardware. servefit's matrix correctly rules it out for everything else — that is the point of the map.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;servefit&lt;/code&gt; name on PyPI is still free; until a release is published there, install from source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;git+https://github.com/techdevfun/servefit
servefit inventory
servefit fit &lt;span class="nt"&gt;--model&lt;/span&gt; Qwen/Qwen3-8B
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Roadmap: more engines, multi-GPU/NUMA topologies, and a &lt;code&gt;servefit watch&lt;/code&gt; mode that re-checks the matrix on a schedule. Repo: &lt;a href="https://github.com/techdevfun/servefit" rel="noopener noreferrer"&gt;github.com/techdevfun/servefit&lt;/a&gt;. Apache-2.0 licensed.&lt;/p&gt;

</description>
      <category>python</category>
      <category>llm</category>
      <category>devops</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>My AI writes 400-line diffs in 30 seconds. I built a 2-second gate that reviews them.</title>
      <dc:creator>TECHDEV</dc:creator>
      <pubDate>Sat, 31 Oct 2026 12:07:46 +0000</pubDate>
      <link>https://dev.to/techdevfun/my-ai-writes-400-line-diffs-in-30-seconds-i-built-a-2-second-gate-that-reviews-them-dn6</link>
      <guid>https://dev.to/techdevfun/my-ai-writes-400-line-diffs-in-30-seconds-i-built-a-2-second-gate-that-reviews-them-dn6</guid>
      <description>&lt;p&gt;My AI pair programmer writes a 400-line diff in 30 seconds. I used to spend 20 minutes reviewing it: scroll, squint, guess whether that new function is "a bit complex" or "merge-blocking complex", wonder if the helper it added already exists somewhere else in the file. Tests pass. The diff looks plausible. Ship it.&lt;/p&gt;

&lt;p&gt;A week later we find the duplicated retry logic in three places and a 14-branch function nobody remembers writing.&lt;/p&gt;

&lt;p&gt;The bottleneck in AI-assisted coding is not writing code. It is reviewing diffs at the speed the model produces them. Eyeball review does not scale to model throughput, and "tests pass" says nothing about the shape of the code that was just added.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gate the diff, not the repo
&lt;/h2&gt;

&lt;p&gt;I built &lt;a href="https://github.com/techdevfun/diffgate" rel="noopener noreferrer"&gt;diffgate&lt;/a&gt;, a zero-config CLI that checks &lt;strong&gt;changed code only&lt;/strong&gt; for complexity, duplication, and PR growth. It runs in CI in under a second and returns a verdict: pass or fail, with reasons.&lt;/p&gt;

&lt;p&gt;Four gates, all scoped to the diff (v1):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Gate&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;What it checks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;complexity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;max 10&lt;/td&gt;
&lt;td&gt;a touched function above the max fails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;delta&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;max +15&lt;/td&gt;
&lt;td&gt;a function gaining more than this at once fails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dupe&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;50 tokens&lt;/td&gt;
&lt;td&gt;a normalized token block repeated in the diff fails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;growth&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;+500 lines&lt;/td&gt;
&lt;td&gt;a single file growing past this fails ("split this PR")&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The point of scoping to the diff: pre-existing tech debt is context, never a failure. You do not get punished for the legacy function you did not touch. You get stopped when &lt;em&gt;this PR&lt;/em&gt; makes things worse.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real run
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fym53lai923ouh5vt3sm8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fym53lai923ouh5vt3sm8.gif" alt="diffgate demo" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A sample PR with three violations: a checkout function at complexity 13, a 106-token duplicated block in a Go file, and one JSON file growing by 622 lines. diffgate fails it with the exact locations. Fix the branch, re-run: PASS, all functions at complexity 5 or below. In CI it emits &lt;code&gt;::error&lt;/code&gt; annotations that land on the PR's Files tab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diffgate check &lt;span class="nt"&gt;--base&lt;/span&gt; origin/main &lt;span class="nt"&gt;--format&lt;/span&gt; github
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx -y diffgate-cli check --base ${{ github.event.pull_request.base.sha }} --format github&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is also a &lt;code&gt;measure&lt;/code&gt; mode that prints numbers with no verdict, for the "measure first, gate later" crowd. A 500-file diff checks in about 0.7 seconds on a laptop. Zero runtime dependencies, Node 20+.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method, stated honestly
&lt;/h2&gt;

&lt;p&gt;Complexity is a &lt;strong&gt;token-based cyclomatic approximation&lt;/strong&gt;: count branch keywords and operators (&lt;code&gt;if&lt;/code&gt;, &lt;code&gt;for&lt;/code&gt;, &lt;code&gt;while&lt;/code&gt;, &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;||&lt;/code&gt;, &lt;code&gt;??&lt;/code&gt;, ternaries, &lt;code&gt;elif&lt;/code&gt;, &lt;code&gt;except&lt;/code&gt;...) per function. Same class as &lt;code&gt;lizard&lt;/code&gt;: fast and deterministic, not compiler-grade.&lt;/p&gt;

&lt;p&gt;Duplication detection normalizes identifiers to &lt;code&gt;id&lt;/code&gt; and literals to &lt;code&gt;str&lt;/code&gt;/&lt;code&gt;num&lt;/code&gt;, then finds repeated token windows with a rolling hash, so copy-paste with renamed variables still matches. Strings are blanked to a single &lt;code&gt;str&lt;/code&gt; marker rather than nothing, which avoids mass false hits on fixtures full of string literals. I hand-computed complexity fixtures in JS, Python, and Go (all 5) and the tool agreed on all of them; the suite is 38 tests, all green.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is NOT
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It does &lt;strong&gt;not&lt;/strong&gt; prove correctness. Structural gates do not prove semantics.&lt;/li&gt;
&lt;li&gt;It does &lt;strong&gt;not&lt;/strong&gt; detect AI-generated code and makes no authorship claims. There is no snake-oil "AI detector" inside.&lt;/li&gt;
&lt;li&gt;Complexity numbers are heuristic-class, not universal truths. Thresholds are team-tunable via flags or a &lt;code&gt;diffgate.json&lt;/code&gt; in the repo root.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where rivals beat us
&lt;/h2&gt;

&lt;p&gt;Honest comparison, checked 2026-10-31:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;unclebob/ub-mode&lt;/strong&gt; wins on process depth: it guides the whole agent workflow (planning, review rounds, verification habits). diffgate is a 2-second CI check, not a workflow. Complementary, not competing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SonarQube / SonarCloud&lt;/strong&gt; wins on history: trend dashboards, leak-period tracking, and 30+ languages. diffgate sees one diff, three languages, no server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;lizard&lt;/strong&gt; wins on language breadth (20+ languages) and editor integrations. It reports numbers; diffgate adds verdicts, PR gating, and annotations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semgrep&lt;/strong&gt; wins on custom rules: you can encode your team's exact patterns. diffgate ships fixed gates with sane defaults, no rule authoring, no rule maintenance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/techdevfun/diffgate.git
&lt;span class="nb"&gt;cd &lt;/span&gt;diffgate
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build
npm &lt;span class="nb"&gt;link&lt;/span&gt;   &lt;span class="c"&gt;# or run ./bin/diffgate directly&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(The &lt;code&gt;diffgate-cli&lt;/code&gt; npm package is being published; until the registry listing is live, install from source. The README will be updated the moment &lt;code&gt;npm install -g diffgate-cli&lt;/code&gt; works.)&lt;/p&gt;

&lt;p&gt;Then drop &lt;code&gt;diffgate check&lt;/code&gt; into CI and let it argue with your pull requests. It is deterministic, local, and has no model, no account, and no network to phone home to.&lt;/p&gt;

&lt;p&gt;Roadmap: new-lines-without-coverage when an lcov/cobertura file is supplied, more languages, and per-file growth budgets. Repo: &lt;a href="https://github.com/techdevfun/diffgate" rel="noopener noreferrer"&gt;github.com/techdevfun/diffgate&lt;/a&gt;. MIT licensed.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>codereview</category>
      <category>ai</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>What would an infostealer find on YOUR machine? I built a CLI that answers exactly that</title>
      <dc:creator>TECHDEV</dc:creator>
      <pubDate>Fri, 30 Oct 2026 12:07:30 +0000</pubDate>
      <link>https://dev.to/techdevfun/what-would-an-infostealer-find-on-your-machine-i-built-a-cli-that-answers-exactly-that-2gmf</link>
      <guid>https://dev.to/techdevfun/what-would-an-infostealer-find-on-your-machine-i-built-a-cli-that-answers-exactly-that-2gmf</guid>
      <description>&lt;p&gt;In October 2026, Check Point Research reported the Aokigahara campaign: infostealers sweeping developer machines for authentication files, session data, and configs of AI coding assistants -- Claude, ChatGPT Desktop, Codex, Cursor, Windsurf, GitHub Copilot, Gemini CLI, and others. Reading that report made me ask an uncomfortable question: if one of those landed on MY machine, what exactly would it find?&lt;/p&gt;

&lt;p&gt;I had no good answer. So I built &lt;strong&gt;sessguard&lt;/strong&gt;: a local, read-only, zero-network CLI that inventories AI-assistant session and credential material on your machine -- and hands you a per-app revocation checklist. It never prints, logs, or exports token values: findings carry paths, file metadata, and pattern-match counts only.&lt;/p&gt;

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

&lt;p&gt;Secret scanners like TruffleHog own the &lt;em&gt;repository&lt;/em&gt; surface. They are excellent at that, and to be fair, that is where they beat sessguard: if your secrets already leaked into git history, you want TruffleHog, not this tool. OSQuery can answer endpoint questions, but only with hand-written queries per app that you maintain yourself. Nobody maps &lt;strong&gt;"AI session material on THIS machine"&lt;/strong&gt; to &lt;strong&gt;"here is exactly how to revoke each one"&lt;/strong&gt;. That one-to-one mapping is the gap: curated, dated path knowledge for 10 AI assistants, strict no-value reporting, and an actionable revocation checklist.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Two commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;inventory&lt;/code&gt; -- scan well-known session/config paths, report paths, file metadata, and token-pattern match counts. Never values.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;checklist&lt;/code&gt; -- per-app revocation checklist: what each store typically holds, where to revoke it, how.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A real run against the synthetic demo profile (all fixtures fake, values never leave the machine):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ SESSGUARD_ROOT=examples/demo-profile node dist/cli.js inventory
AI SESSION STORES ON THIS MACHINE
Token values are never shown -- paths, file metadata, and match counts only.

Apps with stores found: 6/10

- Claude Code / Claude CLI:
    stores: .claude.json, .claude
    files: 2, total size: 634 bytes
    newest: today, oldest: today
    files matching token patterns: 1 (6 matches -- values hidden)
- Codex CLI:
    stores: .codex/auth.json, .codex
    files: 2, total size: 378 bytes
    newest: today, oldest: today
    files matching token patterns: 1 (6 matches -- values hidden)
- Cursor:
    stores: .cursor
    files: 1, total size: 231 bytes
    newest: today, oldest: today
    files matching token patterns: 1 (2 matches -- values hidden)
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the checklist tells you exactly where to click:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ SESSGUARD_ROOT=examples/demo-profile node dist/cli.js checklist
REVOCATION CHECKLIST

## Claude Code / Claude CLI
Stores: .claude.json, .claude
Typically holds: API keys, OAuth session tokens, project session transcripts
Revoke: https://console.anthropic.com/settings/keys
How: Revoke API keys at the console. For OAuth sessions, sign out inside the app as well.
Also rotate: Any API keys you used with Claude Code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every path entry carries the date it was last verified (2026-10-30 for all ten apps in this release), because apps move their storage locations and stale path lists are worse than none. Terminal recording of a real run:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F30jg66iyvad04u50t3s0.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F30jg66iyvad04u50t3s0.gif" alt="sessguard demo" width="800" height="1147"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is NOT
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It does &lt;strong&gt;not&lt;/strong&gt; prevent infostealer infection.&lt;/li&gt;
&lt;li&gt;It does &lt;strong&gt;not&lt;/strong&gt; detect exfiltration.&lt;/li&gt;
&lt;li&gt;Absence of findings is &lt;strong&gt;not&lt;/strong&gt; evidence that a machine is clean.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is a hygiene and incident-response checklist tool. It answers "what AI session material lives on this machine, and how do I revoke each one" -- nothing more.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it is built
&lt;/h2&gt;

&lt;p&gt;TypeScript, zero runtime dependencies, Node.js 20+. Read-only: the scanner never writes, follows no symlinks, and reads at most 2 MB per file. Zero network: CI runs the full suite with networking disabled, and a static test asserts no networking API exists in the source. The redaction self-test plants fake secrets in the demo profile and fails the build if any of them reaches the JSON output. The path list is public knowledge (documented by vendors and threat research), so the tool cannot be used as an exfiltration aid -- and it makes no network calls anyway.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/techdevfun/sessguard.git
&lt;span class="nb"&gt;cd &lt;/span&gt;sessguard
npm ci &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build
node dist/cli.js inventory
node dist/cli.js checklist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MIT licensed. Roadmap: an npm package (so &lt;code&gt;npx sessguard inventory&lt;/code&gt; just works), more assistants as their storage paths get verified, and scheduled re-verification of revocation URLs.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/techdevfun/sessguard" rel="noopener noreferrer"&gt;https://github.com/techdevfun/sessguard&lt;/a&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>security</category>
      <category>cli</category>
      <category>devops</category>
    </item>
    <item>
      <title>Node 26 is LTS today. Should you upgrade? Measure your own app instead of trusting a headline benchmark.</title>
      <dc:creator>TECHDEV</dc:creator>
      <pubDate>Wed, 28 Oct 2026 12:44:40 +0000</pubDate>
      <link>https://dev.to/techdevfun/node-26-is-lts-today-should-you-upgrade-measure-your-own-app-instead-of-trusting-a-headline-3hd5</link>
      <guid>https://dev.to/techdevfun/node-26-is-lts-today-should-you-upgrade-measure-your-own-app-instead-of-trusting-a-headline-3hd5</guid>
      <description>&lt;p&gt;Node 26 entered Active LTS today, 2026-10-28. If you run Node 20 or older,&lt;br&gt;
your runtime gets no more security fixes. If you run 22 or 24, you need a&lt;br&gt;
plan for the move. And if you are choosing between Node and Bun, what you&lt;br&gt;
have today is headline benchmarks: one author's app, on one machine, that&lt;br&gt;
is not yours.&lt;/p&gt;

&lt;p&gt;Nobody's tool answers, for your app on your machine: does it load on the&lt;br&gt;
target runtime, how fast is it there, and should you upgrade — with&lt;br&gt;
reasons, not a headline. So I built one: &lt;strong&gt;runverdict&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;runverdict&lt;/code&gt; is a local, zero-dependency TypeScript CLI. No account, no&lt;br&gt;
network beyond localhost, no downloads, no data leaves the machine. It&lt;br&gt;
never downloads a runtime: it measures only runtimes already installed&lt;br&gt;
and reports the rest as not-installed.&lt;/p&gt;

&lt;p&gt;For each runtime it reports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Version and EOL status from a built-in schedule table (Node only).&lt;/li&gt;
&lt;li&gt;A load check: the entry is spawned, and a failure records the stderr
tail verbatim — which is what names a broken native addon or a missing
module.&lt;/li&gt;
&lt;li&gt;Startup timing: median of 5 spawns, spawn to first stdout line.&lt;/li&gt;
&lt;li&gt;An HTTP workload with the built-in &lt;code&gt;http&lt;/code&gt; client only:
requests/second, p50/p95 latency, and peak RSS, using the same
workload, in the same order, on the same machine for every runtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then it returns a deterministic verdict:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BLOCKED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The target runtime fails the load check, or no runtime produced a measurement. Reasons name the failing runtime and its stderr tail. Exit code 1.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UPGRADE-READY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The current runtime is EOL or Maintenance, the target runtime loads, and its req/s is not worse than the current runtime's by more than 10%. Exit code 0.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;STAY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Otherwise, with reasons and numbers. A measured regression beyond 10% is a reason to stay, stated with both numbers. Exit code 0.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The current runtime is the first runtime in &lt;code&gt;--runtimes&lt;/code&gt; order with a&lt;br&gt;
measurement; the target is the best-performing other measured runtime,&lt;br&gt;
if any. With a single measured runtime there is no target to compare&lt;br&gt;
against, so the verdict is &lt;code&gt;STAY&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Built-in Node EOL schedule
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Node major&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;EOL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;EOL&lt;/td&gt;
&lt;td&gt;2026-04-30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;Maintenance&lt;/td&gt;
&lt;td&gt;2027-04-30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;Maintenance&lt;/td&gt;
&lt;td&gt;2028-04-30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;Active LTS&lt;/td&gt;
&lt;td&gt;2029-04-30&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An unknown Node major is reported as &lt;code&gt;UNKNOWN&lt;/code&gt;, never guessed. Bun has&lt;br&gt;
no LTS/EOL schedule: runverdict reports the Bun version only and never&lt;br&gt;
invents an EOL date for Bun. Deno is explicitly out of scope in v1: it&lt;br&gt;
is not detected, and passing &lt;code&gt;--runtimes deno&lt;/code&gt; is an error, not a silent&lt;br&gt;
omission.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to run it
&lt;/h2&gt;

&lt;p&gt;Requires Node &amp;gt;= 20.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/techdevfun/runverdict
&lt;span class="nb"&gt;cd &lt;/span&gt;runverdict
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm &lt;span class="nb"&gt;link&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repository ships a zero-dependency sample app with &lt;code&gt;/health&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;/json&lt;/code&gt; endpoints (&lt;code&gt;examples/sample-app.js&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;runverdict runtimes
runverdict measure &lt;span class="nt"&gt;--entry&lt;/span&gt; examples/sample-app.js &lt;span class="nt"&gt;--url&lt;/span&gt; /json &lt;span class="nt"&gt;--runtimes&lt;/span&gt; node,bun &lt;span class="nt"&gt;--duration&lt;/span&gt; 5 &lt;span class="nt"&gt;--connections&lt;/span&gt; 20 &lt;span class="nt"&gt;--json&lt;/span&gt; out.json
runverdict &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--duration&lt;/code&gt; defaults to 10 seconds and &lt;code&gt;--connections&lt;/code&gt; defaults to 20.&lt;br&gt;
The JSON manifest carries runtime versions, EOL statuses, every measured&lt;br&gt;
number, run counts, the verdict with reasons, and the machine note:&lt;br&gt;
"single-machine measurement, not a general claim".&lt;/p&gt;

&lt;p&gt;Exit codes: 0 when the verdict is &lt;code&gt;UPGRADE-READY&lt;/code&gt; or &lt;code&gt;STAY&lt;/code&gt;; 1 when the&lt;br&gt;
verdict is &lt;code&gt;BLOCKED&lt;/code&gt; or when no usable runtime measurement exists; 2 for&lt;br&gt;
a usage error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;A real demo run on the build machine (node v24.22.1, bun 1.4.4) measured&lt;br&gt;
the sample app: node startup median 114 ms, 12,675.8 req/s, p50 1.0 ms,&lt;br&gt;
p95 4.0 ms, 81.3 MB peak RSS; bun startup median 33 ms, 12,614.8 req/s,&lt;br&gt;
p50 1.0 ms, p95 4.0 ms, 60.8 MB peak RSS — verdict &lt;code&gt;UPGRADE-READY&lt;/code&gt;,&lt;br&gt;
because node is Maintenance and bun loaded within the 10% regression&lt;br&gt;
allowance. These numbers are that machine's only; re-running produces&lt;br&gt;
new numbers, because the numbers are measurements.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6h2byj0si641k1dskqgi.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6h2byj0si641k1dskqgi.gif" alt="Demo: runverdict runtimes inventory and measure verdict on the build machine" width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits, stated plainly
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A load check proves the entry loads, not full compatibility. A passing
load check is not a guarantee the app behaves identically on another
runtime.&lt;/li&gt;
&lt;li&gt;Numbers are this machine's. A faster runtime here is not a claim about
any other machine, app, or workload.&lt;/li&gt;
&lt;li&gt;Node 26 is EOL-table-only in v1 until it is measured live on a machine
that has it installed. The tool reports not-installed runtimes as
skipped, never downloads them.&lt;/li&gt;
&lt;li&gt;It never downloads runtimes, never touches the network beyond
localhost, and never reads files outside the app directory except the
runtime binaries.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where rivals beat us
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;clinic.js&lt;/strong&gt; beats us on profiling depth: flame graphs and event-loop
diagnosis. It profiles one runtime's bottleneck; it has no
cross-runtime comparison, no EOL schedule, no upgrade verdict.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;hyperfine&lt;/strong&gt; beats us on statistical rigor: outlier detection and
warmup modeling. It is a generic timer; it knows nothing about JS
runtimes, EOL lines, native addons, or upgrade risk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;autocannon&lt;/strong&gt; beats us on HTTP load options. It gives HTTP numbers
only; no runtime awareness, no verdict, no EOL, no addon/load check.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;v1 does none of those deeply. It does the upgrade decision, which none&lt;br&gt;
of them do.&lt;/p&gt;

&lt;p&gt;The code is on GitHub under MIT, zero runtime dependencies:&lt;br&gt;
&lt;a href="https://github.com/techdevfun/runverdict" rel="noopener noreferrer"&gt;https://github.com/techdevfun/runverdict&lt;/a&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>node</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Measured on real public CI logs: what two small tools actually did, and where they fell short</title>
      <dc:creator>TECHDEV</dc:creator>
      <pubDate>Tue, 27 Oct 2026 09:09:57 +0000</pubDate>
      <link>https://dev.to/techdevfun/measured-on-real-public-ci-logs-what-two-small-tools-actually-did-and-where-they-fell-short-3i25</link>
      <guid>https://dev.to/techdevfun/measured-on-real-public-ci-logs-what-two-small-tools-actually-did-and-where-they-fell-short-3i25</guid>
      <description>&lt;p&gt;Most tool READMEs show numbers from their own fixtures. We wanted numbers from logs nobody on our side wrote, so we ran two public repos, context-diet 0.5.0 and ci-failure-detective 0.1.0, cloned from GitHub and built and run exactly as their READMEs describe, on two real public CI logs from other projects. Everything below is what the runs actually printed, including the misses. Lines and bytes are exact. Token counts are the tools' own chars/4 estimates, labeled as estimates throughout, never as measured tokens. We did not run a real tokenizer, so we cannot say whether chars/4 overcounts or undercounts these logs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two logs
&lt;/h2&gt;

&lt;p&gt;Log A: prettier/prettier, workflow "Dev", run 40391802031 (PR #20363, "Update babel to v8.0.8", 2026-10-26), job "[Full Test] Node.js 26 on Linux". Downloaded log: 2,589 lines, 251,942 bytes, 62,967 estimated tokens (chars/4). Ground truth from the log itself: 1 failed suite (tests/format/js/&lt;em&gt;errors&lt;/em&gt;/export-default-from/format.test.js), 4 failed snapshot tests, 1,577 passed suites, 103,099 passed tests, 5 skipped, 103,108 total. The failures are snapshot mismatches: babel v8 changed parser error wording ("Unexpected token" versus "Unexpected token, expected from"), so stored snapshots no longer match. That is a real test failure caused by an upstream message change, not a bug in prettier's own formatting code.&lt;/p&gt;

&lt;p&gt;Log B: jestjs/jest, workflow "Node CI", run 40415519415 (branch renovate/eslint-plugin-unicorn-79.x, 2026-10-26), job "Windows with shard 1_4, Node v25.x". Ground truth: 2 failed tests in e2e/&lt;strong&gt;tests&lt;/strong&gt;/customReporters.test.ts ("Custom Reporters Integration" - "prints reporter errors" and "supports reporter written in ESM"), 2 failed / 11 skipped / 1,394 passed / 1,407 total in the failing invocation. The failure cause visible in the log is a Windows temp-directory cleanup error (EPERM, permission denied, on fs.rmSync). A later invocation in the same job log passed everything (1,396 passed, 11 skipped, 1,407 total). In other words: a Windows-only environment flake, not a code regression.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method note: read this before the numbers
&lt;/h2&gt;

&lt;p&gt;GitHub's downloadable logs prefix every line with a timestamp. ci-failure-detective's test parsers match markers at line starts, so on raw downloaded logs they extract nothing; we verified this on both logs (zero tests extracted from each raw log, framework reported as unknown). To reproduce the input the tool was built for (piped test output, no timestamps), we stripped only the timestamp prefixes and ANSI color codes from Log B before running the parser, and verified the strip changed nothing else: same line count, every line identical after the strip. Log A was run both raw and stripped; the parser gap there is different (see below) and survives the strip. context-diet was run on Log A exactly as downloaded.&lt;/p&gt;

&lt;h2&gt;
  
  
  context-diet on Log A
&lt;/h2&gt;

&lt;p&gt;We replicated the procedure from the tool's own README example: pack with a 400-token budget and the query "test failure". We also ran dedupe alone and extract alone, because a single pack number hides which stage did the work.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Run&lt;/th&gt;
&lt;th&gt;Lines&lt;/th&gt;
&lt;th&gt;Bytes&lt;/th&gt;
&lt;th&gt;Est. tokens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Input (Log A)&lt;/td&gt;
&lt;td&gt;2,589&lt;/td&gt;
&lt;td&gt;251,942&lt;/td&gt;
&lt;td&gt;62,967&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dedupe alone, raw log&lt;/td&gt;
&lt;td&gt;2,589&lt;/td&gt;
&lt;td&gt;251,942&lt;/td&gt;
&lt;td&gt;62,967&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pack, budget 400, query "test failure"&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;1,580&lt;/td&gt;
&lt;td&gt;395&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pack, budget 4000, same query&lt;/td&gt;
&lt;td&gt;163&lt;/td&gt;
&lt;td&gt;15,771&lt;/td&gt;
&lt;td&gt;3,941&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;extract alone, same query&lt;/td&gt;
&lt;td&gt;1,694&lt;/td&gt;
&lt;td&gt;149,870&lt;/td&gt;
&lt;td&gt;(not reported)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three findings, in order of importance.&lt;/p&gt;

&lt;p&gt;First, dedupe removed nothing from the raw log: 0 duplicate lines, 0 duplicate blocks. Every line carries a unique timestamp, so no two lines are identical. The tool does not normalize timestamps today, so on raw GitHub logs its dedupe stage is idle. As a secondary check only, we ran dedupe on the same log with timestamps and color codes stripped: it then removed 266 lines (115 duplicate lines plus 26 blocks covering 151 lines), taking 2,589 lines to 2,323 (10.3% of lines; bytes 174,192 to 158,340 on the stripped input). Near-duplicate collapsing found nothing here, which is expected: this log contains no repeated traces differing only in memory addresses. The timestamp effect is therefore measurable on this log: 0% versus 10.3% of lines.&lt;/p&gt;

&lt;p&gt;Second, the headline pack saving is mostly budget truncation, not duplicate removal. At budget 400, the manifest reads: strip-ansi removed 2,698 bytes and 0 lines; dedupe removed 0 lines; extract kept 10 chunks (2,589 to 1,694 lines); summarize dropped 1,663 lines as over-budget plus 18 noise lines. So the 99.5% line saving (2,589 to 13) is the budget doing the cutting, and every output line is still verbatim from the input (verbatim: true). That is a property worth keeping, but it is not evidence that the log was 99.5% duplication.&lt;/p&gt;

&lt;p&gt;Third, at budget 400 the actual failure did not survive. The 13-line output contains two node warnings and eleven PASS lines, but no FAIL line and no mention of the failing suite. The mechanism is in the summarize code: it picks lines by priority (error-matching lines first) then document order until the budget is met, and its error pattern also matches PASS lines under &lt;em&gt;errors&lt;/em&gt;/ directories. In the extracted text, 50 such lines precede the FAIL line, and the 400-token budget was spent before reaching it. At budget 4000 the failure does survive (163 lines, FAIL present), at a 93.7% line saving that is still mostly budget truncation (summarize dropped 1,531 lines). The honest summary: on this log, pack is a lossy budget summary, and the budget you pick decides whether the failure you care about is still in the output. Protect the lines that must survive, or check the output before trusting it.&lt;/p&gt;

&lt;h2&gt;
  
  
  ci-failure-detective on Log A and Log B
&lt;/h2&gt;

&lt;p&gt;On Log A, the tool found the first error correctly: line 449 of the downloaded log, FAIL tests/format/js/&lt;em&gt;errors&lt;/em&gt;/export-default-from/format.test.js, with the snapshot diff as context. That matches ground truth. But it extracted zero failing tests and reported the framework as unknown, raw and stripped alike. The reason is visible in the parser code: the jest parser needs bullet failure blocks or checkmark/cross result lines, and prettier's custom format-test runner emits neither. Its failing checks appear as "snippet: #0 &amp;gt; format" blocks, which no parser pattern matches. Verdict: unknown, with no history supplied. The tool pointed at the right suite and stopped there; it could not name the four failed checks.&lt;/p&gt;

&lt;p&gt;On Log B (after the timestamp and color strip described above), the tool found the first error correctly: FAIL e2e/&lt;strong&gt;tests&lt;/strong&gt;/customReporters.test.ts (9.769 s), and reported the framework as jest. It named both failing tests correctly ("Custom Reporters Integration" - "prints reporter errors" and "supports reporter written in ESM"), matching ground truth. But it attributed both to the wrong file (packages/jest-environment-node/src/&lt;strong&gt;tests&lt;/strong&gt;/globals_cleanup_3.test.ts instead of customReporters.test.ts). The reason is structural: the bullet failure blocks sit in the summary section at the end of the log, after all file lines, so the parser's "current file" is whatever file line came last, not the failing suite's file. Verdicts for both tests: unknown, confidence 0, reason "No history supplied". That is the honest answer here, but it is not the flaky verdict the ground truth would support.&lt;/p&gt;

&lt;p&gt;Why verdicts cannot go further on logs like these: the tool's record command stores failing tests plus per-test pass records, but pass records only exist when the parser sees individual passes, which needs verbose test output. Jest's own CI log contains zero checkmark/cross lines, so no pass records exist at all. Reading the scoring code, without pass records the same-commit verdict (0.95), the flip-rate verdict (0.70), and the regression verdict (0.80) cannot fire; the reachable verdicts are unknown or "real" at 0.55 for a test that failed before and never passed in recorded history. The in-log retry heuristic also needs retry markers, and this log contains none. Separately, the file misattribution above means history keys (file plus test name) would not match pass records even if verbose logs existed. We did not manufacture history from the same run to force a verdict; that would be circular.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where rivals beat us
&lt;/h2&gt;

&lt;p&gt;Stated plainly, because these are the gaps the measurements exposed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub's own failure annotations beat this tool on position: they show the failing suite directly on the run page, with no parsing step and no format caveats.&lt;/li&gt;
&lt;li&gt;A plain text search for FAIL beats it on Log A: the first error was one grep away, while per-test verdicts needed markers the log does not have.&lt;/li&gt;
&lt;li&gt;Real tokenizers beat chars/4 on measurement: our token numbers are estimates by construction, and we did not verify them against a real tokenizer.&lt;/li&gt;
&lt;li&gt;The projects' own READMEs already list the honest comparisons: ci-failure-detective names hosted flaky platforms (dashboards, alerting, auto-quarantine) that it does not aim to replace, and context-diet keeps every output line verbatim instead of rewriting, which is a smaller claim than tools that summarize freely. We keep both framings: local, deterministic, no log leaves the machine, and narrower than the platforms above.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What we did not measure
&lt;/h2&gt;

&lt;p&gt;Two logs are not a benchmark. We did not test other frameworks' logs, other budgets beyond 400 and 4000, or history-based verdicts, because the logs we used cannot support them (see above). We did not measure tokens with a real tokenizer. Nothing here claims these tools beat any rival on detection quality; the claim is narrower: on two real public logs, first errors were found correctly twice, per-test verdicts were absent twice for stated, checkable reasons, and pack savings track the budget you choose more than the duplication in the log.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Log A run page: &lt;a href="https://github.com/prettier/prettier/actions/runs/40391802031" rel="noopener noreferrer"&gt;https://github.com/prettier/prettier/actions/runs/40391802031&lt;/a&gt; (job "[Full Test] Node.js 26 on Linux", job id 121814403282, 2026-10-26; log downloaded 2026-10-27 via the repo's public run logs)&lt;/li&gt;
&lt;li&gt;Log B run page: &lt;a href="https://github.com/jestjs/jest/actions/runs/40415519415" rel="noopener noreferrer"&gt;https://github.com/jestjs/jest/actions/runs/40415519415&lt;/a&gt; (job "Windows with shard 1_4, Node v25.x", 2026-10-26; log downloaded 2026-10-27 via the repo's public run logs)&lt;/li&gt;
&lt;li&gt;context-diet 0.5.0: &lt;a href="https://github.com/techdevfun/context-diet" rel="noopener noreferrer"&gt;https://github.com/techdevfun/context-diet&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;ci-failure-detective 0.1.0: &lt;a href="https://github.com/techdevfun/ci-failure-detective" rel="noopener noreferrer"&gt;https://github.com/techdevfun/ci-failure-detective&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Method note: GitHub's downloadable logs add per-line timestamps; the timestamp and color strip applied to Log B removed only those prefixes and codes (line count unchanged, all other bytes identical). context-diet ran on Log A exactly as downloaded.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>typescript</category>
      <category>testing</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
    <item>
      <title>A stolen token turns your own CI into a secret dump. I built a local detector that watches for the act</title>
      <dc:creator>TECHDEV</dc:creator>
      <pubDate>Tue, 27 Oct 2026 07:10:04 +0000</pubDate>
      <link>https://dev.to/techdevfun/a-stolen-token-turns-your-own-ci-into-a-secret-dump-i-built-a-local-detector-that-watches-for-the-13o1</link>
      <guid>https://dev.to/techdevfun/a-stolen-token-turns-your-own-ci-into-a-secret-dump-i-built-a-local-detector-that-watches-for-the-13o1</guid>
      <description>&lt;p&gt;Your private repository's CI can betray you without a single strange domain ever appearing. That is the shape of Gheist, a campaign Wiz Research has tracked since September 15, 2026: more than 13,500 planted branches recovered from more than 850 compromised accounts, from personal projects to enterprise private repositories.&lt;/p&gt;

&lt;p&gt;The mechanism is brutally simple. A stolen token — Wiz points mainly to infostealer logs and credentials exposed in earlier supply-chain incidents — creates a branch named &lt;code&gt;ci-sync-&lt;/code&gt; plus six random characters, then commits a workflow at &lt;code&gt;.github/workflows/sync-check.yml&lt;/code&gt; with the message &lt;code&gt;ci: sync config&lt;/code&gt;. The push triggers the workflow, which prints repository secrets and environment variables after base32-encoding them. GitHub masks secret values in logs only when they appear verbatim, so an encoded secret prints in full. Seconds after the job finishes, the attacker's tool downloads the workflow log through the GitHub API and deletes the run. No external command-and-control is needed, because the secrets leave through GitHub's own API. A second variant, seen October 19, 2026, targets self-hosted runners with a job named &lt;code&gt;sync-host&lt;/code&gt;, hunting the host filesystem, process environments, and mounted containers for 31 credential formats.&lt;/p&gt;

&lt;p&gt;Deleting the branch or the run does not undo the exposure. Every secret the repository could read must be rotated, and the token that pushed the branch must be revoked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the existing answers miss it
&lt;/h2&gt;

&lt;p&gt;GitHub's control from July 28, 2026 holds suspicious workflow runs in public repositories pending collaborator approval, and it stopped every public run Wiz checked. It does not cover private repositories or GitHub Enterprise Server — exactly where Gheist succeeds.&lt;/p&gt;

&lt;p&gt;The organization audit log is ground truth: it records the created, completed, and deleted workflow-run events, plus the tool's user agent. But the audit-log API is available to organization owners only, and nothing ships a packaged Gheist rule — the operator must already know the pattern, write the query, and interpret the result.&lt;/p&gt;

&lt;p&gt;Static secret scanners scan content. A dump workflow contains no secret text, so it is invisible to them by construction — and deleting the offending line does nothing to the credential anyway. Rotation is the only step that neutralizes exposure.&lt;/p&gt;

&lt;p&gt;SIEM forwarding works, but it needs an enterprise plan, streaming setup, and hand-written queries. Manual audit runbooks are exact and free, but they are one-off: no scheduling, no report, no false-positive discipline, no checklist.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I built: gheist-guard
&lt;/h2&gt;

&lt;p&gt;gheist-guard is a local, deterministic, read-only behavioral detector. It watches for the act, from the evidence that survives it: the branch name, the audit-log sequence, the tool fingerprint, and the rotate checklist.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One command, one verdict per repository: CLEAR, SUSPECT, or CONFIRMED.&lt;/li&gt;
&lt;li&gt;Three commands: &lt;code&gt;scan&lt;/code&gt; (REST path over branches, workflows, and runs), &lt;code&gt;audit --org&lt;/code&gt; (audit-log path, or &lt;code&gt;--events&lt;/code&gt; with recorded payloads), and &lt;code&gt;checklist&lt;/code&gt; (re-print the rotate checklist from a saved report).&lt;/li&gt;
&lt;li&gt;Seven rules: the &lt;code&gt;ci-sync-&lt;/code&gt; branch pattern, the sync-check workflow identity, base32/base64 dump content, completed-then-deleted within 60 seconds, the &lt;code&gt;gheist&lt;/code&gt; user agent, a generic unreviewed-workflow rule that also catches the adjacent GhostAction shape, and the self-hosted &lt;code&gt;sync-host&lt;/code&gt; variant.&lt;/li&gt;
&lt;li&gt;Read-only by default. The checklist lists secret NAMES only — it never prints a secret value, never deletes anything, never rotates anything itself. A partial signal is SUSPECT with its next check, never silently CLEAR and never CONFIRMED. The name "Gheist" appears in a verdict only when the full documented pattern matches.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install from source (the tool is not on npm):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/techdevfun/gheist-guard
&lt;span class="nb"&gt;cd &lt;/span&gt;gheist-guard
npm ci
npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use it as a GitHub Action: &lt;code&gt;techdevfun/gheist-guard@v1&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measured numbers (build 2026-10-27)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Test suite: 52/52 green, TypeScript strict clean.&lt;/li&gt;
&lt;li&gt;False-positive probe: 24 legitimate branch names + 5 legitimate workflows → 0 CONFIRMED, 1 SUSPECT (&lt;code&gt;ci-sync-config&lt;/code&gt; matches the six-character pattern by design; documented boundary).&lt;/li&gt;
&lt;li&gt;Rule-evaluation latency (in-process, 500 iterations on the planted fixture): median 0.0045 ms, p95 0.0148 ms per repository.&lt;/li&gt;
&lt;li&gt;Live REST end-to-end (real GitHub API, read-only): 3 real repositories scanned, all CLEAR (vetgate, context-diet, breakcheck); plus a private test repository with a branch-only plant: SUSPECT before a real adapter fix (it only read the default branch) and CONFIRMED after (R1+R2+R3).&lt;/li&gt;
&lt;li&gt;Demo (real CLI runs on fixtures, not staged): a 9.4-second GIF plus the full transcript in the repo's examples.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo6hy2mou5pz6qwblcmip.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo6hy2mou5pz6qwblcmip.gif" alt=" " width="799" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where rivals beat us
&lt;/h2&gt;

&lt;p&gt;Honestly, and this stays in the README too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub's native controls beat us on authority and position. The audit log is ground truth; the approval hold stops public execution before any dump. We only read what the platform records.&lt;/li&gt;
&lt;li&gt;Socket, StepSecurity, and GitGuardian beat us on threat-intelligence scale. They see campaigns across the ecosystem; we see only your repositories. We will never be the first to name a new campaign.&lt;/li&gt;
&lt;li&gt;Gitleaks and TruffleHog beat us on secret-pattern breadth in code and history. We do not scan secrets in code at all — run one of them alongside this tool if you need that.&lt;/li&gt;
&lt;li&gt;SIEM tooling beats us on central correlation across sources. We are a single-purpose local tool.&lt;/li&gt;
&lt;li&gt;Manual runbooks beat us on zero-install immediacy. Copy-paste commands work today with no install. We have to earn the install with scheduling, reporting, false-positive discipline, and the checklist.&lt;/li&gt;
&lt;li&gt;The July 28 approval hold beats us on prevention for public repositories. Our value concentrates where the hold does not reach: private repositories, GitHub Enterprise Server, and the response after a run has completed and been deleted.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limits, stated plainly
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The audit-log path is fixture-tested end to end. No organization-owner account was available for a live audit-log test.&lt;/li&gt;
&lt;li&gt;GitHub Enterprise Server logs are documented but untested.&lt;/li&gt;
&lt;li&gt;Pull-request association is an approximation: a workflow commit with an associated pull request is treated as reviewed.&lt;/li&gt;
&lt;li&gt;The tool cannot see a run that was deleted before the first scan unless the branch or the audit log still carries it.&lt;/li&gt;
&lt;li&gt;"Gheist" is Wiz's name for the campaign. This tool credits Wiz as the source of the name and pattern; there is no link to or endorsement by Wiz.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code is MIT, on GitHub: &lt;a href="https://github.com/techdevfun/gheist-guard" rel="noopener noreferrer"&gt;https://github.com/techdevfun/gheist-guard&lt;/a&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>security</category>
      <category>devops</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Your agent installs packages before anyone vets them. I built a gate that runs first.</title>
      <dc:creator>TECHDEV</dc:creator>
      <pubDate>Mon, 26 Oct 2026 07:22:43 +0000</pubDate>
      <link>https://dev.to/techdevfun/your-agent-installs-packages-before-anyone-vets-them-i-built-a-gate-that-runs-first-59i7</link>
      <guid>https://dev.to/techdevfun/your-agent-installs-packages-before-anyone-vets-them-i-built-a-gate-that-runs-first-59i7</guid>
      <description>&lt;h1&gt;
  
  
  Your agent installs packages before anyone vets them. I built a gate that runs first.
&lt;/h1&gt;

&lt;p&gt;My coding agent resolves a task, picks a package, and runs the install. The &lt;code&gt;postinstall&lt;/code&gt; hook fires before I read any output. By the time a post-install audit, a PR check, or a dashboard tells me something was wrong, the payload has already run.&lt;/p&gt;

&lt;p&gt;That is the moment I wanted to gate: after the agent has chosen the artifact, before the install or run command executes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/techdevfun/vetgate" rel="noopener noreferrer"&gt;vetgate&lt;/a&gt; is a local, deterministic pre-execution gate. It is an MCP server and a CLI mirror. Call it &lt;em&gt;instead of&lt;/em&gt; running the install: one call, one verdict (&lt;code&gt;ALLOW&lt;/code&gt;, &lt;code&gt;DENY&lt;/code&gt;, or &lt;code&gt;REVIEW&lt;/code&gt;), every finding carrying the exact evidence string it matched, and a manifest that makes the verdict re-checkable without re-running anything.&lt;/p&gt;

&lt;p&gt;It vets three kinds of artifact:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;vet_package&lt;/code&gt; / &lt;code&gt;npm:name@version&lt;/code&gt; — downloads the published tarball, inspects it, never executes it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vet_action&lt;/code&gt; / &lt;code&gt;action:owner/repo@ref&lt;/code&gt; — a local form check: SHA-pinned, mutable tag, or unpinned.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vet_script&lt;/code&gt; / &lt;code&gt;url:https://...&lt;/code&gt; — fetches the script text, scans it, never executes it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no model, no account, no API key, and no verdict score. The default path sends nothing anywhere except downloading the artifact itself (&lt;code&gt;vet_package&lt;/code&gt;) and fetching the script text (&lt;code&gt;vet_script&lt;/code&gt;, labeled in the verdict manifest). All analysis is local.&lt;/p&gt;

&lt;p&gt;The rules are stated, not learned. A finding without evidence is a bug. Any &lt;code&gt;high&lt;/code&gt; finding means &lt;code&gt;DENY&lt;/code&gt;; otherwise any finding means &lt;code&gt;REVIEW&lt;/code&gt; with a &lt;code&gt;safeAction&lt;/code&gt; — for example, install with &lt;code&gt;--ignore-scripts&lt;/code&gt;, pin to a digest, or ask the user. A single weak signal alone (an install script that only echoes, a missing license field, a mutable tag) yields &lt;code&gt;REVIEW&lt;/code&gt;, never a silent &lt;code&gt;ALLOW&lt;/code&gt; and never a &lt;code&gt;DENY&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real run
&lt;/h2&gt;

&lt;p&gt;From the CLI, on hand-built fixtures (no real malware, nothing fetched in fixture mode):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ vetgate check local:tests/fixtures/exfil
vetgate: DENY — 2 finding(s)   # exit 1

$ vetgate check local:tests/fixtures/benign
vetgate: ALLOW — 0 finding(s)  # exit 0

$ vetgate check action:actions/checkout@v4
vetgate: REVIEW — 1 finding(s) # exit 1, pin to a full commit SHA
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full transcript is in the repo at &lt;code&gt;docs/demo/transcript.txt&lt;/code&gt;, and the README shows a 30-second demo rendered from that transcript.&lt;/p&gt;

&lt;p&gt;As an MCP server, it exposes the same three tools over stdio:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"vetgate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vetgate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"serve"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I verified it with a real MCP session: the server lists all three tools, &lt;code&gt;vet_action&lt;/code&gt; on &lt;code&gt;actions/checkout@v4&lt;/code&gt; returns &lt;code&gt;REVIEW&lt;/code&gt;, and &lt;code&gt;vet_package&lt;/code&gt; on the real published &lt;code&gt;lodash@4.17.21&lt;/code&gt; returns &lt;code&gt;ALLOW&lt;/code&gt; with the tarball SHA-256 &lt;code&gt;6a087ac9e5702a0c9d60fbcd48696012646ec8df1491dea472b150e79fcaf804&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measured numbers, not claims
&lt;/h2&gt;

&lt;p&gt;Measured locally at build time on the fixture set (analysis only; network download time excluded). The measurement script is &lt;code&gt;scripts/measure-latency.mjs&lt;/code&gt; in the repo, so you can re-run it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fixture suite: 15 hand-built fixtures — ALLOW 4, DENY 6, REVIEW 5. Every malicious-pattern fixture DENYs, every benign fixture ALLOWs, every weak-signal fixture REVIEWs.&lt;/li&gt;
&lt;li&gt;False-positive probe: 10 benign fixture contents — 0 DENY.&lt;/li&gt;
&lt;li&gt;Determinism: the same package contents vetted twice produce identical verdicts after stripping &lt;code&gt;manifest.checkedAt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Latency over the fixture set (3,000 timed checks): median 0.006 ms, p95 0.075 ms, mean 0.026 ms per check.&lt;/li&gt;
&lt;li&gt;Test suite: 26 tests, all passing (&lt;code&gt;npm test&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where rivals beat us
&lt;/h2&gt;

&lt;p&gt;Honesty first, because a security gate that overclaims is worse than useless:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SafeDep &lt;code&gt;vet&lt;/code&gt; beats us on breadth and on malicious detection.&lt;/strong&gt; More ecosystems, Docker and SBOM support, policy expressions, and cloud active scanning for malicious packages in the wild. Our v1 is npm-only with a fixed local rule set and cannot match that breadth or that cloud detection power.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Socket beats us on behavioral detection depth.&lt;/strong&gt; Its malware database, obfuscation analysis, and telemetry detection come from operating a cloud scanner at scale. Our local rules cover only the documented install-script and pattern surface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snyk and DeepSource beat us on monitoring and fixing.&lt;/strong&gt; Continuous re-evaluation and fix PRs are a different, genuinely valuable job; we do neither. We vet one artifact once, before execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenSSF Scorecard beats us on maintainer-risk signals.&lt;/strong&gt; Branch protection, review discipline, and fuzzing adoption are real risk signals our artifact inspection does not measure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OSV and the advisory databases beat us on known-vulnerability breadth.&lt;/strong&gt; Any local-only v1 misses known CVEs that a database lookup would catch. There is no enrichment mode in v1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A registry firewall beats us on position.&lt;/strong&gt; Blocking at the registry edge stops a bad package before download; we inspect after download and before execution, by design, because we are the gate for agents that install from anywhere, not just one registry.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The wedge is narrow and deliberate: a gate that sits inside the agent loop at the moment of execution, returns a deterministic verdict with quotable evidence, and runs fully locally with no account, no key, and no data leaving the machine. If you need breadth, cloud detection, monitoring, or fix PRs, use the tools above — vetgate does not replace them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is NOT in v1
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Other ecosystems (PyPI, Maven, Go, Ruby, Rust, PHP). npm only.&lt;/li&gt;
&lt;li&gt;Agent skill / MCP-server-config vetting — a real pain, a different detection domain, and the first follow-up candidate.&lt;/li&gt;
&lt;li&gt;Container image layer inspection, monitoring, re-scanning, fix PRs, dashboards, accounts, cloud APIs, or policy languages.&lt;/li&gt;
&lt;li&gt;Any claim about malware families beyond the stated rule list. The tool reports what its rules matched, nothing more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The roadmap is in the README: skill/config vetting first, then ecosystems one at a time, then optional labeled network enrichment, off by default.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm ci
npm run build
npm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Requires Node 20 or later. The repo is MIT licensed, and the code, rules, fixtures, and measurement script are all in the open:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/techdevfun/vetgate" rel="noopener noreferrer"&gt;https://github.com/techdevfun/vetgate&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;README: &lt;a href="https://github.com/techdevfun/vetgate#readme" rel="noopener noreferrer"&gt;https://github.com/techdevfun/vetgate#readme&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you run it on a package your agent was about to install, I would like to hear what it caught — or what it missed. The rule list is explicit, so a miss is a concrete bug report, not a vague score to argue about. What would you want the gate to check next: another ecosystem, or skill/config vetting?&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>mcp</category>
      <category>security</category>
      <category>devops</category>
    </item>
    <item>
      <title>Your agent isn't running out of context because of one big file</title>
      <dc:creator>TECHDEV</dc:creator>
      <pubDate>Sun, 25 Oct 2026 12:07:37 +0000</pubDate>
      <link>https://dev.to/techdevfun/your-agent-isnt-running-out-of-context-because-of-one-big-file-20bb</link>
      <guid>https://dev.to/techdevfun/your-agent-isnt-running-out-of-context-because-of-one-big-file-20bb</guid>
      <description>&lt;p&gt;Your agent isn't running out of context because of one big file. It is&lt;br&gt;
running out because of accumulation: tool outputs, test logs, build logs,&lt;br&gt;
repeated turns, and the same content pasted again in a slightly different&lt;br&gt;
form. I kept watching sessions die on the tenth retry of the same failing&lt;br&gt;
test — not because any single output was huge, but because nobody ever&lt;br&gt;
threw anything away.&lt;/p&gt;

&lt;p&gt;So I built Context Diet: an MCP server (plus a CLI) that shrinks the&lt;br&gt;
content an agent actually sends, deterministically, with receipts. Every&lt;br&gt;
output line is copied verbatim from the input. Every dropped line is&lt;br&gt;
counted and categorized in a manifest. No model, no download, no data&lt;br&gt;
leaves the machine.&lt;/p&gt;
&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Five tools, one promise — nothing invented:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;diet_count&lt;/code&gt; — lines, bytes, and estimated tokens of a text.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;diet_dedupe&lt;/code&gt; — removes exact duplicate lines and repeated blocks.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;diet_extract&lt;/code&gt; — relevance-ranked excerpts for a query, with context
around each hit.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;diet_summarize&lt;/code&gt; — an extractive summary to a token budget. It keeps
headings, error and failure lines, and the first and last lines of
each section. It never rewrites a word.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;diet_pack&lt;/code&gt; — the full pipeline: dedupe, then extract if you give it
a query, then summarize to a budget, plus a manifest for every line.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The manifest is the point. A summary that silently rewrites your log is&lt;br&gt;
a liability; a summary that says "kept 23 lines, dropped 54 as&lt;br&gt;
duplicates, 4 in extraction, 88 in summarization" is something you can&lt;br&gt;
diff and trust.&lt;/p&gt;
&lt;h2&gt;
  
  
  Measured, not claimed
&lt;/h2&gt;

&lt;p&gt;On a real session log (verbose test runs, a README excerpt, a build&lt;br&gt;
output — &lt;code&gt;examples/session.log&lt;/code&gt; in the repo), packed to a 400-token&lt;br&gt;
budget with the query &lt;code&gt;test failure&lt;/code&gt;:&lt;/p&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;Lines&lt;/th&gt;
&lt;th&gt;Bytes&lt;/th&gt;
&lt;th&gt;Est. tokens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Before&lt;/td&gt;
&lt;td&gt;169&lt;/td&gt;
&lt;td&gt;7,917&lt;/td&gt;
&lt;td&gt;1,942&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;1,587&lt;/td&gt;
&lt;td&gt;392&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Token counts are estimates (a documented chars/4 rule); lines and&lt;br&gt;
bytes are exact. The manifest adds up: 169 = 23 kept + 54 deduped + 4&lt;br&gt;
extracted away + 88 summarized away. Verbatim: true.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to run it
&lt;/h2&gt;

&lt;p&gt;From source (the npm package arrives with the first npm publish):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/techdevfun/context-diet
&lt;span class="nb"&gt;cd &lt;/span&gt;context-diet
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build
node dist/cli.js pack &lt;span class="nt"&gt;--input&lt;/span&gt; examples/session.log &lt;span class="nt"&gt;--budget&lt;/span&gt; 400 &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"test failure"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As an MCP server over stdio, point your client at&lt;br&gt;
&lt;code&gt;node /path/to/context-diet/dist/server.js&lt;/code&gt;. The repo has a demo GIF&lt;br&gt;
recorded from a real terminal session, CI on Node 20 and 22, and a test&lt;br&gt;
suite that runs the server through a real MCP client.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it compares (checked 2026-10-25)
&lt;/h2&gt;

&lt;p&gt;This is a crowded corner, and the honest version is that each rival is&lt;br&gt;
better at something:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;mcp-token-optimizer&lt;/strong&gt; minifies code with AST awareness and restores
formatting with Prettier. It works on code files; it does not touch
logs, outputs, or conversation turns, which is where session context
actually accumulates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLMLingua / LLMLingua-2&lt;/strong&gt; compress harder (up to 20x on benchmarks)
— but they need a model download and a Python/PyTorch stack, and
their drops are unauditable at token granularity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entroly&lt;/strong&gt; claims 78% fewer tokens per request across whole
codebases, with learned weights and a dashboard. That figure is
self-reported, and the weighting is a black box: you cannot list
exactly why a line was dropped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vendor tool search / code mode&lt;/strong&gt; (Claude Code deferral, Cloudflare
Code Mode) report the biggest percentages, up to 99.9% — but they
shrink tool &lt;em&gt;definitions&lt;/em&gt;, not the content the agent accumulates.
Different layer, complementary, not competing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where rivals beat us
&lt;/h2&gt;

&lt;p&gt;Plainly: LLMLingua compresses harder than our typical 2–5x on logs.&lt;br&gt;
mcp-token-optimizer understands code structure and restores formatting;&lt;br&gt;
we treat code as text. Entroly covers whole codebases with a dashboard;&lt;br&gt;
we are one focused pipeline. Vendor tool search fixes tool-schema bloat&lt;br&gt;
at percentages we will never match, because that is not our layer. We&lt;br&gt;
do not have those things in v1, and we do not claim them.&lt;/p&gt;

&lt;p&gt;What we have instead is the one thing none of them offer: a&lt;br&gt;
deterministic, auditable diet for session content, where every cut is&lt;br&gt;
accounted for and nothing is invented.&lt;/p&gt;

&lt;h2&gt;
  
  
  Roadmap (not in v1)
&lt;/h2&gt;

&lt;p&gt;LLM summarization (opt-in), model-based compression, AST-aware code&lt;br&gt;
minification, cross-session cache, dashboard, auto-apply to agent&lt;br&gt;
configs, JUnit/XML parsing, SARIF output, secret redaction.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/techdevfun/context-diet" rel="noopener noreferrer"&gt;https://github.com/techdevfun/context-diet&lt;/a&gt; — MIT licensed,&lt;br&gt;
no account, no network, no model calls. If your sessions also die by&lt;br&gt;
accumulation, try it on your own ugliest log and check the manifest.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>mcp</category>
      <category>ai</category>
      <category>node</category>
    </item>
    <item>
      <title>Your CI failed. Was it a flake or a real break?</title>
      <dc:creator>TECHDEV</dc:creator>
      <pubDate>Sat, 24 Oct 2026 12:08:16 +0000</pubDate>
      <link>https://dev.to/techdevfun/your-ci-failed-was-it-a-flake-or-a-real-break-561k</link>
      <guid>https://dev.to/techdevfun/your-ci-failed-was-it-a-flake-or-a-real-break-561k</guid>
      <description>&lt;h1&gt;
  
  
  Your CI failed. Was it a flake or a real break?
&lt;/h1&gt;

&lt;p&gt;Last week I watched a team re-run a red CI job three times, merge on the third green run, and ship the bug anyway. The test was not flaky. The retry just hid it for one run.&lt;/p&gt;

&lt;p&gt;The opposite happens just as often: a genuinely flaky test blocks a good PR for an afternoon while someone scrolls a 40,000-line log looking for the first real error.&lt;/p&gt;

&lt;p&gt;I built a small step that answers three questions before anyone re-runs anything:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Where did it actually break first?&lt;/li&gt;
&lt;li&gt;Is each failing test flaky, or genuinely broken?&lt;/li&gt;
&lt;li&gt;Has this exact failure happened before?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is called &lt;a href="https://github.com/techdevfun/ci-failure-detective" rel="noopener noreferrer"&gt;CI Failure Detective&lt;/a&gt;, and it runs entirely inside your runner. No log ever leaves your repo. No signup, no server, no bill.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fltoyqp6ctn9i0o6svylz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fltoyqp6ctn9i0o6svylz.gif" alt="Demo: flaky verdict, infra verdict, and failure clusters from real terminal runs" width="800" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Add one step after your test command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test 2&amp;gt;&amp;amp;1 | tee ci.log&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Diagnose failures&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;failure()&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;techdevfun/ci-failure-detective@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;log-file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ci.log&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It posts a diagnosis to the job summary: the first error line with context, a verdict per failing test with a confidence score and a reason, a stable 12-character signature for the failure, and suggested next steps. Suggestions only — it never quarantines, retries, or edits anything by itself. Automatically silencing failures hides real bugs, so that part stays human.&lt;/p&gt;

&lt;p&gt;As a CLI it works the same way, with zero dependencies (Node.js 18+):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ci-failure-detective analyze ci.log
cifd analyze ci.log &lt;span class="nt"&gt;--format&lt;/span&gt; md &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$GITHUB_STEP_SUMMARY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flaky detection needs history, so you commit a small JSONL file and append each run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cifd record &lt;span class="nt"&gt;--history&lt;/span&gt; .cifd-history.jsonl &lt;span class="nt"&gt;--commit&lt;/span&gt; &lt;span class="nv"&gt;$GITHUB_SHA&lt;/span&gt; &amp;lt; ci.log
cifd &lt;span class="nb"&gt;history&lt;/span&gt;   &lt;span class="c"&gt;# show failure clusters&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rules are deliberately boring and deterministic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A test that fails and passes on the &lt;strong&gt;same commit&lt;/strong&gt; is flaky, confidence 0.95.&lt;/li&gt;
&lt;li&gt;A test that flips frequently across recent runs is flaky, 0.70.&lt;/li&gt;
&lt;li&gt;A test that fails consistently on a commit where it used to pass is a real regression, 0.80.&lt;/li&gt;
&lt;li&gt;No history at all? The verdict is &lt;code&gt;unknown&lt;/code&gt;, with a reason. It never guesses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same-commit verdicts need per-test pass records, so run tests verbose (&lt;code&gt;jest --verbose&lt;/code&gt;, &lt;code&gt;pytest -v&lt;/code&gt;). Plain logs only record failures, so same-commit evidence is unavailable from them — flip-rate and regression verdicts still work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I verified before publishing (2026-10-24)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Test suite: 46/46 passing locally; CI green on Node 20.x and 22.x.&lt;/li&gt;
&lt;li&gt;Six fixture logs classify correctly: Jest, pytest, go test, Mocha, npm install failure, and tsc failure.&lt;/li&gt;
&lt;li&gt;A real failing &lt;code&gt;node:test&lt;/code&gt; run gets the right category (test failure) and the right first error line.&lt;/li&gt;
&lt;li&gt;Same-commit flaky detection works end to end through the real CLI: record a passing run, then analyze a failing run on the same commit, and it reports flaky at 0.95.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live GitHub Action run on a real pull request:&lt;/strong&gt; I ran the composite action on a private test repo's PR with a deliberately failing test. Both action invocations succeeded — one on the real failing log, one on a Jest fixture log — and a verify step asserted both produced a non-empty verdict and a 12-hex-character signature (real-log signature &lt;code&gt;41968f39ccc7&lt;/code&gt;, fixture signature &lt;code&gt;4f709b012d30&lt;/code&gt;). The run itself stayed red by design, because the test step failed. That is the point of a failure-diagnosis tool.&lt;/li&gt;
&lt;li&gt;Parsers today: Jest, pytest, go test, Mocha. Failure classification and first-error extraction work on any raw log, even without a test framework.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How this compares (checked 2026-10-24)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;What it costs you&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BuildPulse ($99/month Startup) / Trunk / Datadog / Mergify&lt;/td&gt;
&lt;td&gt;Flaky detection, dashboards, trend, auto-quarantine, alerts&lt;/td&gt;
&lt;td&gt;You upload all test results and logs to their cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gh-failmap (OSS)&lt;/td&gt;
&lt;td&gt;Clusters failed runs by shared error line&lt;/td&gt;
&lt;td&gt;CLI only, needs Python + &lt;code&gt;gh&lt;/code&gt; login, GitHub only, no flaky verdicts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flakestat (OSS, zero-dependency)&lt;/td&gt;
&lt;td&gt;Local-first flaky hunting&lt;/td&gt;
&lt;td&gt;Only hunts flakes via re-runs or JUnit XML; no log reading, no clustering, no Action&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI Failure Detective&lt;/td&gt;
&lt;td&gt;First error + flaky-vs-real + clustering, in the runner, zero dependencies&lt;/td&gt;
&lt;td&gt;No dashboard, no alerting, no auto-apply&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Where rivals beat us (honestly)
&lt;/h3&gt;

&lt;p&gt;BuildPulse and friends have dashboards, long-term trend analytics, auto-quarantine, multi-channel alerting, and AI grouping — we have none of that in v1. gh-failmap's clustering is a focused, proven approach; we have not run a head-to-head cluster comparison against it yet, so we claim no advantage there. flakestat's JUnit XML input is something we do not have yet.&lt;/p&gt;

&lt;p&gt;CI Failure Detective is not a replacement for those platforms. It is a specialized triage layer for teams that cannot or will not send logs out, or that want a deterministic answer inside the runner before deciding whether a retry is even sensible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No native &lt;code&gt;node:test&lt;/code&gt; parser yet: a real &lt;code&gt;node:test&lt;/code&gt; log gets the right category and first error, but its tests are not listed individually.&lt;/li&gt;
&lt;li&gt;No JUnit XML input yet, no dashboard, no alerting, no auto-quarantine apply, no SARIF output.&lt;/li&gt;
&lt;li&gt;History is a JSONL file you own and rotate yourself; there is no automatic rotation yet.&lt;/li&gt;
&lt;li&gt;If your log contains a secret, that secret is in the log you already have; the summary shows to the same audience that can see the log. Redaction is on the roadmap.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Roadmap (not in v1)
&lt;/h2&gt;

&lt;p&gt;JUnit XML as an additional input, Slack webhook output, more frameworks (PHPUnit, RSpec, .NET), SARIF output. Quarantine stays suggest-only by design.&lt;/p&gt;

&lt;p&gt;The repo is MIT, the Action is &lt;code&gt;techdevfun/ci-failure-detective@v1&lt;/code&gt;, and the README has the full input/output reference. If your team re-runs red jobs on hope, try the step that tells you whether hope was justified.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>devops</category>
      <category>testing</category>
    </item>
    <item>
      <title>Your agent asked "can I run this?" You said yes. It ran it 40 minutes later.</title>
      <dc:creator>TECHDEV</dc:creator>
      <pubDate>Fri, 23 Oct 2026 12:00:55 +0000</pubDate>
      <link>https://dev.to/techdevfun/your-agent-asked-can-i-run-this-you-said-yes-it-ran-it-40-minutes-later-9j</link>
      <guid>https://dev.to/techdevfun/your-agent-asked-can-i-run-this-you-said-yes-it-ran-it-40-minutes-later-9j</guid>
      <description>&lt;p&gt;I run coding agents the way most of us do now: it proposes, I approve, it executes.&lt;br&gt;
Last week I caught something uncomfortable in my own workflow. I told an agent&lt;br&gt;
"yes, run the deploy" — then got pulled into a meeting. Forty minutes later it&lt;br&gt;
ran the deploy. Different directory. Code that had changed twice since I'd&lt;br&gt;
looked at it.&lt;/p&gt;

&lt;p&gt;I had approved the command at 10:00. I had not approved the command at 10:40.&lt;br&gt;
But nobody wrote that down, so my yes quietly survived past its own relevance.&lt;/p&gt;
&lt;h2&gt;
  
  
  The pattern has a name: stale approvals
&lt;/h2&gt;

&lt;p&gt;DZone recently ran a piece on authorization leases for agentic workflows, and it&lt;br&gt;
clicked: the problem isn't that agents are disobedient. It's that our approvals&lt;br&gt;
have no expiry date, no scope, and no receipt. A human approval is a moment in&lt;br&gt;
time — but we hand it to the agent as if it were a permanent permission.&lt;/p&gt;
&lt;h2&gt;
  
  
  So I built the lease
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/techdevfun/leaseguard" rel="noopener noreferrer"&gt;leaseguard&lt;/a&gt; turns "yes" into a&lt;br&gt;
signed, time-boxed, directory-scoped lease:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;leaseguard grant &lt;span class="s2"&gt;"npm run deploy"&lt;/span&gt; &lt;span class="nt"&gt;--for&lt;/span&gt; 10m &lt;span class="nt"&gt;--note&lt;/span&gt; &lt;span class="s2"&gt;"hotfix for #42"&lt;/span&gt;
leaseguard run &lt;span class="nt"&gt;--lease&lt;/span&gt; &lt;span class="nv"&gt;$TOKEN&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; npm run deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gate checks: standing deny policy (deny always wins, even over a valid&lt;br&gt;
lease) → lease signature → expiry → directory scope → command pattern. Then it&lt;br&gt;
runs the command, passes through the exit code, and appends every decision to&lt;br&gt;
a hash-chained ledger. &lt;code&gt;leaseguard audit&lt;/code&gt; shows what ran, what was denied,&lt;br&gt;
which approvals died quietly unused, and whether anyone tampered with the log.&lt;/p&gt;

&lt;p&gt;One file, zero dependencies, Node 18+. The part I'm proudest of is what it&lt;br&gt;
&lt;em&gt;doesn't&lt;/em&gt; claim: it won't stop a determined adversary with your file&lt;br&gt;
permissions, and it says so in the README. It stops stale approvals, accidents,&lt;br&gt;
and casual misuse — which is the actual problem I had.&lt;/p&gt;

&lt;p&gt;Try the demo: grant a 3-second lease, use it, watch it expire, then audit.&lt;br&gt;
The stale approval dies quietly, and nothing runs on your old yes.&lt;/p&gt;

</description>
      <category>node</category>
      <category>ai</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
