DEV Community

Casey Li
Casey Li

Posted on

A Free Reviewer Should Comment, Never Block

A Free Reviewer Should Comment, Never Block

The free tier is the right home for a model that only ever produces comments. The moment a required check, a protected-branch rule, or a paging alert depends on it, a paid responsibility has been moved onto capacity that nobody promised to keep running. That failure mode is not a slow review. It is a merge queue with no owner.

That is the argument. The rest of this piece is the field guide for staying on the correct side of it.

What the free tier is, and what that implies

MonkeyCode is described by its maintainers as an open-source project offering free model access and a free server option; the same material advertises a free allowance on the order of ten million tokens. Those are operator-supplied availability claims rather than measured results, and the part of any free tier most likely to move is the quota itself. Check the project's own documentation before sizing a pipeline against a number.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

None of that makes free access a poor choice. It makes it a specific one: inexpensive, genuinely useful, and outside your control. Workloads should be sorted along that single axis before anything else is decided. A process that posts a review comment fits the free tier well. A process that decides whether a merge is allowed does not, because the second one is a gate, and gates are contracts. Contracts need a party that answers when they break.

The split that keeps this honest

The workflow that survives contact with a free tier has two jobs, not one. The advisory job may fail at any time and nobody notices. The gate job is deterministic, boring, and owned by you. They share a repository and nothing else.

The reviewer below reads a diff on stdin, prints comments, and returns zero no matter what happens. That final return is the design, not laziness: an advisory reviewer that can fail a build is a gate wearing the wrong name.

# advisory_review.py - illustrative, not executed for this article.
import json, os, sys, urllib.request

BASE = os.environ['FREE_BASE_URL']   # confirm the current endpoint from the project docs
KEY  = os.environ['FREE_API_KEY']
MAX_DIFF_BYTES = 60_000              # a partial review beats a dead job

def ask(diff):
    payload = {
        'messages': [
            {'role': 'system', 'content':
             'Review the diff. Return one JSON object per line: '
             '{"path": str, "note": str}. No summary, no praise.'},
            {'role': 'user', 'content': diff},
        ],
        'max_tokens': 700,
        'temperature': 0,
    }
    req = urllib.request.Request(
        BASE + '/chat/completions',
        data=json.dumps(payload).encode(),
        headers={'Authorization': 'Bearer ' + KEY,
                 'Content-Type': 'application/json'},
    )
    with urllib.request.urlopen(req, timeout=45) as resp:
        return json.load(resp)['choices'][0]['message']['content'].splitlines()

def main():
    diff = sys.stdin.read()
    if len(diff) > MAX_DIFF_BYTES:
        print('::notice::diff truncated at %d bytes; review is partial'
              % MAX_DIFF_BYTES)
        diff = diff[:MAX_DIFF_BYTES]
    try:
        for line in ask(diff):
            print('::warning::' + line)
    except Exception as exc:
        # The entire point: unavailability is a non-event.
        print('::notice::advisory reviewer unavailable (%s); gate unaffected' % exc)
    return 0

if __name__ == '__main__':
    sys.exit(main())
Enter fullscreen mode Exit fullscreen mode

Two details carry most of the value. The 60 KB truncation is stated out loud, because a silent truncation teaches reviewers to distrust comments they cannot verify. The broad exception handler does not swallow a bug; it converts an outage into a log line, which is the correct weight for a comment.

The workflow then keeps the two concerns physically separate:

# .github/workflows/review.yml - illustrative.
name: review
on: pull_request

jobs:
  advisory:
    runs-on: ubuntu-latest
    continue-on-error: true        # belt
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - env:
          FREE_BASE_URL: ${{ secrets.FREE_BASE_URL }}
          FREE_API_KEY: ${{ secrets.FREE_API_KEY }}
        run: git diff origin/${{ github.base_ref }}...HEAD | python3 advisory_review.py

  gate:
    runs-on: ubuntu-latest         # no shared steps, no shared secrets
    steps:
      - uses: actions/checkout@v4
      - run: make lint test
Enter fullscreen mode Exit fullscreen mode

The gate job never imports the reviewer, never reads its secrets, and never waits on it. That independence is the artifact worth copying. Everything else here is a variation on a single HTTP call.

One weekly measurement keeps the arrangement from drifting:

#!/usr/bin/env bash
# reviewer_drift.sh - illustrative. Summarises the last 100 advisory runs.
set -euo pipefail
runs=$(gh run list --workflow review.yml --limit 100 --json conclusion)
total=$(jq 'length' <<<"$runs")
failed=$(jq '[.[] | select(.conclusion != "success")] | length' <<<"$runs")
awk -v t="$total" -v f="$failed" 'BEGIN {
  printf "advisory runs: %d\nunavailable:   %d\nrate:          %.1f%%\n", t, f, (t ? 100*f/t : 0)
}'
# Policy, not product: above roughly 5% the reviewer becomes an outage you
# keep explaining. Above roughly 20% nobody reads the comments any more, and
# the free tier has stopped working for you while remaining free.
Enter fullscreen mode Exit fullscreen mode

Where the line actually sits

The distinction is not open source against paid. It is whether a failure costs a re-read or a release.

Workload Free tier Why
Advisory comment on a side-project PR Yes A missed comment costs a re-read.
Inline autocomplete for one developer Usually Slow output annoys rather than blocks.
Drafting a changelog line from a merged diff Yes, human-signed A person still owns the wording and the publish.
Required status check on a protected branch No The free tier now holds the merge bit.
Deciding whether to replay a webhook or page a human No Failures arrive as missing work, not as errors.
Inputs already governed by a third-party processing rule Decide first The free path is the one people take by accident.

Where the free tier is wrong, three alternatives are usually better: a deterministic linter, a paid endpoint with a written response time, or a human checklist that takes ten minutes. Each of them can fail loudly, which is more than can be said for a comment bot that quietly stopped posting.

The last two rows of the table are where quiet incidents live. A reviewer that never runs looks like a quiet week. A webhook that never replays looks like a customer who stopped ordering.

Red flags worth acting on immediately

Four signals, in the order they usually appear.

The reviewer starts showing up in an incident channel. Once a comment bot has uptime expectations, it is infrastructure, and free capacity is not offered with uptime expectations attached.

A fallback branch approves when the model is unavailable. Failing open on a gate converts every provider hiccup into an unreviewed merge. Failing closed is defensible; failing open with a green check is not.

Prompts begin to contain production rows, customer identifiers, or a full private export, because the free path was the shortest one. That decision belongs to whoever owns data policy, and it is usually made by accident on a Friday afternoon.

Nobody can reconstruct last Tuesday's reviews from logs. If the advisory job's inputs and outputs are not retained, the pipeline has no history, and no one can say whether the reviewer ever helped.

Exit criteria

Three checkable rules, written so a person can act on them without a meeting.

Promotion requires evidence. After thirty days, count how many advisory comments changed a line of code and how many distinct people acted on them. Meaningful conversion by more than one person justifies dedicated, contracted capacity. It does not justify promoting free capacity into a required check.

Failure rate has a ceiling. Cross five percent unavailability in a week and the reviewer is either noise to accept or a dependency to move. Above twenty percent the comments have stopped being read; keep the job for the archive and stop pretending it informs anything.

Scope has a boundary. The moment the reviewer needs a credential broader than read access to the repository it reviews, the free tier is the wrong host and the design is wrong regardless of host.

Limitations and who should skip this

This approach assumes a repository where a merge can be paused safely. Teams under an audit obligation that requires a named approver per change need a record of who approved what, and an advisory bot produces no such record. Regulated environments should settle the data-processing question before any diff leaves the network. Groups that need a contractual response time cannot get one from a free tier, and should not paper over that by wiring it into a gate.

The approach is also weak on large diffs. Truncation keeps the job alive and makes the review partial by construction, so a monorepo with thousand-line pull requests will see shallow comments. That is an acceptable trade for a comment and an unacceptable one for a gate.

If any of the red flags above look familiar, the free tier is still a reasonable place to measure them for a month before buying anything. Bring it in as a commenter, read the output for thirty days, and let the numbers decide whether it has earned a promotion.

Top comments (0)