The founder copied a working script at 12:10 a.m. On the laptop the waitlist form returned 201. On a blank box the same script died immediately.
A leftover virtualenv had hidden the real breakage. A local secret file had fed the client. The house was overpacked after six loud weeks.
Indie week looks like this more often than decks admit. The solo shop still ships from one crowded machine. That crowded machine already knows far too much. Env files, cached wheels, and yesterday's model output all sit nearby.
Paid clouds make the same lie more expensive. A GPU bill does not prove the patch is portable. It only proves the founder can rent a louder hotel.
The useful move is much smaller than that. Draft the patch at home without extra services. Unpack the same bag in an empty apartment. Merge the patch only when both rooms agree.
This article treats that empty apartment as a workflow. The code below is an example, not a production run. No named model is required for the ritual. No token quota is claimed in these pages.
An empty apartment still needs three cheap rules. The repo must boot from a lockfile. Tests must not read the founder's home directory. The model may draft, but tests own the merge.
Some indie shops point the draft step at MonkeyCode. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The project is offered as an open source codebase. It offers free model access and a free server option. Those two availability claims come from the operator. This write-up does not add model names, hardware, or lasting quotas.
The rest of the method still works if that product is removed. A clean clone and a strict test gate carry the weight.
The Friday scene continues without a new invoice. The founder needed a waitlist endpoint before Monday. No invoice was allowed to follow the patch. The form had to survive a stranger's shell.
The envelope lives under tools/suitcase/ on purpose. Boring tools survive a noisy launch week intact.
# tools/suitcase/contract_test.py
# Example only. Not executed for this article.
from http.server import BaseHTTPRequestHandler, HTTPServer
import json, threading, unittest, urllib.error, urllib.request
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
if self.path != "/waitlist":
self.send_error(404)
return
n = int(self.headers.get("Content-Length", "0"))
body = json.loads(self.rfile.read(n) or b"{}")
email = body.get("email", "")
domain = email.split("@")[-1] if "@" in email else ""
if "@" not in email or "." not in domain:
self.send_response(400)
self.end_headers()
return
self.send_response(201)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps({"ok": True}).encode())
def log_message(self, *args):
return
class WaitlistContract(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.httpd = HTTPServer(("127.0.0.1", 0), Handler)
cls.port = cls.httpd.server_address[1]
t = threading.Thread(target=cls.httpd.serve_forever, daemon=True)
t.start()
def post(self, payload):
req = urllib.request.Request(
f"http://127.0.0.1:{self.port}/waitlist",
data=json.dumps(payload).encode(),
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=2) as r:
return r.status, json.loads(r.read())
except urllib.error.HTTPError as e:
return e.code, {}
def test_accepts_plain_email(self):
status, body = self.post({"email": "ava@example.com"})
self.assertEqual(status, 201)
self.assertTrue(body.get("ok"))
def test_rejects_garbage(self):
status, _ = self.post({"email": "not-an-email"})
self.assertEqual(status, 400)
if __name__ == "__main__":
unittest.main()
That file is the packing list on the counter. The model does not get to choose the list. The founder writes the contract before any draft. The patch must match that list or leave.
The prompt stays short for a hard reason. A long prompt invites a long unpaid night. Indie time is the real scarce resource here.
# tools/suitcase/prompt.txt
# Example prompt. Keep it short.
Add POST /waitlist to app.py.
Read only app.py and tools/suitcase/contract_test.py.
Do not touch secrets, CI, or package managers.
Return a unified diff and nothing else.
Reject the email when it lacks @ or a dot in the domain.
Return 201 and {"ok": true} for a normal address.
A wrapper then treats the model like a draftsman. The draftsman gets a time card and a fence. The script below is an unexecuted sketch only. Wire draft_cmd to a free CLI the shop already runs.
#!/usr/bin/env bash
# tools/suitcase/draft.sh
# Example only. Unexecuted.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
cd "$ROOT"
git diff --quiet && git diff --cached --quiet
test -z "$(git ls-files --others --exclude-standard)"
if python3 tools/suitcase/contract_test.py; then
echo "contract already green; refuse to draft"
exit 2
fi
mkdir -p .suitcase
cp tools/suitcase/prompt.txt .suitcase/prompt.txt
# draft_cmd is local to the shop.
# Example: a free-model CLI that prints a unified diff.
draft_cmd --prompt-file .suitcase/prompt.txt > .suitcase/patch.diff
grep -q '^diff --git ' .suitcase/patch.diff
grep -qv 'BEGIN RSA' .suitcase/patch.diff
grep -qv 'SECRET' .suitcase/patch.diff
git apply --check .suitcase/patch.diff
git apply .suitcase/patch.diff
python3 tools/suitcase/contract_test.py
Home success is still not enough for tonight. The laptop knows the founder's PATH too well. It knows a global package cache by heart. It knows a dotenv file a stranger will never have.
The empty apartment is a clean clone on purpose. A free server option is enough for this check. The founder does not need a GPU for a waitlist form.
#!/usr/bin/env bash
# tools/suitcase/unpack.sh
# Example only. Unexecuted.
set -euo pipefail
HOST="${SUITCASE_HOST:?set SUITCASE_HOST}"
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
cd "$ROOT"
test -z "$(git status --porcelain)"
SHA="$(git rev-parse HEAD)"
ssh "$HOST" 'test -d ~/suitcase.git || git init --bare ~/suitcase.git'
git push --force "ssh://$HOST/~/suitcase.git" HEAD:launch
ssh "$HOST" bash -s <<EOF
set -euo pipefail
rm -rf "\$HOME/suitcase_work"
git clone "\$HOME/suitcase.git" "\$HOME/suitcase_work"
cd "\$HOME/suitcase_work"
git checkout $SHA
test ! -f .env
test ! -d venv
python3 -m venv .clean
. .clean/bin/activate
pip install -q -r requirements.txt
python3 tools/suitcase/contract_test.py
EOF
The remote script is the point of the night. It refuses a leftover dotenv file on sight. It builds a throwaway venv from the lockfile. It runs the same contract the laptop just ran. If this step fails, the laptop was lying.
A small ignore file keeps the lie from traveling.
# extra lines for .gitignore
.suitcase/
.clean/
.env
.env.*
venv/
.venv/
The founder then records both rooms in the commit. Future self will need that breadcrumb much later. Launch week forgets why a patch felt safe.
Waitlist POST returns 201 for plain email.
Home unpack: python3 tools/suitcase/contract_test.py
Empty apartment: unpack.sh on the free box
Model drafted the diff. Tests owned the merge.
Limits sit beside the ritual, not after it. A free model lane can sit in a queue. A free server option can throttle or pause. This article does not freeze those terms for anyone. Public caps can change without a matching blog post. Remote queues also change without a warning banner. Treat both as a spare room, not a lease.
The method also fails closed in honest ways. It does not replace a human reading the diff. It does not prove load around the form. It does not prove XSS, CSRF, or inbox spam. It proves only that clean Python can serve the contract.
Secrets do not belong inside the prompt file. Secrets do not belong on the borrowed box either. The remote script assumes a throwaway waitlist service. It does not assume a customer database nearby. Copying production dumps onto a free server is a bad story.
Teams with a real staging cluster already have an empty apartment. Regulated shops cannot send diffs to a shared free model. Founders who need GPU training are in another building. Anyone who lets the model merge without tests skipped the method.
The Monday check is a clone on a friend's laptop. Run the contract with no whispered environment variables. If the bag needs a private garnish, it was never packed. The empty apartment told the truth before any invoice.
Indie shipping is not a bigger agent loop. Extra tool hops do not create a teammate. The founder already knows the actual merge gate. Tests go red, a draft appears, tests go green. The clean room goes green, then the merge happens.
A suitcase test keeps that merge gate honest. The bill stays at zero for this class of patch. The limits stay in the room where they belong. The merge stays with the founder, not the draft.
Top comments (0)