A team shipped a summarizer on a free model lane. It lived in one script, behind one wrapper function.
Two sprints later, the same wrapper appeared inside a webhook handler. Nobody decided that. The import was convenient, the tests were green, and the review diff was four lines.
That is the failure mode worth designing against. Adoption rarely happens in one decision. It happens in dozens of small ones.
The wrapper is not the problem. The call sites are.
A shared client module looks like good hygiene. It centralizes configuration and keeps vendor code out of feature files. It also makes every call site look identical in review.
A summarizer for internal docs and a signature-verifying webhook now share one import line. The audit question is not "is this module safe." The question is "which call sites may reach it."
Free model access and a free server option lower the cost of the first call site. They do not lower the cost of the tenth. MonkeyCode exposes both, and both are operator-supplied availability claims rather than guarantees of duration or capacity. Check current limits at the source before planning around them.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
What the audit actually checks
The audit answers three questions per file.
- Does this file import the lane wrapper directly?
- Which enclosing function makes each lane call?
- Does a policy rule allow, review, or block that path-and-function pair?
It is deliberately static. Runtime tracing misses the call site that only fires on a rare branch. Static analysis sees it even when the branch never runs in tests.
Artifact one: a stdlib-only call-site classifier
Save this as tools/blast_radius.py. It uses only ast, fnmatch, json, and pathlib, so it runs anywhere Python runs.
#!/usr/bin/env python3
"""Classify call sites that touch a free-lane wrapper. Exit 1 on blocked hits."""
import ast
import fnmatch
import json
import sys
from pathlib import Path
LANE_MODULES = {"monkeycode_lane"} # your wrapper module, adjust
LANE_CALLS = {"generate", "chat", "complete", "run_job"}
def load_rules(path: str) -> list[dict]:
return json.loads(Path(path).read_text(encoding="utf-8"))["rules"]
def classify(rules, rel_path: str, func: str | None):
key = f"{rel_path}::{func or '<module>'}"
for rule in rules:
if fnmatch.fnmatch(key, rule["match"]) or fnmatch.fnmatch(rel_path, rule["match"]):
return rule["class"], rule.get("reason", "")
return "review", "no rule matched"
class Scanner(ast.NodeVisitor):
def __init__(self, rel_path: str):
self.rel_path = rel_path
self.stack: list[str] = []
self.hits: list[dict] = []
def visit_Import(self, node):
self.generic_visit(node)
def visit_ImportFrom(self, node):
self.generic_visit(node)
def _visit_func(self, node):
self.stack.append(node.name)
self.generic_visit(node)
self.stack.pop()
visit_FunctionDef = _visit_func
visit_AsyncFunctionDef = _visit_func
def visit_Call(self, node):
func = node.func
name = func.attr if isinstance(func, ast.Attribute) else getattr(func, "id", None)
if name in LANE_CALLS:
self.hits.append({
"path": self.rel_path,
"func": self.stack[-1] if self.stack else None,
"line": node.lineno,
"call": name,
})
self.generic_visit(node)
def main(argv: list[str]) -> int:
root = Path(argv[1] if len(argv) > 1 else ".")
rules = load_rules(argv[2] if len(argv) > 2 else "tools/lane_policy.json")
findings, blocked = [], 0
for path in sorted(root.rglob("*.py")):
rel = path.relative_to(root).as_posix()
try:
tree = ast.parse(path.read_text(encoding="utf-8"))
except SyntaxError as exc:
print(f"SKIP {rel}: {exc}", file=sys.stderr)
continue
scanner = Scanner(rel)
scanner.visit(tree)
for hit in scanner.hits:
cls, reason = classify(rules, hit["path"], hit["func"])
hit["class"], hit["reason"] = cls, reason
findings.append(hit)
blocked += cls == "blocked"
width = max((len(f["path"]) for f in findings), default=4)
for f in findings:
print(f'{f["class"]:<8} {f["path"]:<{width}}:{f["line"]:<4} '
f'{f["func"] or "<module>"} # {f["reason"]}')
print(f"\n{len(findings)} call sites, {blocked} blocked")
return 1 if blocked else 0
if __name__ == "__main__":
sys.exit(main(sys.argv))
Artifact two: the policy file that carries the judgment
The script encodes mechanics. The policy encodes intent, and intent belongs in review.
{
"rules": [
{"match": "app/webhooks/*::*", "class": "blocked", "reason": "signed path"},
{"match": "app/payments/*::*", "class": "blocked", "reason": "money path"},
{"match": "app/auth/*::*", "class": "blocked", "reason": "identity path"},
{"match": "app/jobs/*::enrich", "class": "review", "reason": "needs fallback"},
{"match": "scripts/*", "class": "allowed", "reason": "local only"},
{"match": "tests/*", "class": "allowed", "reason": "fixtures"}
]
}
Unmatched call sites default to review. Fail-open on classification, fail-closed on blocked hits. That default keeps the rule list honest instead of silently permitting new paths.
Wire it into the pull request gate
name: lane-audit
on: [pull_request]
jobs:
blast-radius:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: python tools/blast_radius.py . tools/lane_policy.json
A blocked finding fails the job. The author either moves the call or changes the policy in the same diff. Both outcomes are visible decisions, which is the entire point.
Artifact three: prove the critical paths survive removal
Static classification shows where the lane is called. A stubbed lane shows whether anything breaks without it.
# tests/conftest.py
import sys
import types
import pytest
@pytest.fixture
def no_free_lane(monkeypatch):
stub = types.ModuleType("monkeycode_lane")
def _raise(*args, **kwargs):
raise RuntimeError("free lane reached from a critical path")
stub.generate = _raise
stub.chat = _raise
stub.complete = _raise
monkeypatch.setitem(sys.modules, "monkeycode_lane", stub)
return stub
Then apply it selectively.
# tests/test_checkout.py
def test_checkout_survives_without_lane(no_free_lane, client):
response = client.post("/checkout", json={"cart": [1, 2]})
assert response.status_code == 200
If that test raises the stub error, a blocked path reached the lane. No log scraping required.
Apply the same test to the free server option
The server question is narrower: can the workload be rebuilt from the repository alone?
Write one command that rebuilds from an empty host. Run it on a second machine. Record what the command silently needed.
Common findings are boring and decisive. A hand-installed package. A config file living only on the first host. A cron entry added by shell history.
Any workload that cannot rebuild from the repo is a workload that should not sit on a server whose lifecycle you do not control.
The decision table
| Class | Typical signals | Default action |
|---|---|---|
| blocked | signature verification, payments, auth, audit writes, regulated data | never import the lane; CI fails on a hit |
| review | user-visible summaries, background enrichment, retries | needs a named owner and a documented fallback |
| allowed | local scripts, fixtures, doc drafts, one-off analysis | no rule required |
Where this audit breaks
The classifier reads names, not types. A method called complete on an unrelated object is a false positive.
Dynamic dispatch hides call sites. Factories, dependency injection containers, and getattr will slip past the AST.
Policy drift is real. A stale rule list quietly blocks legitimate work, and people start weakening patterns instead of reviewing them.
The audit says nothing about cost, latency, or quality. Those need separate measurements.
Who should skip this
A single service with one obvious call site does not need a scanner. Read the file.
Teams under a compliance regime that excludes free tiers entirely should not audit first. They should exclude first, then audit the exceptions.
Repositories with heavy metaprogramming will produce noise that outweighs the signal. A runtime allowlist may fit better there.
Exit criteria for the lane itself
- The wrapper module is the only import path; no vendor SDK appears elsewhere.
- Every blocked-class path passes with the lane stubbed out.
- The rebuild command reproduces the free server workload from the repo alone.
- Each review-class call site has a named owner and a fallback flag.
- Removing the lane module produces a diff of known, intended failures only.
Adopting free model access and a free server is a per-call-site decision, not a repo-wide one. Start with the wrapper module, run the classifier once, and let the blocked list tell you what the free tier is actually for.
Top comments (0)