惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LINUX DO - 最新话题
T
Tor Project blog
G
GRAHAM CLULEY
S
Security Affairs
P
Palo Alto Networks Blog
TaoSecurity Blog
TaoSecurity Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
aimingoo的专栏
aimingoo的专栏
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园 - 三生石上(FineUI控件)
Cloudbric
Cloudbric
Cyberwarzone
Cyberwarzone
A
About on SuperTechFans
Microsoft Azure Blog
Microsoft Azure Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
C
CERT Recently Published Vulnerability Notes
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
Check Point Blog
宝玉的分享
宝玉的分享
Forbes - Security
Forbes - Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Microsoft Security Blog
Microsoft Security Blog
Schneier on Security
Schneier on Security
The Last Watchdog
The Last Watchdog
T
The Blog of Author Tim Ferriss
S
SegmentFault 最新的问题
H
Heimdal Security Blog
Recorded Future
Recorded Future
L
LangChain Blog
WordPress大学
WordPress大学
Know Your Adversary
Know Your Adversary
C
Cyber Attacks, Cyber Crime and Cyber Security
V
Visual Studio Blog
B
Blog
H
Help Net Security
T
Tailwind CSS Blog
The Hacker News
The Hacker News
雷峰网
雷峰网
P
Proofpoint News Feed
博客园 - Franky
Attack and Defense Labs
Attack and Defense Labs
有赞技术团队
有赞技术团队
S
Schneier on Security
T
Troy Hunt's Blog
云风的 BLOG
云风的 BLOG
Hacker News - Newest:
Hacker News - Newest: "LLM"
Blog — PlanetScale
Blog — PlanetScale
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
P
Proofpoint News Feed

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
I shipped the wrong abstraction, then deleted it
Mike Lane · 2026-06-01 · via DEV Community

git-prism's first git-interception mechanism was a Claude Code hook. It worked, until I watched an agent run make review: the Makefile shelled out to git diff, and the hook never fired. The interception I'd shipped was invisible to one of the cases it most needed to catch.

v0.9.0 fixes that. The hook is gone, replaced by a PATH shim that intercepts git at the process layer. This is the story of why a hook can't win here, what a spike proved, and what gave me the confidence to delete working, shipped code.

What git-prism is, in one paragraph

git-prism is an MCP server that gives AI coding agents structured git data instead of human-oriented porcelain. When an agent reads a unified diff, it pays tokens for @@ hunk headers, +/- line prefixes, and whitespace context that carry no semantic meaning. Then it has to reconstruct what actually changed (which functions, which imports, whether the file is generated) from raw text. git-prism hands it that structure directly. Five MCP tools cover it: get_change_manifest (what changed), get_file_snapshots (before/after content), get_commit_history (per-commit manifests), get_function_context (callers, callees, test references), and review_change (manifest plus context in one call, built to replace git diff <ref>..<ref>).

Here is that difference on a real change in git-prism's own history: the fix that taught the shim to return exit code 126 (found but not executable) instead of 127 (not found). The porcelain an agent gets from git diff:

@@ -67,8 +67,8 @@ impl<E: EnvSource> RealGitExec for StdRealGitExec<'_, E> {
-            eprintln!("git-prism shim: failed: {err}");
-            ExitCode::from(127)
+            eprintln!("git-prism shim: {} failed: {err}", real.display());
+            ExitCode::from(exec_failure_exit_code(&err))
         }
@@ -90,6 +90,17 @@ impl<E: EnvSource> RealGitExec for StdRealGitExec<'_, E> {
+/// Map an io error to the conventional shell exit code.

The same change as a get_change_manifest payload (trimmed to the one source file):

{
  "path": "src/shim/real_git.rs",
  "language": "rust",
  "change_type": "modified",
  "lines_added": 32,
  "lines_removed": 2,
  "functions_changed": [
    {
      "change_type": "modified",
      "name": "StdRealGitExec<'_, E>::passthrough",
      "signature": "fn passthrough(&self, argv: &[&str]) -> ExitCode",
      "start_line": 54, "end_line": 79
    },
    {
      "change_type": "added",
      "name": "exec_failure_exit_code",
      "signature": "fn exec_failure_exit_code(err: &std::io::Error) -> u8",
      "start_line": 97, "end_line": 102
    }
  ]
}

The agent doesn't reconstruct which functions moved; it's told. That's the whole product. So the question driving this release is: how do you make sure an agent's git calls actually reach git-prism?

The first abstraction: a redirect hook

The original answer was a Claude Code PreToolUse hook (ADR-0008). When an agent issued a Bash command, the hook inspected the command string before execution. If it saw git diff main..HEAD, it rewrote the call to route through git-prism's structured output instead.

For commands an agent types directly, this works. It also has a property the shim can't match: it sees the agent's intent, the literal text with full conversational context, so it can advise or soft-warn in-band. That's a real capability, and it's why I didn't expect to throw it away.

The blind spot

A PreToolUse hook fires on the top-level Bash command string. It is not a syscall interceptor. Any git call issued inside a subprocess never triggers the event:

  • make review, where the Makefile target runs git diff
  • a pre-push hook (lefthook, husky) that runs git under the covers
  • a cargo build script, a test harness, any wrapper an agent invokes

The hook sees make review. It never sees the git diff three layers down. Those calls reach the real git untouched and hand the agent exactly the porcelain git-prism exists to replace.

No hook patch fixes this. It's the layer the hook works at, not the depth of the bug. As long as interception happens on the command string, anything that reaches git through a process the agent didn't type stays invisible.

The pivot: a PATH shim

The fix is to intercept one layer down, at the process layer instead of the command string (ADR-0009).

Put a binary named git on PATH, ahead of the real git. Now every git invocation in that process tree resolves to git-prism first, including the nested ones, because child processes inherit PATH. git-prism then decides, per call:

  • Intercept when an AI agent is detected, the subcommand is on the watch list (diff/log/show/blame/pickaxe), and it carries a ref range (main..HEAD). Return structured JSON.
  • Pass through otherwise. Humans, CI, non-agents, a bare git status, git diff --staged: all hit vanilla git, unchanged.

A few design choices that mattered:

  • One binary, argv[0] dispatch. The shim is git-prism. At startup, main checks whether it was invoked under a name ending in git; if so, it enters shim mode, otherwise it runs the normal CLI. One build, one release artifact, no version skew between the shim and the JSON path it reuses. It's the classic Unix multi-call trick (busybox, coreutils' [).
  • A cross-process loop break. When the shim hands off to the real git, that git may itself shell out (hooks, build scripts), and those nested calls re-enter the shim, because it's still first on PATH. An in-process counter can't see across a process boundary; only an inherited environment variable can. So the shim sets GIT_PRISM_INSIDE_SHIM=1 in every child it spawns and passes straight through if it sees that flag on entry. As a bonus, GIT_PRISM_INSIDE_SHIM=1 git … is a user-facing escape hatch that forces vanilla git for one command.
  • An exact port of the existing classifier. The watch-list and ref-range logic is a faithful port of the hook's Python classifier into src/shim/classify.rs (same watch list, same ref-range detection), so the two interception points can't disagree about what counts as interceptable.

The shim also extends past git. With a gh symlink ahead of the real gh, the shim recognizes argv[0] == "gh" and routes gh pr diff <number> through the same manifest pipeline, resolving the PR's base..head with gh pr view and returning the same JSON. Every other gh subcommand passes through. One interception layer for every channel an agent reaches for.

The hard part: a frozen PATH

There was one load-bearing unknown, and it nearly sank the whole plan: does the shim's PATH entry actually reach the subshell Claude Code spawns for each Bash command? If Claude Code runs commands in a fresh shell that doesn't see the rc-edited PATH, the shim never resolves and the whole approach is dead.

I spiked it from inside a live Claude Code session, the exact process under investigation (ADR-0010). What I found:

  • Each Bash tool call runs as /bin/zsh -c, and the command is wrapped to first source a shell snapshot.
  • That snapshot is generated once per claude launch, and it hardcodes PATH as a single resolved string. It does not re-evaluate your rc files per command.
  • The snapshot captures rc-defined aliases and functions too, which proves it sources your rc when it's built, even though the launching shell didn't.

So PATH inside the Bash tool is the rc-derived PATH from when claude was launched, frozen into a per-session snapshot.

Then I tested a fake git ahead of the real one, five ways:

Invocation Intercepted?
Direct git status yes
sh -c 'git log' yes: children inherit PATH
git inside a Makefile target yes: build tools inherit PATH
script → script → git (2 levels) yes: transitive, arbitrarily deep
env -i PATH=/usr/bin:/bin git no: strip PATH and the shim is gone

Rows two through four are precisely the calls the redirect hook is structurally blind to. The shim catches all of them. That table is the empirical proof of the "shim ⊇ hook" claim.

It also defines the one constraint: the snapshot is frozen at launch, so you install the shim and then restart Claude Code. A PATH change after launch is invisible to the current session. That's a one-time step, not an ongoing race, but it has to be loud, which is why git-prism shim install prompts to append the export PATH line to your rc and then tells you, in plain terms, to restart Claude Code.

Deleting my own feature

Once the spike proved the shim is a strict superset of the hook on a correctly-configured PATH, the hook's status changed. It no longer covered anything the shim didn't, and it covered less. Keeping it would mean shipping two overlapping interception mechanisms, two classifiers to keep in sync, two things to document and reason about.

So v0.9.0 removes it (ADR-0011). It isn't deprecated-but-functional; it's deleted. The Python hook scripts are gone from the repo, the embed code is gone from src/hooks.rs, and git-prism hooks install now exits non-zero with a message pointing you at the shim. That commit removed 3,883 lines.

Deleting a shipped feature is the part that's supposed to feel reckless. It didn't, and the reason isn't nerve.

What let me hit delete without flinching

I'm a solo developer. There's no team to catch me, no second reviewer who remembers why the hook existed. The only thing standing between me and a confident-but-wrong deletion is the process I hold myself to. For this epic, that process was the safety net:

  • Spike → ADR, before any production code. The unknowns went on a disposable spike branch whose only deliverable is an ADR, with no TDD and no code to fall in love with. ADR-0008, 0009, and 0010 captured why the hook existed, why the shim supersedes it, and what evidence backs that, in writing, before I touched the implementation. By the time I got to removal, ADR-0011 wasn't a gut call; it was a conclusion with three documents of argument behind it. The "supersedes ADR-0008" line at the top of 0011 is the receipt.

  • BDD scenarios that bind to behavior, not internals. The acceptance tests are Gherkin run by behave: Python driving the Rust binary as a black box, on purpose. A different language than production means the tests can't reach into internals; they can only assert on observable behavior. They encoded the exact cases the hook missed (nested git, gh pr diff) as the shim's contract before I wrote it. When they went green, "the shim covers what the hook couldn't" stopped being a claim and became a passing test.

  • Strict TDD for the implementation. Red, green, triangulate, refactor. The exit-code fix I showed earlier shipped with tests/shim_exit_codes.rs asserting 126 for an unexecutable git and 127 for a missing one. Every behavior the shim has, a test pins down.

  • A pre-merge gauntlet, every time. Bug hunting, a quality audit, a security pass, adversarial QA, and a set of language-specific purity checks run before anything reaches main. "Tests pass" is necessary, not sufficient. Nothing in this epic skipped it.

  • A capstone demo as the final gate. The epic isn't done until there's a narrated, end-to-end recording proving it works, including the nested make review case and gh pr diff against a real PR. (That's the recording below.) If it can't be demonstrated, it isn't finished.

None of that is heroic. It's boring, and that's the point. The confidence to delete working code came from artifacts, not bravado: the ADRs said why, the BDD scenarios and the capstone said it still works. I could afford to be bold precisely because the process is conservative.

See it run

Demo GIF goes here: drag capstone.gif into the dev.to editor at this spot, or paste a hosted URL.

Six beats: git-prism shim install (with PATH consent) → git-prism shim status → a direct git diff main..feature returning JSON inside an agent session → the same diff caught when make review runs it as a subprocess → gh pr diff against a real PR returning a manifest → and git-prism hooks install erroring out, the hook formally retired.

(Full video: link the .mp4 once hosted.)

Migrating from the hook

If you were running the redirect hook, the upgrade is two commands plus a restart:

# Remove the old settings.json hook entry
git-prism hooks uninstall --scope user   # or --scope project / local

# Install the PATH shim (prompts to add the export PATH line to your rc)
git-prism shim install

# Then restart Claude Code so its frozen shell snapshot picks up the new PATH

Verify with git-prism shim status, which shows whether the shim is active and where it lives. Note the deliberate breakages: git-prism hooks install now exits non-zero by design, and git-prism hooks install --path-shim still works this release as a deprecated alias for git-prism shim install (with a warning). git-prism hooks uninstall and hooks status stick around for legacy cleanup.

Try it

cargo install git-prism
# or
brew tap mikelane/tap && brew install git-prism

# register the MCP server with Claude Code
claude mcp add git-prism -- git-prism serve

# put the shim ahead of git on PATH, then restart Claude Code
git-prism shim install

The shim is one interception layer for every channel an agent reaches for git: direct calls, nested subprocesses, and gh pr diff. The redirect hook is retired. Code, ADRs, and the full changelog are at github.com/mikelane/git-prism; the crate is on crates.io.

The move that looks like nerve, deleting your own shipped feature, was really just process showing its work.