A free model and a free server are two failure domains. You do not join them just because both bills read zero. Cost is not the same thing as control.
You still own the repo, the secrets, and the merge button. The remote side may draft text or execute a job. Neither remote room should write your main branch.
Think of two rented rooms with a short hallway. One room holds a writer who only returns paper. The other room holds a drill that only runs jobs.
You pass a folder through that hallway only. You do not let the drill edit the original blueprint. Paper can be wrong without moving a single brick.
A running drill can still crack a borrowed floor. Treat each room as a place that can fail alone. Do not let a shared price tag erase that split.
Name the constraints
Start with constraints, not with a tool logo. Your context budget is finite, and today's ceiling is unknown. Your free runner may vanish, throttle, or share a noisy neighbor.
Assume no durable disk on that borrowed runner. Assume no private network that you personally control. Assume no promise that tomorrow will match today.
Model text is untrusted input on your desk. Server logs are untrusted input on your desk too. A green line in a log is a claim, not a merge.
Your main branch accepts a commit only after a local read. You perform that read on your own machine. A remote checkmark does not replace your own read.
You also constrain the path itself before any send. Secrets never enter the hallway between those rooms. Generated files never return home with write rights.
A retry starts from a known local tree. It does not resume a half-written remote folder. Partial state is a failed copy, not a useful clue.
Move one change
Picture one small change, not a whole platform. You edit locally, and then you add a worktree. That experiment cannot touch your current branch.
A filter walks that worktree before any copy leaves. It marks each file local-only, shareable, or review-first. The mark is a latch on the hallway door.
Only shareable text may become a prompt bundle. The bundle is a copy, and the worktree stays source. You can delete the bundle without losing the change.
The writer room returns a single patch file. You store that patch beside the worktree, not inside it. You do not drop it onto your main checkout.
You read the patch before you apply any hunk. An empty patch is a failed draft, so you delete it. A patch aimed at an unknown path is a failed draft.
If the patch looks sane, apply it inside the worktree only. Then you sync that worktree into the runner room. The runner executes the tests and prints plain logs.
Logs come home as files you can store. The runner does not push commits back to you. The runner does not open a review on your behalf.
You read those logs beside the local diff. A pass means the job finished the command you asked. It does not mean the design itself is right.
A failure means you now have a local task. It does not mean you discard the diff and start blind. You widen the next bundle only when you mean to.
That loop is the architecture you can defend. A vendor may sit in either rented room. The rooms stay separate even when one offer covers both.
Run a gate
The script below is a proposal you can run locally. It is not a benchmark, and it is not a product. It only stamps a domain on each path before a copy.
#!/usr/bin/env python3
# Proposal: stamp a domain on each file before a remote copy.
import json
import sys
from pathlib import Path
LOCAL_ONLY = {'.env', '.pem', 'id_rsa', 'credentials.json'}
TEXT_EXT = {'.py', '.md', '.txt', '.json', '.toml', '.yml'}
def kind_of(path: Path) -> str:
if path.name in LOCAL_ONLY or path.name.startswith('.env'):
return 'local-only'
if path.suffix in TEXT_EXT:
return 'shareable-text'
return 'review-first'
def main(root: str) -> int:
base = Path(root)
rows = []
blocked = 0
for path in base.rglob('*'):
if not path.is_file():
continue
if '.git' in path.parts:
continue
kind = kind_of(path)
if kind == 'local-only':
blocked += 1
rows.append({'path': str(path), 'domain': kind})
report = {'root': str(base), 'blocked': blocked, 'files': rows}
json.dump(report, sys.stdout, indent=2)
print()
return 1 if blocked else 0
if __name__ == '__main__':
target = sys.argv[1] if len(sys.argv) > 1 else '.'
sys.exit(main(target))
Run the gate before any sync leaves the desk. A non-zero exit means a denied name was present. Fix that locally, and do not override the exit code.
git worktree add ../draft-room HEAD
python3 boundary.py ../draft-room
echo boundary_exit=$?
Use a toy tree before you touch a real repo. The hidden env file should force a non-zero exit. A zero exit on that tree means the gate is wrong.
mkdir -p /tmp/gate-demo/src
printf '%s\n' 'print(1)' > /tmp/gate-demo/src/app.py
printf '%s\n' 'SECRET=1' > /tmp/gate-demo/.env
python3 boundary.py /tmp/gate-demo
echo expect_nonzero=$?
If the exit is zero, build the bundle from shareable text. Keep that bundle inside a separate scratch directory. Do not point a tool at the raw worktree by habit.
mkdir -p ../scratch/bundle
find ../draft-room -type f -name '*.py' > ../scratch/bundle/manifest.txt
find ../draft-room -type f -name '*.md' >> ../scratch/bundle/manifest.txt
wc -l ../scratch/bundle/manifest.txt
After a reviewed patch lands in the worktree, sync a copy. The host name below is only a placeholder string. Replace it with the runner you actually control today.
rsync -a --delete --exclude '.git' --exclude '.env' \
../draft-room/ runner.example:/tmp/job-184/
ssh runner.example 'cd /tmp/job-184 && python3 -m pytest -q'
Bring logs back as files with a stable name. Do not treat the terminal scrollback as the record. A later reader cannot audit a window you already closed.
ssh runner.example 'cd /tmp/job-184 && python3 -m pytest -q' \
> ../scratch/pytest.log
wc -l ../scratch/pytest.log
git -C ../draft-room rev-parse HEAD > ../scratch/source-rev.txt
Read the failure rooms
The writer room fails when the patch is empty or truncated. It also fails when the patch names a file you withheld. You drop that patch file and keep the recorded revision.
Main never notices that failed draft at all. That silence is the point of the extra worktree. A bad draft should stay cheap to discard.
The hallway fails when the remote copy is only partial. A retry must not continue a broken upload in place. Delete the remote job directory, then sync from the worktree.
If you resume a partial tree, you test a fiction. That fiction can look exactly like a product bug. Often it is only a torn folder on disk.
The runner room fails when the job is killed mid-command. It also fails when the disk vanishes or the clock expires. Missing logs are a failed run, not a quiet pass.
Write that rule where your future self will see it. A vanished machine feels a lot like silence. Silence is easy to misread as a clean success.
Your own desk can fail in the same afternoon. You might apply the patch in the wrong worktree. You might sync an older folder after a newer edit.
Stamp the git revision into the report before the sync. Compare that stamp when the logs finally return. If the revisions differ, throw those logs away.
These domains should fail alone, not as one blob. A writer outage should not erase a saved runner result. A runner outage should not force a wider prompt.
Widen the prompt only after the filter runs again. A second send is a new copy, not a harmless retry. Keep the bundle list attached to that exact send.
Change the stamp next
The next change is a domain stamp on every artifact. You do not need a smarter prompt before that stamp. You need to see which room produced each byte.
Each patch file should record the source revision and bundle hash. Label that file with the plain word remote-draft. Do not put a guessed model name inside the stamp.
Each log file should record the job id and same revision. Label that file with the plain word remote-run. You can then match paper to drill without a dashboard.
Make the remote job id idempotent on purpose. A second sync of the same revision should replace the folder. It should not nest another copy inside the first one.
Double trees create failures that look like flaky tests. Those failures are usually two copies of one job. Replace the folder, and the false flake disappears.
Store the stamp beside the commit message when you merge. Future you should see which room produced the diff. Future you should also see which room produced the log.
That provenance stays on your desk with the commit. It does not require trust in either rented room. It only requires that you wrote the stamp before you forgot.
Next, teach the filter to scan file contents for secrets. The script above trusts file names and suffixes only. A token pasted into a Python string will sail through.
Until that scan exists, read the bundle yourself. The name check is a latch, not a real lock. A clean exit lets you look, not send by reflex.
Where a free offer fits
Disclosure: This article was prepared as part of MonkeyCode's product outreach. The operator describes MonkeyCode as an open-source project. It includes free model access and a free server option.
This draft does not verify a token quota or machine size. It also does not verify a region or a duration. Those terms move, so read the current project page first.
Confirm the repository and the license on that page. A remembered offer is not a standing contract. Design the rooms before you design around a price.
If you try that offer, keep it inside the rooms above. Point the writer at the bundle, not at your home directory. Point the runner at the job path, not at production.
Then keep the stamps next to the result. The offer itself is only a supply choice. The boundary is the part you should keep.
Confirm the live terms, then clone a toy repo. Run the boundary script before either room sees real work. Let the gate feel boring before you trust the hallway.
Skip this when
Skip this flow when the repo holds regulated data. Also skip it when you lack approval to send filtered text. A name filter is not a legal review.
Skip this flow when you need a production runtime. A free shared runner is a lab bench, not a shop floor. Do not hang a customer path on a borrowed bench.
Skip this flow when your team requires signed builds. Locked runners and a real audit trail belong in your pipeline. One local JSON file is not that audit trail.
Skip this flow when you wanted a public scoreboard. There is no accuracy number in this article. There is no speed claim and no tool ranking either.
The artifact is a gate plus a path. It shows where bytes are allowed to travel. It does not show which writer is best.
Limits you should keep
The classifier trusts suffixes and file names alone. Renamed secret files can still sail through the gate. Binary assets land in review-first, and the script never opens them.
You can still sync a forbidden file if you bypass the exit. The gate is voluntary on a solo desk. A voluntary gate fails when you treat it as décor.
The sample commands assume SSH and rsync exist locally. Those commands do not create the remote runner. They do not prove the remote disk started empty.
A dirty job directory is its own failure domain. Prefer a fresh path for every new job id. Delete that path after the logs are safely home.
Nothing here measures how often a draft compiles. If you need that number, count it on your own tree. Do not borrow a figure from a marketing page.
Do not freeze yesterday's quota into a design document. Quotas change, and this article did not measure one. Design for a missing room, not for a permanent gift.
You can change the stamp format without moving the rooms. Keep the writer from running code on your behalf. Keep the runner from opening reviews in your name.
Keep the merge decision on your own desk. That split survives a price change and a model swap. It also survives a week when the free box is gone.
Top comments (0)