Route architectural constraints to AI coding agents per file
Boffin feeds an AI coding agent the architectural constraints for the exact file it's editing, then requires it to verify the result.
1.0.1Add to Favorites
Why it matters
Prevent AI coding agents from turning small fixes into large rewrites by injecting file-specific architectural constraints before edits and requiring proportional verification afterward, ensuring agents respect boundaries like sync/async separation, trust boundaries, and scope limits.
Outcomes
What it gets done
Select and route only the architectural constraints relevant to the specific file being edited
Require verification checks proportional to the scope of the code change
Preserve critical boundaries like sync/async separation, API contracts, and state ownership during refactors
Deliver signed constraint packs to Cursor, Claude Code, Codex, and OpenCode from a single npm package
Source
Get it from source
Spark does not host a copy of it.
Open sourceReports
Agent outcome reports
No reports yet
Overview
Boffin
Boffin is a staff-engineer control layer for AI coding agents that routes only the architectural constraints relevant to the file being edited, then requires proportional verification of the result. It runs as one npm package across Cursor, Claude Code, Codex, and OpenCode, with every constraint shipped as readable, signed markdown. Use it when an AI agent keeps expanding small fixes into large rewrites, or when a change risks breaking a real architectural invariant. It is not a linter, sandbox, or security tool, and does not replace tests or code review.
What it does
Boffin is a staff-engineer control layer for AI coding agents: it feeds the agent the architectural constraints that apply to the specific file it's about to edit, then requires the agent to verify the result. Unlike a static AGENTS.md block that applies the same instructions to the whole repository, Boffin routes only the constraints relevant to the current edit, powered by its ParselFire Core engine. In a public case study on DuckDB, a guided refactor landed at +17/-17 lines with 2,104 assertions passing across 8 test files.
When to use - and when NOT to
Use it when an AI agent keeps turning a requested small fix into a large, scope-creeping rewrite, or when a change is genuinely risky - merging a real special case, blurring a sync/async boundary, or moving state away from its owner - and you want the agent stopped before it merges that mistake rather than caught after. It is not a linter, CI gate, command sandbox, or security tool: it does not isolate processes, filter shell commands, or restrict filesystem or network access, and it does not replace your repository's tests or code review, which remain authoritative. It also isn't built for speed - the entire framing is review-safety, so expect it to slow an agent down on purpose when a change looks risky.
Inputs and outputs
For a focused change, Boffin keeps the requested scope small and asks for the narrowest check that proves the edit; for an open-ended refactor or review, it requires a read-only audit first, then one verified finding at a time, and correctness always wins over cleanup when the two conflict. Three profiles - lite, full, max, selected via /boffin lite and similar on plugin hosts - tune how much cleanup pressure is applied, not correctness: every profile keeps the same trust-boundary, data-loss-prevention, security, and accessibility rejection rules, and there is no off profile. Generated code carries boffin: audit-token comments marking which invariants held or were refused, greppable across a codebase and disabled with an empty .boffin-trace-off file; nothing is sent anywhere.
Integrations
Boffin ships as one npm package (boffinit) for Cursor, Claude Code, Codex, and OpenCode.
npx boffinit cursor
Claude Code installs via /plugin marketplace add MicSm/boffin then /plugin install boffin@boffin; Codex via codex plugin marketplace add MicSm/boffin and codex plugin add boffin@boffin, plus a one-time /hooks trust step since Codex doesn't trust plugin hooks by default; OpenCode via npx boffinit opencode, delivering always-on guidance through .boffin/AGENTS.md. Portable adapters extend it to other hosts that read AGENTS.md, CLAUDE.md, or workspace rules. Every constraint ships as readable, GPG-signed, versioned markdown under packs/ in the repository, so nothing is hidden at install time. It requires Node.js 18+ and is MIT-licensed.
Who it's for
Teams who've been burned by an AI coding agent's small fix ballooning into an unreviewed rewrite, and who want architecture-aware guardrails and proportional verification built into the agent's workflow itself, on top of, not instead of, their existing tests and review process.
Source README
Boffin
Boffin (npm: boffinit) is a staff-engineer control layer for AI coding
agents: it feeds the agent the architectural constraints for the exact file it
is editing and makes it verify the result -- DuckDB case study:
a guided refactor landed at +17 / -17 lines with 2,104 assertions passing.
You ask for a 15-line fix; the agent comes back with a 500-line renovation.
Boffin gives the agent the architectural constraints that apply to the file it
is about to touch, then makes it verify the result.
It is not another AGENTS.md and not a prompt pack. Those formats usually ship
one static instructions block for the whole repository; Boffin routes only the
constraints relevant to the current edit. Powered by ParselFire Core.
npx boffinit cursor
What Boffin is not
- Not a static repo-wide rules file (
AGENTS.md-style one block for everything) - Not a prompt pack or system-prompt trick
- Not a linter or CI gate -- it acts before and after the edit
- Not a speed tool -- the frame is review-safety
What Boffin does
- Selects the constraints relevant to the edit in front of the agent
- Requires verification proportional to the change
- Ships for Cursor, Claude Code, Codex, and OpenCode from one npm package
- Delivers signed portable packs powered by ParselFire Core
How it differs (honest comparison)
Static rules file (AGENTS.md) |
Boffin | |
|---|---|---|
| Delivery | Usually one static block for the whole repo | Constraints routed to the current edit |
| Verification | None required by the format | Required, proportional to the change |
| Evidence | Usually none | Recorded case studies with numbers |
Proof, not promises
The public case studies record these guided refactors on real open-source code:
- DuckDB:
+17 / -17; 2,104 assertions
across 8 test files passed; distinct continuation and recovery paths were
preserved. - FastAPI:
+16 / -33; 49 tests passed;
no public API change. - LangChain: the sync/async boundary was
preserved; 4 tests passed.
These are reproducible case studies, not a controlled A/B benchmark.
Install
Boffin requires Node.js 18 or newer.
Cursor
Run from your project:
npx boffinit cursor
Claude Code
Run these inside Claude Code:
/plugin marketplace add MicSm/boffin
/plugin install boffin@boffin
Codex
Run these from a terminal:
codex plugin marketplace add MicSm/boffin
codex plugin add boffin@boffin
Codex does not trust plugin hooks automatically. Run /hooks once inside Codex
to review and trust Boffin's hooks; until then the plugin's skills work but the
automatic per-session activation stays off.
OpenCode
Run from your project:
npx boffinit opencode
Then open the project in OpenCode. Always-on guidance lands viaopencode.json -> .boffin/AGENTS.md. On demand: /boffin,/boffin-review, or the boffin / boffin-review skills.
Install details, commands, and troubleshooting:
OpenCode delivery.
Want the machinery? Read how ParselFire Core works.
What Boffin is fussy about
Similar code is not always the same code. Boffin gives the agent a reason to
stop before it merges a real special case, blurs a sync/async boundary, moves
state away from its owner, or turns a focused task into a tour of the codebase.
- For a focused change, it keeps the requested scope small and asks for the
narrowest check that proves the edit. - For an open-ended refactor or review, it requires a read-only audit first,
followed by one verified finding at a time. - When cleanup conflicts with an earlier correctness rule, correctness wins.
The point is not to make the agent timid. It is to make the expensive details
explicit before they become an interesting afternoon.
FAQ
How is Boffin different from AGENTS.md?
AGENTS.md is usually one static instructions file for the whole repository.
Boffin routes only the architectural constraints relevant to the file the agent
is about to edit, then requires a check proportional to the change.
Where do the constraints come from?
Every rule ships in this repository as readable, versioned markdown underpacks/, and the packs are GPG-signed. Nothing is hidden at install
time: open any pack and read every rule before trusting it. At edit time Boffin
selects which of those rules apply to the file being touched. See
how ParselFire Core works for the routing map.
Why does my coding agent turn small fixes into huge rewrites?
You ask for a small fix; the agent comes back with a renovation. Boffin
injects the load-bearing constraints for the current file before the edit and
forces verification afterward.
What do lite, full, and max change?
They tune cleanup ambition, not correctness:
litekeeps cleanup pressure low and favors the smallest useful change.fullis the balanced default.maxapplies the strongest cleanup pressure when the task justifies it.
On plugin hosts, select a profile with /boffin lite, /boffin full, or/boffin max. There is no off profile.
Do profiles change the safety floor?
No. Every profile keeps the same early correctness stages and rejection rules,
including trust-boundary validation, data-loss prevention, security, and
accessibility requirements.
What are boffin: comments in generated code?
boffin: marks are machine-readable audit tokens: they let you grep/harvest
which invariants held and which were refused across a codebase. Nothing is
sent anywhere; the payload is the invariant itself. Disable with one line:
create an empty .boffin-trace-off file at the repository root.
Is Boffin a command sandbox or security tool?
No. Boffin does not isolate processes, filter shell commands, or restrict
filesystem or network access. It guides architectural decisions in generated
code. Use command sandboxes and security controls for their own job; Boffin has
a different job.
How do I uninstall the Cursor or OpenCode integration?
npx boffinit cursor uninstall
npx boffinit opencode uninstall
Each uninstaller removes that host's managed files only. Shared.boffin/packs and .boffin/VERSION stay if the other host is still
installed. Unrelated project files are left alone.
Does Boffin replace tests or code review?
No. It tells the agent which contracts deserve attention and requires external
checks, but your repository's tests and review process remain authoritative.
Other hosts
Portable adapters cover hosts that read AGENTS.md, CLAUDE.md, workspace
rules, or repository instructions. See
host delivery and adapters for
the technical map.
Project
- Repository: https://github.com/MicSm/boffin
- Engine documentation: ParselFire Core
- Evidence: Python and
C++ - Contributions: CONTRIBUTING.md
Boffin is available under the MIT License. See credits.
npx boffinit cursor
examples/before-after-cpp.md
C++ Before/After Evidence
This note summarizes a guided refactoring experiment against a real C++ project
cloned under .repos/: DuckDB, an analytical in-process SQL database
(~39k stars). It uses the current live pack surfaces and fresh verification
gathered in this session rather than historical notes.
The point of this example is not the cleanup. It is the catastrophe the agent refused to cause. The target is DuckDB's parallel execution engine-a high-stakes concurrency surface. The request was a classic AI trap: "Make it more DRY and remove redundant checks."
Without guardrails, AI tools "clean up" C++ by silently erasing the exact invariants that prevent deadlocks. Here is how Boffin (powered by ParselFire Core) stopped it.
DuckDB: src/parallel/pipeline_executor.cpp
Repo: https://github.com/duckdb/duckdb
Refactor branch: refactor-pipeline_executor on the fork
https://github.com/MicSm/duckdb (commit a1b3ffc97f).
Before
PipelineExecutor drives a query pipeline across worker threads. To a naive AI (or a junior developer), the file looks like a minefield of copy-pasted code waiting to be DRY'ed:
- Four
BLOCKED/INTERRUPTEDreturn paths (source, sink, next-batch, combine) repeat a nearly identical shape. - Two
FinishSourcecalls look completely redundant. - The batch-index initialization block is copy-pasted verbatim in the constructor and in
Reset().
A standard LLM eagerly merges these. But doing so destroys the state machine.
Loaded guidance
Loaded packs/universal/pack.urf.md and packs/cpp-architecture/pack.urf.md.
Because the request is a late-stage DRY refactor (S04) of a parallel executor,
the read set was widened across the early-stage correctness leaves the code
actually touches, and stages S00-S06 were walked before any edit:
packs/universal/control-flow.urf.md(S01-S02)packs/universal/lifecycle.urf.md(S03)packs/universal/shared-abstractions.urf.md(S04)packs/cpp-architecture/concurrency.urf.md(S01/S03)packs/cpp-architecture/lifecycle.urf.md(S03)packs/cpp-architecture/state-modeling.urf.md(S02)
The Fixes Applied
Instead of breaking the file, the agent used the stage-walk to find real bugs:
- Caught a cross-execution liveness bug (
UNI-K23): The agent noticedReset()cleared every per-execution flag exceptsource_profiling_finalized. A reused executor would start its next run with a stale flag, skipping profiling on early LIMIT termination. The agent added the missing reset. - Removed dead state (
UNI-K16): Deletedexhausted_sourcewhich was written but never read, relying purely on the trueexhausted_pipelinelatch. - Safe invariant extraction (
UNI-K24): Extracted the actually safe duplication (the batch-index setup) into a singleInitializeLocalSinkPartitionInfo()helper, preserving its criticalD_ASSERTpreconditions.
The Catastrophes Avoided (Guard rails kept intact)
This is what happens when you load architectural constraints before generating code. The agent refused to make the destructive edits:
- Refused to collapse thread-suspension states (
UNI-X08/UNI-K18): An unguided LLM merges the fourBLOCKED/INTERRUPTEDpaths because they look the same. Boffin stopped it, recognizing that returningSinkNextBatchType::BLOCKEDversusOperatorResultType::BLOCKEDpersists completely different thread continuation flags (next_batch_blockedvsremaining_sink_chunk). Flattening them would cause silent pipeline hangs and deadlocks. - Refused to merge lifecycle overloads (
UNI-K08): The twoFinishSourcecalls were left alone. The agent verified they encode different active-operator contracts depending on whether aStartOperatorlock bracket is active. - Preserved cross-thread visibility (
CPP-K02/CPP-K26): The agent explicitly refused to "simplify" synchronization, citing the rule that cross-thread global state must stay behind itsannotated_mutex, while per-executor flags must remain instance-owned. - Preserved explicit thread detaching (
CPP-X28): The debug-onlythread.detach()blocks were flagged but deliberately kept because the agent identified them as isolated async test scaffolding (#ifdef DUCKDB_DEBUG_ASYNC_SINK_SOURCE).
After
pipeline_executor.cpp has one shared batch-index helper, one fewer dead field,
and a Reset() that fully restores execution state. Every distinct interrupt,
profiling, and output path that the file relied on is still explicit.
Why better
- We prevented a catastrophic breakage of the execution engine.
- The one genuinely duplicated invariant (batch-index setup) now lives in one place instead of two.
- A dead state field is gone, and a reused executor no longer carries a stale profiling flag into its next run.
- The interrupt-recovery special cases stayed explicit instead of being flattened into a fake generic path.
- The change stayed local to one file pair, with no public API change, and deleted as much as it added.
Verification
- build: configured and built with MSVC 19.44 (Visual Studio 2022 Community) +
CMake/Ninja,Debug; the parallel translation unit that containspipeline_executor.cppcompiles clean (no errors, no warnings) - tests: ran the intra-query parallelism and parallel-CSV suites plus the
parallelism-verification test from the project's ownunittestbinary -2104 assertions across 8 test files, all passing(1 test skipped: requires
thetpcdsextension) git diff --stat:src/include/duckdb/parallel/pipeline_executor.hpp | 5 ++--src/parallel/pipeline_executor.cpp | 29 +++++++++++------------2 files changed, 17 insertions(+), 17 deletions(-)
- linter: no diagnostics for the touched files
What This Example Demonstrates
- Any 7B parameter model can delete duplicate lines. It takes architectural memory to know which duplicates are load-bearing. The strongest signal here is that the agent walked the correctness stages first and explicitly refused to flatten thread-continuation paths that a line-count-driven refactor would have destroyed.
- Universal rules need C++ translation: Universal guidance caught the DRY trap, but the C++ pack provided the exact vocabulary (
CPP-K02,CPP-K26,CPP-X28) needed to evaluate cross-thread visibility and detached worker lifecycles accurately. - Audits find what diffs miss: Loading the early-stage correctness leaves turned a dangerous "make it DRY" request into an audit that found a missing lifecycle reset and a dead field, while leaving the high-risk concurrency code intact.
Reproduction Outline
- Clone or update the target repository under
.repos/. - Load
packs/universal/pack.urf.mdandpacks/cpp-architecture/pack.urf.md,
route to the relevant leaves, and walk stages S00-S06 before deciding what to
act on. - Because the seam is a late-stage DRY refactor, also load the early-stage
correctness leaves the code touches (control-flow, lifecycle, concurrency,
state-modeling) and say so in the write-up. - Do a read-only audit pass first, record findings by stage, then apply and
verify only the safe rows one at a time. - Build with the project's own toolchain (here: MSVC + CMake/Ninja) and run a
targeted subset of the project's own tests that exercises the changed
subsystem; record the exactgit diff --statthat produced the example.
examples/before-after-python.md
Python Before/After Evidence
These notes summarize two guided refactoring experiments against external Python
repositories cloned under .repos/. They use the current live pack surfaces
and fresh verification gathered in this session rather than historical notes.
FastAPI: fastapi/security/http.py
Before
HTTPBase.__call__, HTTPBearer.__call__, and HTTPDigest.__call__ repeated
the same Authorization parsing and auto_error handling. The only real
variation between bearer and digest was the expected scheme, while HTTPBasic
still carried genuinely distinct protocol work.
Loaded guidance
Loaded packs/universal/shared-abstractions.urf.md andpacks/universal/control-flow.urf.md. Using two universal leaves was justified
here because the change truly spans shared-invariant extraction and special-case
retention.
Applied:
universal/shared-invariant-extraction(UNI-K24)universal/parameterized-duplicates(UNI-K28)universal/exceptional-path-retention(UNI-K08)
Guard rails kept intact:
universal/repeated-invariant-inline(UNI-X24) was removed by moving the
shared authorization invariant intoHTTPBaseuniversal/special-case-flattening(UNI-X08) was not introduced;HTTPBasic
kept its separate base64 decode and colon-validation path
After
HTTPBase now owns the shared authorization invariant through_get_authorization_credentials(). HTTPBearer and HTTPDigest declare only a
private _expected_scheme and inherit the shared __call__ behavior.HTTPBasic stays on its own path because it still needs protocol-specific
decode and credential-shape validation.
Why better
- one authorization invariant now lives in one place instead of three near-copies
- bearer and digest variation is expressed as bounded policy instead of repeated
control flow - the refactor stayed local to one file and deleted more code than it added
- the special HTTP Basic path stayed explicit instead of being flattened into a
fake generic branch
Verification
git diff --stat:fastapi/security/http.py | 49 ++++++++++++++++--------------------------------1 file changed, 16 insertions(+), 33 deletions(-)
git diff: one file changed, no public API changes, helper kept private onHTTPBase- tests:
49 passedvia the repo venv across thetest_security_http*.pysuite - linter: no diagnostics for the touched file
LangChain: langchain_core/runnables/retry.py
Before
_batch() and _abatch() contained copy-pasted retry bookkeeping. Pending
index computation, pending input/config/manager extraction, result processing,
and final reassembly all appeared twice, with the only real differences being
sync versus async iteration and the final dispatch to super().batch() versusawait super().abatch().
Loaded guidance
Loaded packs/universal/shared-abstractions.urf.md andpacks/python-architecture/async-surfaces.urf.md.
Applied:
universal/shared-invariant-extraction(UNI-K24)universal/parameterized-duplicates(UNI-K28)python-architecture/native-sync-async-paths(PY-K13)
Guard rails kept intact:
universal/copy-paste-variants(UNI-X28) was reduced by moving neutral
mechanics into helperspython-architecture/mixed-mode-engines(PY-X13) was not introduced;_batch()and_abatch()remain distinct entry points
After
RunnableRetry now extracts the neutral batch mechanics into four private
helpers:
_get_pending_batch_indices()_get_pending_batch()_process_batch_result()_assemble_batch_results()
_batch() still owns the sync retry loop and super().batch() dispatch._abatch() still owns the async retry loop and await super().abatch()
dispatch. The shared helpers only handle index tracking, pending-subset
assembly, result mapping, and ordered reassembly.
Why better
- shared retry bookkeeping now lives in one place instead of two 50+ line variants
- the sync/async boundary remains explicit, matching
python-architecture/native-sync-async-paths - the extracted helpers make the original-index contract obvious during retries
and final reassembly - the change stayed local to one file and avoided new dependencies or public API
changes
Verification
git diff --stat:libs/core/langchain_core/runnables/retry.py | 164 ++++++++++++++++------------1 file changed, 97 insertions(+), 67 deletions(-)
git diff: one file changed, no public API changes, only private helper
extraction- tests:
4 passed, 117 deselectedvia the repo venv withtest_runnable.py -k retry - linter: no diagnostics for the touched file
What These Examples Demonstrate
- Universal pack guidance is enough to drive real refactors when the problem is
duplicated invariants or duplicated workflow mechanics. - Cross-theme loading should stay rare, but it is justified when a refactor
touches both shared extraction and a real special-case contract, as in the
FastAPI HTTP security case. - Python-specific guidance matters once shared refactoring approaches an async
boundary: the LangChain change improved reuse without collapsing sync and
async into one mixed-mode engine. - Fresh diff stats and project-local tests make the evidence portable and
falsifiable instead of narrative-only.
Reproduction Outline
- Clone or update a target repository under
.repos/. - Load
packs/universal/pack.urf.md, route to the primary relevant universal
leaf, and walk stages S00-S06 before deciding what to act on. - For Python targets, also load
packs/python-architecture/pack.urf.mdand
the primary relevant Python leaf. - If the primary seam is a late-stage refactor (S04-S06), also load at least
one early-stage correctness leaf (S01-S03). If the walked stages reach other
leaves, load the leaves whosestages=cover them and say so in the
write-up. - For a focused change, keep the edit local; the 3-5 budget applies only to
the K/X entries you materially act on after the stage walk, not to the read
set itself. - For an open-ended refactor or review, do a read-only audit pass first,
record findings by stage, then apply and verify them one by one. - Run the target project's own tests from the repo's working environment and
record the exactgit diff --statoutput that produced the example.
FAQ
Common questions
Trust
How it checks out
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.