Disclaimer: Opinions expressed are solely my own and do not express the views or opinions of my employer or any other entities with which I am affiliated to.
There’s a philosophical question buried in every lockfile, and most developers probably know about it.
When you run npm install and it reads from package-lock.json, what’s actually happening isn’t just “installing dependencies.” It’s an act of reproducing a known state. Someone, at some point, made a set of decisions about which versions of which libraries to trust. The lockfile is the artifact of those decisions. It exists so that every subsequent build doesn’t have to re-make them, and so that when something goes wrong, you can trace back to the exact combination of inputs that produced the problem.
Reproducibility isn’t a convenience. It’s how you debug. It’s how you audit. It’s how you answer the question “why does this work the way it does?” six months from now when nobody remembers.
I keep coming back to this because I think we are quietly abandoning this principle in the most consequential part of modern software development. And the thing that bothers me most isn’t that nobody has solved it. It’s that almost nobody has noticed.
Open any active repository at a mid-size engineering org right now and look at the root directory. If the team uses more than one AI coding tool, and most do, you’ll find some combination of this:
.cursor/
rules/
.claude/
skills/
.windsurf/
rules/
.github/copilot-instructions.md
.opencode/
.zed/
CLAUDE.md
AGENTS.md
GEMINI.mdEvery AI coding tool has invented its own configuration surface. Its own folder, its own rules format, its own discovery mechanism. None of them talk to each other. An engineering manager recently described his repo as having eight different config paths for what is fundamentally the same thing: telling the AI how to behave. His solution? A shell script that hard-links a single markdown file into every tool’s expected location. That’s where we are. Shell scripts holding the config layer together with string and duct tape.
AGENTS.md is emerging as a cross-tool standard with adoption across 60,000+ open source projects, which is encouraging. But the fragmentation is still bad enough that people are writing symlink scripts just to keep their instructions consistent across Cursor, Claude Code, Windsurf, and Copilot. That’s the visible symptom.
The deeper issue is what you can’t see in the file tree: every developer on that team is running a completely different agent stack under the hood, and there’s no mechanism to detect, enforce, or even record the divergence.
Think about a pretty ordinary week at a pretty ordinary company. The backend team is building an API service in Rust. The frontend team is building the dashboard in TypeScript. A platform engineer is writing infrastructure-as-code.
The backend engineer chose Claude Code backed by Opus 4.6, deliberately. Opus reasons more carefully through ownership semantics and lifetime annotations. For unsafe Rust, where a subtle mistake compiles fine but causes memory corruption at runtime, that deeper reasoning isn’t a luxury. It’s a material input to code safety.
The frontend engineer is on Cursor backed by GPT-5.3, because for rapid TypeScript UI iteration, the model’s speed and pattern completion are more valuable than deep reasoning. Fair choice.
The platform engineer hasn’t updated their configuration in two months and is still running Sonnet 4.5.
All three are committing to the same monorepo. All three are producing code shaped by models with fundamentally different reasoning characteristics, different strengths, different failure modes. The Opus sessions handle Rust lifetime analysis differently than GPT would. The Sonnet 4.5 sessions produce different patterns from both. These aren’t editor preferences. The model is making architectural decisions, choosing patterns, selecting approaches. It’s a core input to the code.
And it’s completely unpinned.
Models are just one dimension. Here’s the rest of the stack that’s silently diverging across your team.
Skills. The SKILL.md ecosystem crossed 2,600 published skills earlier this year, spanning Claude Code, Codex, Cursor, and a growing list of platforms. They’re the extension ecosystem for AI agents, portable instruction sets that teach an agent specific workflows. Developers install them casually: browse a registry, grab one that looks useful, start coding. Researchers have already found over 340 malicious skills harvesting credentials, exfiltrating environment variables, or injecting behavioral guidance that subtly shapes every line of code the agent writes. When a developer loads a skill, it becomes part of the agent’s instruction set. When the session ends, no record of which skills were active survives. This can be however managed / controlled at the enterprise
Rules files. CLAUDE.md and .cursor/rules and AGENTS.md sit in the repo, which creates the illusion they’re managed. They’re not. They’re configuration for an autonomous system disguised as documentation, and nobody treats them with the rigor that deserves. They drift. They go stale. They contain instructions from engineers who left the company months ago, still telling the agent to trust certain internal packages, skip certain validation, use architectural shortcuts that made sense in a different threat model. The agent follows them faithfully, because that’s what agents do.
MCP servers. Each developer connects whichever MCP servers they’ve set up locally. The MCP specification doesn’t require tool description signing or versioning, so there’s no way to pin or verify the behavioral instructions these servers provide. One developer has a community-built database tool that subtly influences how the agent constructs queries. Another has no MCP servers at all. Same repo, same prompt, different context, different code.
None of this is visible to anyone. Not to the developer, not to the reviewer, not to CI, not to your security team.
The obvious argument for pinning is consistency: everyone on the team should run the same approved configuration. That’s true, but it’s the least interesting reason.
The more important reason is that different models and tools are genuinely better at different things, and those differences have real consequences for code quality and security. Pinning isn’t about restriction. It’s about making the choice deliberate.
You might specifically want Opus 4.6 for the Rust backend because its deeper reasoning catches lifetime and ownership issues that lighter models miss. That’s not a preference. It’s an engineering decision with safety implications, and it should be documented, reviewed, and enforced the same way you’d enforce a linter configuration or a minimum test coverage.
The same logic applies to skills. A skill designed for secure authentication flows should be a pinned, reviewed, approved dependency for any PR touching auth, not something one developer happens to have installed and another doesn’t. A skill that generates database migrations should be version-locked so every developer’s agent produces migrations with the same conventions.
This is exactly how we think about library dependencies. We don’t let one developer use ORM version 2.x and another use 3.x in the same project. We pin it. We review the upgrade. We test the change. The same discipline needs to apply to the tools that are now writing our code.
You could argue that configuration divergence is manageable today because a human still reviews the output. Even a quick scan of a diff catches the worst problems. A thin safety net, but it exists.
That net is disappearing faster than most people realize.
Multi-agent workflows are moving from demos into production. A planning agent decomposes tasks, Coding agents implement the pieces, Review agents evaluate the PRs. Anthropic’s own research shows autonomous Claude Code sessions nearly doubled in duration between late 2025 and early 2026, with tail sessions running over 45 minutes before a human touches anything. AI-powered code review tools like CodeRabbit, or even Claude are already handling PR evaluation at scale in real organizations.
Question for the security teams to think about: what does your review agent actually know about the coding agent’s session?
The answer: the diff. That’s it.
The review agent doesn’t know which model version wrote the code. It doesn’t know which skills were active. It doesn’t know which MCP servers were connected or what tool descriptions shaped the agent’s decisions. It can evaluate whether the code looks correct. It cannot evaluate whether the code was produced under conditions you’d trust.
That’s not code review. That’s pattern matching on the output of a black box.
A lockfile in the dependency world does three things: it pins exact versions so everyone builds the same way, it enables reproducibility so you can rebuild any past state, and it makes changes auditable so a version bump is a visible, reviewable event.
An agent.lock does the same, for the agent stack.
# agent.lock
# Pinned agent configuration for this repository
# Generated: 2026-04-10T14:32:00Z
[model]
provider = "anthropic"
default = "claude-opus-4-6-20260205"
min_version = "claude-sonnet-4-6-20260410"
[model.overrides]
# Deliberate model choices per codebase area
"src/rust/**" = { model = "claude-opus-4-6-20260205", reason = "deep reasoning for unsafe/lifetime analysis" }
"src/frontend/**" = { model = "claude-sonnet-4-6-20260410", reason = "faster iteration for UI components" }
[skills]
"frontend-design" = {
version = "2.1.0",
publisher = "anthropic/official",
hash = "sha256:b4c2d8e...",
verified = true
}
"auth-patterns" = {
version = "1.0.3",
publisher = "community/securecoder",
hash = "sha256:f7e1a3b...",
verified = false,
review_status = "approved-2026-03-20"
}
"db-migrations" = {
version = "0.8.1",
publisher = "community/dbtools",
hash = "sha256:a1c9e2f...",
verified = true
}
[rules]
"CLAUDE.md" = {
hash = "sha256:c9d4e1f...",
last_reviewed = "2026-03-15",
reviewed_by = "@lead-engineer"
}
[mcp_servers]
"github" = {
version = "1.4.0",
publisher = "github/official",
tool_description_hash = "sha256:d2f5b9a..."
}
"internal-docs" = {
url = "https://internal.company.com/mcp",
tool_description_hash = "sha256:e8a3c7d..."
}
[constraints]
require_skill_verification = true
allow_unverified_skills_if_reviewed = true
require_rules_review_within_days = 90
block_unregistered_mcp_servers = true
allow_model_fallback = falsePinning. Every component has a version and a content hash. Model, skills, rules files, MCP tool descriptions. Change any of them and the lock breaks.
Enforcement. When a developer starts an agent session in this repo, the tooling checks the lockfile. Wrong model? Blocked. Unregistered skill? Blocked. MCP server with a changed tool description? Flagged, re-authorization required. Configuration isn’t documented after the fact; it’s enforced before the agent writes a single line.
Auditability. CI diffs the agent.lock exactly the way it diffs package-lock.json. A PR that adds a new skill, changes the model, or connects a new MCP server shows up as a lockfile change. Reviewers see it. Security teams can gate on it.
Drift detection. The lockfile becomes the source of truth for what’s approved. If a developer’s local setup drifts, they know immediately. Though it’s worth noting: the lockfile captures the approved configuration. Capturing what actually ran for a specific commit, the full runtime context, the session state, which skills fired, which MCP tools were called, that’s a related but distinct problem. It’s the difference between a menu and an order ticket, and it’s important enough that I plan to writing a separate piece about it.
Here’s where this gets tangible.
When you clone a repo and run npm install, the package manager reads the lockfile and sets up your dependency tree to match the team’s approved state. You don’t think about it. It just works. We need the same moment for AI agent configuration.
youragent install agent.lockThat command would configure the developer’s agent environment to match the repo’s pinned state. Pull the specified skills at the correct versions and verify their hashes. Register the allowed MCP servers. Set the model version or model overrides. Validate the rules file against its expected hash. If something doesn’t match, fail loudly, just like npm ci fails when the lockfile is out of sync.
The same mechanism extends to CI, and this is where it gets really powerful. When a review agent spins up to evaluate a PR, it runs youragent install first. Now it’s not just looking at a diff in a vacuum. It has the full configuration context: which skills are approved for this repo, what behavioral instructions the rules file contains, which MCP servers the coding agent was expected to connect to. It can pull the skill definitions to understand why a coding agent chose certain patterns. It can check whether the PR’s code aligns with the conventions the pinned skills enforce. It can flag a dependency choice that matches a known bias in an unverified skill’s instructions.
The review agent goes from pattern matching on syntax to reasoning about intent. And it all starts with a lockfile and an install command.
IDE vendors need to ship lockfile support. Cursor, Claude Code, VS Code with Copilot, Windsurf. They already have all the data. The first vendor to generate and enforce an agent.lock creates the de facto standard. Same dynamic that drove lockfile adoption in package managers: one tool gets it right, and it becomes table stakes.
The ecosystem needs to converge on configuration. The .cursor/, .claude/, .windsurf/, .github/copilot-instructions.md, .opencode/, .zed/, CLAUDE.md, AGENTS.md, GEMINI.md situation is unsustainable. An agent.lock would at least give teams one canonical file for the parts that actually matter for security and reproducibility, regardless of which IDE each developer uses.
The MCP spec needs tool description signing and versioning. You can’t pin what you can’t hash. Tool descriptions need to be signed, versioned, and pinnable so an agent.lock can reference a specific, verifiable snapshot of what a server exposes.
The skills ecosystem needs trusted publishing. Publisher verification, content signing, version pinning, integrity hashes. PyPI learned this the hard way. npm learned it the hard way. The skills ecosystem is in its “hope nobody notices the malicious packages” phase, and we know exactly how that story ends.
CI needs lockfile awareness. The same way your pipeline fails when package-lock.json is out of sync, it should fail when agent configuration drifts. No silent divergence. No surprise skills. No model roulette.
We all use AI coding tools every day and the leverage is real.
But I’ve started noticing something about my own workflow that I can’t unsee. When I accept agent-generated code and commit it, I can’t fully explain the conditions under which it was produced. Not because I wasn’t paying attention, but because the system doesn’t preserve that information. The skills that were loaded, the exact model version that ran, the MCP tool descriptions that shaped the agent’s decisions: gone the moment I hit commit.
We solved the “it works on my machine” problem for builds a decade ago because we recognized that reproducibility isn’t optional for software you ship to production. We need to recognize that “my agent wrote it differently than your agent” is the same category of problem, with the same solution pattern, and the same urgency.
Every package has a lockfile. Every build has provenance. Every dependency has a hash.
It’s 2026. Let agents also have a lock file.

























