AI coding cost per accepted change: a local tracking workflow

ai-codingcost-analysisworkflowgitproductivity

TL;DR: Judge an AI coding workflow by the cost of accepted changes, including failed attempts and human review. Keep a local task ledger and use Git to attach evidence; commit counts alone cannot measure productivity. Start with your existing tool and actual invoices before buying another subscription or building a telemetry pipeline.

What you’ll be able to do:

  • Connect a coding task to its starting commit, final commit, and validation result.
  • Calculate cash cost and human effort per accepted task with a small Python script.
  • Compare workflows without claiming that generated lines or agent commits are shipped value.

Honest take: A spreadsheet-sized ledger is enough for a solo developer; measure a bounded set of real tasks before paying for an analytics dashboard.

What counts as an accepted AI coding change?

An accepted change is a task that meets a written acceptance condition and survives your normal review, not merely an agent response that looks plausible. Define that condition before starting: a failing regression test passes, a documented behavior changes, or a reviewer accepts a patch with the required checks.

Keep the unit stable. If one workflow tackles an entire feature and another fixes a single typo, cost per task will say more about scope than tool quality. Group work into categories such as small bug fixes, test additions, documentation, and multi-file refactors. Compare like categories and keep the raw rows available.

Use a status field with clear outcomes: accepted, rejected, or pending. A pending task still consumed effort. A rejected attempt still consumed money. Do not erase either from the ledger just because it makes a preferred tool look worse.

For a task reopened after review, keep the original task identifier and append the extra effort to the same task record. If you decide to count follow-up bugs separately, state that policy and apply it to every tool. Changing the definition halfway through a comparison invalidates the result more effectively than a small rounding error.

Why are commits and lines changed poor productivity scores?

Commits document repository history, but they do not define a completed unit of work. Aider, for example, documents automatic commits for its edits and separate handling of pre-existing dirty changes. One task can therefore produce a sequence of commits before a human accepts the result. See Aider’s Git integration.

The same problem applies to lines changed. A formatter can touch a large part of a repository while delivering little behavioral change; a small conditional can fix an important defect. Use change size to help a reviewer understand scope, not as a multiplier for developer value.

EvidenceWhat it can establishWhat it cannot establish
Start and end commitsWhich repository states were comparedWho supplied every idea
File diffWhat changed between those statesWhether the change meets the requirement
Test resultWhether the executed checks passedWhether untested behavior is correct
Provider invoiceThe provider’s billed amountHow much human work the tool saved
Task ledgerYour recorded scope, effort, and outcomeA causal productivity gain by itself

This distinction matters when choosing a subscription tier. A larger allowance that encourages more unsuccessful retries can produce a lower cost per request while increasing the cost per accepted task. Measure the outcome you want to buy.

How do you capture Git evidence without changing the repository?

Start with read-only commands and save their output in your private task notes. The Git status manual documents porcelain output for a stable machine-readable view of the working tree:

git status --porcelain=v1
git rev-parse HEAD

The first command should return no paths if the tracked and untracked working-tree state is clean. Otherwise, record the existing paths and keep them separate from the task. Do not delete or reset another person’s edits to make a measurement easier.

After the task is committed, record the final commit and inspect the relevant range. Replace START_SHA and END_SHA with the hashes you saved:

git log --format="%h %s" START_SHA..END_SHA
git diff --stat START_SHA END_SHA
git diff --check START_SHA END_SHA

The Git log reference describes commit ranges and formatting. The Git diff reference explains the endpoint comparison, summary statistics, and whitespace checks used here. An empty diff --check result is useful hygiene evidence, not proof that the code works.

If the patch is still uncommitted, the ending commit does not contain it. Record that fact and inspect the working diff separately. If your workflow uses squash merges, attach the final merge commit to the task rather than treating every intermediate agent commit as an accepted deliverable.

For parallel work, keep task branches separate. Combining unrelated changes into one commit makes later attribution harder, even when the combined patch is technically correct. You do not need a new Git policy for the entire team; you need enough evidence to identify what your own comparison included.

Which costs belong in the ledger?

Record cash and time separately. Cash includes the subscription allocation for the period and additional usage charges you can attribute to the tasks. Human effort includes prompting, reviewing, testing, debugging, and repairing the proposed solution. Agent waiting time is a separate observation if you worked on something else during it.

Avoid counting the same usage twice. If a request was included in a prepaid subscription allowance, it is not also an additional invoice charge. If an IDE subscription and a separately billed provider were both required, record both. Use the actual bill or provider usage export; a guessed token rate is not a substitute for billing evidence.

For local inference, distinguish a new purchase from existing equipment. A task that runs on hardware you already own has different incremental cash costs from one that requires a dedicated machine. Electricity and hardware allocation can be added when measured or documented, but do not label them zero because there is no API invoice.

The tracking workflow itself needs Python 3, Git, and local disk space for a CSV. It does not run a model, need a GPU, or require a hosted account. Your coding tool and inference backend retain their own requirements and costs. For that separate choice, see our local Ollama setup for Aider.

How do you calculate cost per accepted change locally?

Create tasks.csv with one row per task. These rows are synthetic arithmetic fixtures, not measured developer results or product prices:

task_id,category,status,variable_usd,human_minutes
demo-1,bugfix,accepted,0.40,20
demo-2,bugfix,rejected,0.60,30
demo-3,bugfix,accepted,0.50,25

Save the following script as summarize_tasks.py. It rejects unknown statuses, duplicate IDs, negative amounts, and pending tasks. That last rule makes you close or explicitly exclude unfinished work before reporting a final result.

import csv
import sys
from decimal import Decimal

def nonnegative(text):
    value = Decimal(text)
    if not value.is_finite() or value < 0:
        raise ValueError("Expected a finite, nonnegative number")
    return value

fixed = nonnegative(sys.argv[2])
with open(sys.argv[1], newline="", encoding="utf-8-sig") as handle:
    rows = list(csv.DictReader(handle))
if not rows:
    raise SystemExit("No tasks to summarize")
seen = set()
accepted = 0
cash = fixed
minutes = Decimal("0")
for row in rows:
    task = row["task_id"].strip()
    if not task or task in seen:
        raise SystemExit("Missing or duplicate task_id")
    seen.add(task)
    status = row["status"].strip()
    if status not in {"accepted", "rejected"}:
        raise SystemExit("Resolve pending or unknown task status first")
    accepted += status == "accepted"
    cash += nonnegative(row["variable_usd"])
    minutes += nonnegative(row["human_minutes"])
print(f"closed_tasks={len(rows)} accepted={accepted}")
print(f"cash_usd={cash:.2f} human_minutes={minutes:.1f}")
if accepted:
    print(f"cash_per_accepted_usd={cash / accepted:.2f}")
    print(f"human_minutes_per_accepted={minutes / accepted:.1f}")
else:
    print("per_accepted=undefined (no accepted tasks)")

The second argument is the fixed subscription cost allocated to this comparison. For the fixture, use an illustrative allocation of $3.00:

python summarize_tasks.py tasks.csv 3.00

Expected output from the supplied fixture:

closed_tasks=3 accepted=2
cash_usd=4.50 human_minutes=75.0
cash_per_accepted_usd=2.25
human_minutes_per_accepted=37.5

The calculation includes the rejected task’s $0.60 and 30 minutes. Dropping that row would answer a different, misleading question: how cheap were only the successes? Keep failures in the numerator while counting accepted outcomes in the denominator.

The script summarizes the file you give it; it does not group categories automatically. Export a separate subset for each task category you want to compare, and allocate fixed costs once across those subsets. Keep a master file so that the subset totals can be reconciled to the original period.

How should you allocate subscriptions and human time?

Choose an allocation rule before looking at the result. If the whole subscription is used for the measured tasks, include the whole period’s charge. If it also serves unrelated work, document an allocation basis and show both the allocated and unallocated totals when the decision is sensitive to that choice.

Do not automatically convert every minute into revenue. A salaried developer finishing earlier does not necessarily reduce payroll, and a freelancer may not have another paid task ready. Time is valuable, but saved time and recovered cash are different outcomes.

If you want an economic scenario, show the assumed hourly value separately:

scenario cost = cash cost + human minutes / 60 * assumed hourly value

Label the hourly value as an assumption. Publish the underlying minutes so another reader can use their own value. That keeps the comparison useful without disguising an opinion about labor value as a measured invoice.

For API-heavy work, reduce avoidable context before declaring a model too expensive. Our Aider context-budget workflow addresses that narrower problem. A cheaper request only helps if the change still passes the same acceptance condition.

How do you keep the comparison fair enough to act on?

Use the same project checks, review standard, and task categories for both workflows. Aider supports configured lint and test commands, as described in its linting and testing documentation. Record the actual command and outcome, whether the assistant ran it or you did.

Avoid replaying the identical bug immediately with another tool and calling the second run faster. You already know the answer, the failing location, and the likely fix. For a practical purchasing decision, collect comparable fresh tasks over a defined period and note changes in task difficulty or developer familiarity.

Keep timestamps for traceability, but record active effort explicitly. Time between two commits can contain lunch, meetings, or overnight waiting. It is not automatically human coding time. Likewise, do not count a background agent’s elapsed runtime as human attention if the developer was productively occupied elsewhere.

Include a short reason for rejection. Wrong behavior, excessive scope, unmaintainable code, and a tool outage suggest different interventions. The cheapest useful response might be a better task brief or a smaller context window, rather than a different subscription.

Honest take: when should this change your tool choice?

Switch tools when the ledger shows a repeatable advantage on work you actually do, with review quality held constant. Keep the existing workflow when the apparent win rests on a few unusually easy tasks or incomplete cost allocation.

This method is a purchasing aid, not an employee ranking system. It cannot establish that one developer is better because they have cheaper tasks, and it does not prove a tool caused a productivity increase. Broader evidence belongs in our analysis of controlled AI coding studies; your ledger answers the narrower question of what your recorded workflow cost.

Stop collecting extra fields when they do not affect a decision. A task ID, category, outcome, cost, effort, and evidence link usually explain more than a dashboard full of generated-token counters. The goal is a subscription decision you can defend, with enough detail to notice when it stops being true.

Sources

Last updated November 2, 2026. Commands were checked against official documentation. Dollar amounts in the CSV are synthetic test inputs, not subscription quotes or measured ROI.

Was this article helpful?

Know which coding tool is worth paying for

Hands-on comparisons of AI coding assistants and what each one costs to run — including the local-model path. Sent only when something changes. Unsubscribe anytime.