DEV Community

Finley Zhou
Finley Zhou

Posted on

Free Tokens, Free Server, and a Property Test That Refuses to Be Gamed

Unit tests tell you what the author expected. Property tests tell you what the universe thinks.

If an AI-generated patch only passes the first, it is not ready. I have spent the last month building a verification loop for agent patches, and the one tool that consistently catches more than unit tests is a property-based harness that runs a few thousand random operations against an invariant.

Here is the workflow I now use: ask a free model via MonkeyCode to write a patch, push that patch to a test harness on a free server, and let the server hammer the code with randomized sequences. This article shows you the exact harness, the freeze rule for flaky failures, and why the free tier is enough for meaningful verification.

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

Why Unit Tests Are Not Enough for Agent Patches

An LLM is extremely good at producing code that matches the shape of the surrounding project. It is much worse at reasoning about global state, ordering, and edge conditions that the test author never wrote down.

A unit test fixes one input and one expected output. The agent can accidentally overfit the test, especially if the test is in the prompt context. The result is a patch that looks correct, passes CI, and then corrupts state in production.

Property tests invert that. They define a valid state and a set of allowed operations. The test generates arbitrary sequences and checks that the state remains valid after every step. There is no single expected output to memorize.

The Harness: A Ring Buffer That Should Never Lie

I used a classic data structure for the experiment: a fixed-capacity ring buffer. The invariants are simple:

  • size() always equals the number of elements successfully pushed minus the number popped.
  • pop() returns elements in FIFO order.
  • Pushing into a full buffer either blocks or returns an error; it never silently loses data.

The agent's task was to implement a thread-safe version. The property test needs to detect corruption even when the unit tests pass.

Here is the minimal C++ property harness. It uses only the standard library, so it runs on any free server without proprietary dependencies.

#include <cassert>
#include <deque>
#include <random>
#include <vector>

struct RingBuffer {
    explicit RingBuffer(size_t capacity) : cap(capacity) {}
    bool push(int v) {
        if (data.size() == cap) return false;
        data.push_back(v);
        return true;
    }
    bool pop(int &out) {
        if (data.empty()) return false;
        out = data.front();
        data.pop_front();
        return true;
    }
    size_t size() const { return data.size(); }
private:
    size_t cap;
    std::deque<int> data;
};

void property_check(size_t cap, size_t steps, uint32_t seed) {
    std::mt19937 rng(seed);
    RingBuffer buffer(cap);
    std::deque<int> model;

    for (size_t i = 0; i < steps; ++i) {
        if (rng() % 2 == 0) {
            int v = static_cast<int>(rng());
            if (buffer.push(v)) model.push_back(v);
        } else {
            int actual = -1, expected = -1;
            bool has_actual = buffer.pop(actual);
            bool has_expected = !model.empty();
            if (has_expected) {
                expected = model.front();
                model.pop_front();
            }
            assert(has_actual == has_expected);
            if (has_actual) assert(actual == expected);
        }
        assert(buffer.size() == model.size());
    }
}

int main() {
    for (uint32_t seed = 1; seed <= 1000; ++seed) {
        property_check(/*cap=*/8, /*steps=*/200, seed);
    }
}
Enter fullscreen mode Exit fullscreen mode

This is not a property-test library. It is a brute-force generator. But that is enough to fail a broken patch in under a second.

The first time I ran this against an agent-generated ring buffer, the failure appeared at seed 17. The unit test passed because it only pushed 8 values and popped 8 values in perfect order. The agent had used a std::vector and rotated the head index with a signed integer. When a pop was attempted on an empty buffer, the head went negative and wrapped around to a corrupt position.

A property test with 1000 random seeds would have caught it at seed 17. That is the difference between a check and a belief.

Why Run It on a Free Server?

You can run 1000 seeds locally in a few milliseconds. So why bother with a server?

Because the patch is not the only moving part. You also want to verify that the code works under different flags, different standard library implementations, and maybe different operating systems. The free server option in MonkeyCode gives you a disposable environment without draining your laptop’s battery or your CI budget.

More importantly, the server is where you run the slow variants. Change the seed count to 100,000 and the steps to 10,000. That takes a few minutes on a free tier. It is still cheap, and it will find state corruption that a 1000-seed run misses.

Here is a concrete loop I use:

  1. Generate a patch from the free model endpoint inside MonkeyCode.
  2. Copy the patch into a fresh project.
  3. Compile with warnings as errors (-Wall -Wextra -Werror).
  4. Run the unit tests. If they pass, move on.
  5. Run this property harness with a high seed count on the free server.
  6. If a seed fails, paste that seed back into the prompt and ask for a fix.

The Freeze Rule: Don't Retry, Record

A flaky test is a piece of code that tells you it is broken. Your first instinct is to retry it. Resist that instinct.

In my workflow, any test that fails once is immediately frozen: it is pinned to a known_flaky list and excluded from normal CI. The property harness, however, is never frozen. If it fails, it must be fixed, because a random seed is a specific input, not a timing artifact.

Why freeze at all? Because agent-generated patches often introduce nondeterminism through uninitialized memory or race conditions. A retry hides the root cause and teaches the agent that flakiness is acceptable. Freezing makes the failure visible and forces a fix.

The property harness is deliberately deterministic. I seed the RNG explicitly, so a failure is reproducible forever. There is no excuse to retry.

Who Should Not Use This Approach

This property harness is not a substitute for a full property-testing library like RapidCheck or Hypothesis. It does not shrink inputs, find minimal counterexamples, or integrate with your build system automatically. If your code is heavily stateful and thread-safe, you need a real framework plus a memory sanitizer.

Also, this workflow assumes the agent's patch is small and scoped. If you are trying to verify a 5000-line rewrite, a simple random fuzzer will not give you confidence. You need formal verification or at least a serious differential testing setup.

The free tier is good for iterating, not for infinite scale. My observation is that 10 million free tokens are enough to generate and revise dozens of patches for a mid-size repository, but they will not cover a high-frequency production pipeline. Treat the free tier as a testing ground, not a permanent runtime.

A Final Word on the Free Server

The most underrated feature of MonkeyCode is not the tokens. It is the free server option that lets you run these deterministic checks in a clean environment. I no longer worry about whether my local machine has the right libraries or whether a stale build is lying to me. The server starts empty, pulls the repo, applies the patch, and runs the harness. If it fails, I know it was the code, not the machine.

If you are reviewing agent patches, do not add more unit tests. Add properties. Then freeze the flaky and let the server verify every seed.

Try it with MonkeyCode's free tier. The worst that can happen is a failing seed you understand.

Top comments (0)