Most free-tier spikes fail on drift, not on model quality.
The agent wrote a correct patch. The host could not run it.
That is an environment bug. You can find it in 90 minutes.
The rule: fingerprint both sides before you prompt anything.
One hypothesis, written before the timer starts
A spike without a falsifiable sentence is a demo. Demos always pass.
Write exactly one line:
A patch produced on the free server passes the same five-command contract test as local CI.
Commit that line to a file in your repo. Now you cannot move the goalposts later.
Why drift is the interesting failure
Model quality shows up as a bad diff. You see it in review.
Drift shows up as a bad host. You see it 40 minutes later, as EACCES.
Drift is cheaper to measure and more expensive to ignore.
Pre-register the thresholds
Write these numbers down before you touch the server. Fill the observed column last.
| Metric | Pre-registered threshold | Observed |
|---|---|---|
| Time to first green contract test | ≤ 25 min | |
| Fingerprint fields that differ | ≤ 2 | |
| Repair time per drift field | ≤ 10 min | |
| Files touched by environment fixes | ≤ 3 | |
| Review cost vs local baseline | ≤ 1x |
Two rules keep this honest.
- Thresholds are written first, in the same commit as the hypothesis.
- Any change to a threshold is a new spike on a new day.
The 90-minute budget
| Minutes | Activity | Output |
|---|---|---|
| 0–5 | Hypothesis and thresholds | spike/ledger.json committed |
| 5–20 | Fingerprint local | local.json |
| 20–40 | Fingerprint server, diff | drift.diff |
| 40–65 | One real patch, timed | timing.txt |
| 65–80 | Fill the ledger | Facts, no opinions |
| 80–90 | Verdict | ship or kill |
When the timer ends, the spike ends. A partial ledger is still a result.
Step 1: fingerprint both environments
The script below is a template. I have not run it against your machines.
#!/usr/bin/env bash # drift.sh - capture an environment fingerprint set -euo pipefail OUT="${1:-fingerprint.json}" row() { printf ' "%s": "%s",\n' "$1" "$2"; } { printf '{\n' row os "$(uname -srm | tr '"' "'")" row node "$(node -v 2>/dev/null || echo missing)" row python "$(python3 -V 2>&1 | awk '{print $2}' || echo missing)" row lock "$(sha256sum package-lock.json 2>/dev/null | cut -c1-16 || echo none)" row tz "$(cat /etc/timezone 2>/dev/null || date +%Z)" row locale "${LANG:-unset}" row cores "$(nproc 2>/dev/null || echo unknown)" row mem_mb "$(awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo 2>/dev/null || echo unknown)" printf ' "_end": "ok"\n}\n' } > "$OUT" echo "wrote $OUT" Run it on both hosts. Then diff the JSON, not the vibes.
./drift.sh local.json ssh <free-server> 'bash -s' < drift.sh > server.json diff <(jq -S 'del(._end)' local.json) <(jq -S 'del(._end)' server.json) | tee drift.diff A thirteen-line diff is a finding. A missing node is a kill signal.
Step 2: one contract test, five commands, both hosts
Use the same commands, in the same order, on both machines.
set -e npm ci --no-audit --no-fund npm run lint npm test -- --runInBand git status --porcelain # must print nothing node -e 'console.log(process.version)' Do not add || true. Do not skip npm ci.
The last two commands catch the quiet failures. Dirty checkouts and version drift.
Step 3: one patch, timed, not five
Pick a real ticket from your backlog. Toy functions hide environment bugs.
/usr/bin/time -f '%e s wall, %M KB peak' \ npm test -- --runInBand 2> timing.txt cat timing.txt Record three numbers: time to first run, time to first green run, time to review.
Review time is the one people forget. Ask a colleague to read the diff cold.
The ledger
{ "hypothesis": "A patch produced on the free server passes the local five-command contract test.", "thresholds": { "green_minutes": 25, "max_drift_fields": 2, "max_repair_minutes_per_field": 10 }, "observed": { "green_minutes": null, "drift_fields": null, "repair_minutes_total": null, "review_minutes": null }, "verdict": null, "next_review_date": "2026-12-07" } Nulls are permitted entries. Guesses are not.
How to read the verdict
| Drift fields | Repair time | Verdict |
|---|---|---|
| 0 | any | ship — server becomes primary |
| 1–2 | ≤ 10 min each | ship with pins — codify fixes |
| 1–2 | > 10 min each | defer — pin first, re-spike |
| 3+ | any | kill — do not chase it |
| any | green > 25 min | kill — the loop is too slow |
defer is not a soft ship. It has a date and an owner, or it is a kill.
Where the free tier fits this worksheet
MonkeyCode is presented by its maintainers as an open-source coding-agent project.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
Two operator-supplied availability claims matter for this spike. Free model access and a free server option.
The quota is reported as roughly 10 million free tokens at the time of writing (2026-11-07). Treat that as a moving number, not a contract, and check the project page before you plan around it. Free allowances change.
Why this fits the drift test: the test needs a second host. A second host is the expensive part of the experiment.
Why free models are sufficient here: you grade the patch with npm test, not with prose. The contract test is the judge.
One practical note. Record the model name and the quota snapshot in your ledger. A spike with an unrecorded variable is a memory.
If you want a second host for this worksheet, run the drift step on MonkeyCode's free server and keep the ledger in your repo either way.
Limitations
- Availability and quotas can change without notice. Re-run the spike monthly.
- A free host is not your production host. Passing drift does not prove production parity.
- Fingerprints cover language runtimes and lockfiles, not kernel behavior or syscalls.
- Single-patch timings are noisy. Treat one run as one data point, not a benchmark.
- Secrets handling on any shared host needs review before you paste a token anywhere.
- Nothing here proves a model is good. It only proves a host is compatible.
Who should not use this approach
- Teams handling regulated or customer data on a host they have not reviewed.
- Repos with native toolchains, GPU builds, or long compile steps.
- Monorepos where
npm cialone exceeds ten minutes. - Anyone who will not re-run the spike when the free terms change.
For those teams, a pinned devcontainer is the cheaper answer.
What a kill verdict buys you
You spent 90 minutes. You avoided a month of intermittent CI failures.
Keep the fingerprint script. It is reusable on every new host.
Pin the drift fields in a devcontainer and re-spike next month.
Short spikes, written thresholds, recorded verdicts. That beats a lucky demo.


