
Threat Research
/Security News
Searchjacked: Hidden Browser Extensions Hijack Searches for 13 Million Users
319 browser extensions installed by 13.4 million users hijack searches, capture queries, and collect referral payments from Yahoo and Bing.
Enforcement layer for AI coding agents: finish every item you asked for, stop generating slop, stop repeating corrected mistakes — proven by machine-checked evidence, not the agent's own word.
No more skipped. No more slopped. No more forgotten rules. Exactly what you ask.
You asked for seven things. The agent did four, added two you never asked for, broke a rule you corrected last week, and announced "done". A2D makes that impossible to say.
It is not another way to run agents. It is an enforcement layer you put on top of the harness you already use — a checklist the agent cannot close on its own word, rules that survive its memory, and a completion verdict that is produced by running a proof, not by the model deciding it feels finished.
An agent looking for the shortest path to "done" can edit the checker, rewrite the rule
file, patch the hook config, git commit --no-verify, reinterpret a prose rule as
"not applicable here", or simply mark its own item complete.
So the design rule is one sentence:
Completion must be a fact outside the agent, not its opinion of itself.
Concretely, in this package:
a2d_plan refuses a
bare item and says what a good verifier for that item would look like.a2d_complete is an additional proof, never a substitute. A fabricated proof is
rejected and the item stays open.Remove any one and the enforcement leaks. The first two are a pair on purpose: verifying the proof achieves nothing if the agent chooses which proof after seeing its own work.
Sometimes the criterion really is wrong — the check was written against a file that has
since moved. a2d_replan is the legal exit: it records the old criterion, the proposed
one and your agent's reasoning into plan.json, and parks the item for you to decide.
It does not close the item and does not install the new criterion. Changing the definition
of done is allowed; changing it quietly is not.
Local enforcement protects you from a forgetful agent. It does not protect you from an agent that edits the rules — everything here is a file in its working directory. That is a real limit, and it is stated here rather than hidden.
npm install -g a2done
cd your-repo
a2d init
a2d init writes project-local config only. It never touches your global
~/.claude/settings.json.
$ a2d init
a2d init — repo: /Users/you/your-repo
✓ MCP: Registered MCP server "a2d" (claude mcp add).
✓ hooks: /Users/you/your-repo/.claude/settings.json
added: Stop, PostToolUse, UserPromptSubmit
✓ .a2d: /Users/you/your-repo/.a2d
✓ guardian: Installed git pre-commit guardian.
RESTART REQUIRED — if a Claude Code session is already open in this repo, exit and
start it again. Hooks and MCP servers are read at startup; a live session will keep
running unwired and a2d will look broken.
Then: the agent has a2d_plan / a2d_complete / a2d_status and cannot stop while the
checklist has open items. Run `a2d doctor` to verify wiring, `a2d uninstall` to remove it.
$ a2d doctor
a2d doctor — harness: Claude Code
(running inside Claude Code (CLAUDECODE env); .claude/settings.json present)
✓ node — v22.14.0
✓ MCP "a2d" registered (Claude Code) — present in `claude mcp list`
✓ MCP server built — /usr/local/lib/node_modules/a2done/dist/src/mcp/server.js
✓ guardian built — /usr/local/lib/node_modules/a2done/dist/src/guardian/pre-commit.js
✓ Stop hook — wired
✓ PostToolUse hook — wired
✓ UserPromptSubmit hook — wired
✓ .a2d state dir — /Users/you/your-repo/.a2d
✗ plan.json — none yet (created by a2d_plan)
✓ git pre-commit guardian — installed
On this machine (Claude Code), the enforcement that is LIVE is: MCP tools (plan-time
verifier gate + server-run completion); git pre-commit guardian; Claude Code hooks
(Stop / PostToolUse / UserPromptSubmit).
1 item(s) need attention (✗ above).
That one ✗ is correct: there is no plan until the agent makes one. a2d doctor reports
what is true, not what is comforting.
Run the same command under Codex and the three hook lines change, because Codex does not read the Claude Code hook protocol at all:
– Stop hook — n/a — Claude Code only. Codex does not read the hook protocol
(the entry is in settings.json, and is inert here)
On this machine (Codex), the enforcement that is LIVE is: MCP tools (plan-time verifier
gate + server-run completion); git pre-commit guardian.
NOT live here: Claude Code hooks — Codex cannot run them at all.
An earlier version printed those three as ✓. A green tick where there is no protection is
worse than a red one: it stops you looking.
$ a2d status
No plan. Call a2d_plan to create a checklist.
Canon: no rules yet.
Now give your agent a task with more than one part. It calls a2d_plan to register the
items, and cannot stop until each one is closed with a proof that passes.
Changed your mind? a2d uninstall is the exact inverse of a2d init — it removes only
the entries it recognises as its own, leaves a foreign pre-commit hook untouched, and
keeps your plan and rules unless you pass --purge.
A2D deliberately has no exclusive relationship with any harness.
| Surface | Works with |
|---|---|
MCP server (a2d_plan, a2d_complete, a2d_complete_batch, a2d_replan, a2d_status, a2d_rule_record, a2d_rule_query) | any MCP client — Claude Code, Cursor, Codex, and anything else that speaks MCP over stdio |
| git pre-commit guardian | any harness, any editor, any workflow that ends in a commit — no MCP needed |
| Stop hook and context injection | the Claude Code hook protocol, which is what a2d init wires automatically today |
The first two rows are the load-bearing ones and they are harness-neutral: the plan-time gate
and server-run completion live in the MCP server, and the guardian only needs git. a2d doctor
tells you which rows are live on the machine you are actually on.
Agent frameworks and workflow harnesses are all the same shape from here. If yours runs git and speaks MCP, the enforcement applies. Wiring adapters for further harnesses is free and always will be — that is distribution, not product.
Every checklist item carries one of these from the moment it is registered, and the server runs it to close the item:
| type | passes when … |
|---|---|
file_exists | the path exists (and, if given, contains a substring) |
command_exit | a command exits with the expected code |
grep_count | a regex matches a file/glob an exact number of times |
test_pass | a command exits 0 |
human | never machine-passes → the item becomes needs_human, not done |
Commands run anchored to the repo root, with a hard timeout. Details in SPEC.md.
human matters: an item that genuinely needs your eyes is not quietly auto-approved and
not silently dropped. It is parked in a state that is neither. And because every item now
needs a criterion, human is the honest answer for the ones no machine can settle — it is
accepted, and it can never come back as done.
Your plan says grep_count "2026" in page.html. Then the footer moves into a component.
The work is right and the check fails.
If there is no way out of that, the thing that gets deleted is the enforcement. So there is a way out, and it goes through you:
a2d_replan { item_id: "3",
new_verifier: { type: "grep_count", pattern: "<Year", path: "page.html", expect: 1 },
reason: "the year in the footer: hard-coded in the markup, or a variable/component?
the acceptance criterion depends on which" }
The item becomes needs_human with that question attached. The new criterion is recorded,
not applied — the registered one stays in force. Both, plus the reason, are written into
.a2d/plan.json, which is committed, so the change shows up in git diff rather than in
nobody's memory. One replan per item; a second is refused.
a2d status puts these in their own block, because they are the ones waiting on you:
⚠ 1 item(s) NEED A DECISION FROM YOU — they are not done and will not become done on their own:
☝ #3 footer shows the year
Q: the year in the footer: hard-coded in the markup, or a variable/component? …
criterion registered: grep_count /2026/ in page.html == 1
criterion proposed: grep_count /<Year/ in page.html == 1 ← NOT applied; your call
needs_human does not block a stop, so the agent finishes the rest of the plan instead of
grinding against an impossible item.
When you correct the agent, record the correction once. It becomes canon and applies from then on, in every session:
a2d_rule_record { text: "No emoji in headings", scope: "*.html", class: "hard",
verifier: { type: "grep_count", pattern: "🚀|✨", path: "*.html", expect: 0 } }
Rules come in two classes, and the difference is whether a machine can decide them:
Hard — carries a verifier, so it is decidable.
Edit/Write on a file in scope; a violation is reported back to
the agent immediately.git commit --no-verify — this is your machine, not a jail.Soft — a description with no checker, for taste that no regex can settle.
hits counter instead, so a soft
rule that keeps getting violated is visible as a candidate for a real checker.Rules live in .a2d/canon/*.yaml, one per file, human-readable, meant to be committed and
reviewed like code.
.a2d/plan.json the checklist — human-readable, commit it
.a2d/canon/*.yaml your rules — human-readable, commit them
.a2d/state-runtime.json per-item iteration counters — gitignored
Plain files, plain git. Nothing about your work leaves your machine — no telemetry, no
account, no phone-home. The only processes this package spawns are git, the claude CLI
during init/uninstall/doctor, and the commands you put in your own verifiers.
There is no network in the enforcement path. The Stop hook, PostToolUse,
UserPromptSubmit, the git guardian and the MCP server contain no network code and cannot
reach any — a verdict must never have a remote failure mode. That is not a promise in a
README; test/no-network.test.ts walks the real import graph of each of those entry points
on every test run and fails the build if any of them can reach fetch, node:http(s) or a
URL. There is exactly one place in the package that talks to the network, and it is next.
a2d init, a2d doctor and a2d status check once a day whether a newer version exists.
Three commands you type. Nothing that runs automatically.
What goes over the wire:
GET https://raw.githubusercontent.com/0leg-design/a2done/main/version.json
That is the whole request. No query string, no custom headers, no body, no cookies, no redirects, no identifier of any kind — there is no code here that generates one. What the server can learn is that somebody fetched a public file, which is what any crawler produces.
What comes back is a static file: { latest, notes_url, message }. If latest is newer than
what you have, you see something like:
a2d: 0.4.0 is out (you have 0.3.0).
a2d_replan lands: change a criterion without hiding it
notes: https://github.com/0leg-design/a2done/blob/main/CHANGELOG.md
update: npm install -g a2done · silence this check: A2D_NO_UPDATE_CHECK=1
Otherwise it says nothing at all. Timeout is 2 seconds and the answer is cached for 24 hours
in .a2d/state-runtime.json, which is already gitignored. Every failure — offline, DNS,
proxy, 404, malformed JSON — is silent: the CLI behaves exactly as if the channel did not
exist.
To turn it off entirely:
export A2D_NO_UPDATE_CHECK=1
With that set, no request is made at all — the check returns before it reaches the network.
A loop manifest is a checklist with its verifiers already attached:
a2d loop loops/sdd-page.yaml
Stated plainly, because you will find these out anyway:
human verifiers and soft rules are where that honesty lives.a2d init tells you to restart.Everything in this package, under Apache-2.0, with no account and nothing to sign in to:
a2d_replan — the visible way to change a criteriona2d doctor and a2d uninstallThis is not a trial and not a crippled tier. The line elsewhere is not "more rules"; it is about rules the agent cannot reach to weaken. This package is the half that is useful on its own.
npm install
npm run typecheck
npm test # unit tests
npm run test:integration # end-to-end on a clean repo: real MCP server, hook, guardian
npm run test:scenario # the enforcement transcript, printed verbatim
test:scenario is the one to read if you want to know whether any of the above is true. It
drives the compiled MCP server over stdio through the whole argument — a plan with no
verifiers, a verifier brought to the closing, real work, a replan, a second replan — and
prints every request and response.
v0.3.0. The gate moved to plan time and completion stopped accepting substitutes; doctor
stopped reporting hooks as live on harnesses that cannot run them. See CHANGELOG.md.
Apache-2.0. See LICENSE and NOTICE.
Copyright 2026 Oleg Kukharuk — oleg.design
FAQs
Enforcement layer for AI coding agents: finish every item you asked for, stop generating slop, stop repeating corrected mistakes — proven by machine-checked evidence, not the agent's own word.
The npm package a2done receives a total of 0 weekly downloads. As such, a2done popularity was classified as not popular.
We found that a2done demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Threat Research
/Security News
319 browser extensions installed by 13.4 million users hijack searches, capture queries, and collect referral payments from Yahoo and Bing.

Threat Research
Malicious Firefox extensions mimic Phantom, Jupiter, and Bitget wallets to steal recovery phrases and private keys, with one fetching its payload remotely.

Product
Socket Threat Intel now shows which malware campaigns were blocked or detected in your organization, with links to scans and Firewall events.