The slide had three boxes. Free hosted. Self-hosted. Paid contract. Nobody had written an expiry date under any of them.
Which box is cheaper when the invoice is zero and the review queue is not? That is the question I want a platform lead to answer before the Slack poll starts. Cash is the loud variable. It is rarely the reversing one.
I keep watching 12- to 20-person platform groups treat a free AI coding path as a harmless default. It feels kind. It also quietly rewrites who owns incidents, where source leaves the building, and how long a “pilot” is allowed to live. The decision is not buy versus build. It is whether you will bless a time-boxed exception, or accidentally install a second toolchain with no owner.
This article is a policy gate, not a product review. The scorecard is a conversation tool. It is not objective truth. If one variable below flips, the whole answer can flip with it.
The constraint that reverses the room
Label this a worked example, not a benchmark. Three seniors, extra review on AI-authored diffs, 3.5 hours a week each, loaded cost $175 an hour. That is $1,837.50 a week. Call it $7,350 a month of senior time before anyone pays a seat.
A free path that burns that much review is not free. A paid path that cuts review in half can win on labor even when the invoice looks worse. A self-hosted path that keeps restricted source inside the boundary can win even when both cash and latency look worse. Ask it out loud: if review hours stay high, do you still want the zero-invoice box?
Stop pricing the path in tokens
Tokens are a vendor proxy. Your P&L does not close on tokens. It closes on review hours, incident ownership, data-class breaches, and the cost of leaving.
Define the units before you score anything:
-
R— extra senior review hours per week caused by AI-authored changes -
L— loaded hourly cost of those seniors -
D— data class of the repos in scope (public,internal,restricted) -
W— share of AI output that lands in the normal PR path with tests and CODEOWNERS (0 to 1) -
X— engineer-days required to exit the path (prompts, evals, habit, docs) -
T— calendar days until the exception expires
Monthly labor drag is R * L * 4.3. If that number exceeds the paid-seat or self-host ops line and D is not restricted, money can decide. If D is restricted, money does not get a vote.
Why these six? Because I have seen teams “save” a few hundred dollars a month and spend a senior-week cleaning a chat-only workflow that never became a PR. Cheap tokens. Expensive process.
Where a free path actually belongs
A free hosted coding path is useful when you need a 14- to 45-day exception: no PO, no hardware ticket, enough signal to accept, buy, self-host, or kill. It is a substrate for the gate. It is not the conclusion.
Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode is an open-source project with free model access and a free server option. I am using those two availability claims as a way to run the exception below, not as proof that the free box should win.
If your data class or review budget already fails Gate 1 or Gate 2, skip the free hosted option. Do not “just try it” on restricted source because the invoice is zero.
The 5-gate exception
Run the gates in order. A hard fail stops the process. Scores are for conversation, not for averaging your way into a yes.
Gate 1 — Data class and tenancy (hard)
Can snippets, comments, secrets, and proprietary names leave your boundary?
- Pass: repos are
publicorinternal, and you have a written rule for what may be pasted. - Fail:
restrictedsource, secrets in prompts, or “we will be careful” with no owner.
Free hosted fails closed on restricted source. Self-host or a paid contract with a real data-processing term is the remaining pair. Do not negotiate this gate with a cost model.
Gate 2 — Review-hour budget (hard if R has no owner)
Who absorbs R? If the answer is “the seniors will figure it out,” you already failed.
Write the budget on one line:
R_budget = 2.0 hours/week/senior
R_actual = measured over 10 merged AI-touched PRs
owner = staff engineer on the platform team
If R_actual exceeds R_budget for two consecutive weeks, the exception ends. Not “we should look at it.” Ends.
Gate 3 — Workflow match
Does the output die in a side-channel, or does it hit the same PR template, test gate, and CODEOWNERS file as human work?
Measure W on the last 20 AI-touched changes:
-
W >= 0.8— the path is inside the workflow -
0.5 <= W < 0.8— fix the template before you expand seats -
W < 0.5— you are funding a shadow editor
A free path that cannot survive your existing merge rules is not an adoption win. It is a bypass.
Gate 4 — Incentive alignment
Who gets paged when the generated change fails at 2 a.m.?
If the person who accepted the diff is not on the incident rotation, the free path is paying people to merge lines they will not own. That incentive will beat any scoring rubric. I would rather have a slower paid tool with a named on-call than a fast free tool that orphans failures.
Ask the room: would you still merge this if you were on call tonight?
Gate 5 — Exit cost inside T days
T is 21 days unless you write a shorter number. X must be estimated on day 0, not discovered on day 20.
Count engineer-days to leave:
- Prompt and rule files that only work on this path
- Eval cases that are not in git
- Human habit (the “just paste it here” channel)
- Docs that name the path as default
If X > 3 engineer-days, you are not piloting. You are migrating without a decision. Shrink the surface until X fits inside T.
Worked example (hypothetical 15-person platform team)
Do not treat these numbers as measurements. They are a filled sheet so you can argue with the cells.
| Variable | Free hosted exception | Paid contract | Self-hosted |
|---|---|---|---|
D |
internal services only | internal + one restricted repo via DPA | restricted monorepo |
R hours/week total |
10.5 (3 seniors × 3.5) | 6.0 | 8.0 |
L |
$175 | $175 | $175 |
Monthly labor (R * L * 4.3) |
$7,913 | $4,515 | $6,020 |
| Cash outlay / month | $0 invoice | $1,200 seats (illustrative) | $900 ops (illustrative) |
| Fully loaded month | $7,913 | $5,715 | $6,920 |
W |
0.45 until PR template exists | 0.85 | 0.80 |
X engineer-days |
1.5 if T=21
|
4.0 | 6.0 |
T |
21 days | 12-month term | no expiry unless you write one |
On this sheet the free path loses on labor and on W. Paid wins if D allows it. Self-host wins the moment you move the restricted monorepo into scope, because Gate 1 eliminates the other two.
Change one cell. If those three seniors get R down to 1.0 hour a week each, free hosted labor drops to about $2,258 a month. Then free can beat paid on money — still only if W climbs above 0.8 and D stays internal. That is the point of the sheet. Find the cell that reverses you.
A tiny policy file, not a framework poster
Keep the exception in git so it cannot hide in a doc nobody opens. Proposal only; this is not production policy-as-code.
# exception-ai-coding-path.yaml
id: ai-coding-free-path-2026-09
owner: platform-lead
expires_on: 2026-09-24
scope:
data_class_allowed: [public, internal]
repos_glob: ["services/*", "tools/*"]
repos_denied: ["monorepo-restricted/*"]
budget:
extra_review_hours_per_senior_per_week: 2.0
senior_count: 3
loaded_hourly_usd: 175
gates:
min_workflow_match: 0.8
max_exit_engineer_days: 3
oncall_must_own_merge: true
exit_criteria:
keep: "W >= 0.8 AND R_actual <= budget for 2 weeks AND D unchanged"
buy: "labor+cash of paid < free labor AND D still allowed"
self_host: "any repo in scope becomes restricted"
kill: "any hard-gate fail OR owner missing OR expiry hit"
A conversation calculator you can run locally. Proposal, unexecuted, numbers are yours to replace.
# proposal: exception_score.py — conversation tool, not a verdict
from dataclasses import dataclass
@dataclass
class Path:
name: str
data_class: str # public | internal | restricted
r_hours_per_week: float
loaded_hourly: float
cash_per_month: float
workflow_match: float # 0-1
exit_days: float
oncall_owns: bool
days_until_expiry: int
def monthly_labor(p: Path) -> float:
return p.r_hours_per_week * p.loaded_hourly * 4.3
def hard_fail(p: Path) -> str | None:
if p.data_class == "restricted" and p.name.startswith("free-hosted"):
return "Gate 1: restricted source cannot ride a free hosted path"
if not p.oncall_owns:
return "Gate 4: merger is not on call"
if p.days_until_expiry <= 0:
return "Gate 5: exception expired"
if p.exit_days > 3 and p.name.startswith("free-hosted"):
return "Gate 5: exit cost too high for a pilot"
return None
def fully_loaded(p: Path) -> float:
return monthly_labor(p) + p.cash_per_month
def decide(p: Path) -> str:
fail = hard_fail(p)
if fail:
return f"STOP — {fail}"
if p.workflow_match < 0.8:
return "HOLD — fix PR template before expanding"
return f"CONTINUE — loaded ${fully_loaded(p):.0f}/mo until expiry"
free = Path("free-hosted-exception", "internal", 10.5, 175, 0, 0.45, 1.5, True, 21)
paid = Path("paid-contract", "internal", 6.0, 175, 1200, 0.85, 4.0, True, 365)
host = Path("self-hosted", "restricted", 8.0, 175, 900, 0.80, 6.0, True, 90)
for path in (free, paid, host):
print(path.name, decide(path), f"labor=${monthly_labor(path):.0f}")
On the hypothetical inputs this prints a STOP or HOLD on the free path because W is 0.45, and it keeps self-host alive for restricted source. Change workflow_match to 0.85 and r_hours_per_week to 3.0. Does your call reverse? If it does not, Gate 1 or Gate 4 is doing the real work, and you can stop arguing about invoices.
Inventory the shadow path before you bless a new one
You cannot approve an exception if you do not know what is already in use. Proposal commands only; run them on your own machine, against your own repos.
# proposal: find existing AI-touch markers in git history (last 30 days)
git log --since='30 days ago' --pretty=format:'%h %s' | rg -i 'copilot|codex|llm|generated by|ai-assisted'
# proposal: see whether CODEOWNERS will even see the path
test -f CODEOWNERS && echo 'CODEOWNERS present' || echo 'NO CODEOWNERS — Gate 3 fail'
If those come back noisy, you already have a free path. The 5-gate file is how you pull it out of Slack and into an expiry date.
Hard gates, owner, expiry, exit
Write these on the same page as the scorecard:
- Hard gates: restricted source on a free hosted path; secrets in prompts; no CODEOWNERS; merger not on call; missing owner; missing expiry.
- Owner: one platform lead, named, not a rotation alias.
- Expiry: 21 days from the first merged AI-touched PR, not from the meeting where someone said “sure.”
-
Exit:
keep,buy,self_host, orkill. No fourth option called “leave it for now.”
If expiry hits with no written call, the default is kill. Silence is not consent to keep a toolchain.
Who should not use this
- Teams whose source is regulated or customer-secret. Gate 1 is the article. Stop there.
- Teams with no senior review slack. You cannot budget
Rat zero and hope quality appears. - Individuals looking for an editor tutorial. This is a policy exception for a team that already has a merge path.
- Groups that will not name an owner or an end date. The gates will not save you from a permanent “temporary” tool.
Limitations
I did not measure your review hours. I did not benchmark any model, queue, or box. Loaded cost varies by city and level; $175 is a placeholder so the multiplication is visible. W needs a definition of “AI-touched” that your team will actually tag. If you refuse to tag, the scorecard is theater.
Open-source free paths also move. Treat availability as an input that can disappear, which is another reason T stays short. Do not build a default workflow on an option you have not promised to fund.
What would reverse you?
Not “would you try the free box?” That question invites a shrug. Ask which cell reverses the call.
If D becomes restricted, do you self-host the same week? If R falls under 2 hours a week per senior, does free hosted suddenly win? If X is 5 engineer-days, will you still pretend this is a pilot?
Write the reversing cell in the YAML. Then the Tuesday slide can keep its three boxes. Only one of them gets an expiry date, an owner, and a kill switch. That is the whole job.
Top comments (0)