A solo founder opens the inbox before the first coffee.
Two welcome notes name the same new address.
The public waitlist form accepted a double click.
The second database row is not a mystery.
The button fired again before the first write finished.
A bigger cloud invoice will not remove that race.
The cloud bill must stay at zero today.
The patch must land before the next signup wave.
A rented lab machine is the wrong shape of help.
The generated form was a fast win on the surface.
Fast generation skipped the second-click story in full.
The harness puts that story back into the repo.
The first request starts a write and yields the thread.
The second request arrives before that write commits.
Both reads see an empty slot and both insert.
A coat check, not a second coat
Picture a coat check at a crowded door.
The first click parks the coat and takes a stub.
The second click must show that stub and leave.
A free draft lane can still earn its place.
MonkeyCode is presented as an open-source coding assistant here.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
MonkeyCode offers free model access for a narrow coding pass.
It also offers a free server option for that pass.
The free lane drafts the patch and nothing more.
The local harness decides whether that patch is real.
Current terms live on the project page, not in memory.
Quotas, model names, and uptime are not assumed here.
The operator points to a free token allowance as well.
The live figure must be read on that same page.
This draft will not freeze a number that can move.
One folder, one bug
The founder copies one module into a scratch folder.
That folder holds the signup claim and its proof.
No customer export rides along with the prompt.
The prompt names the bug in plain words.
Two requests with one normalized email must yield one row.
The model may suggest code, not production credentials.
A secret key in the prompt is a leaked key.
A real email list is not a safe fixture.
Synthetic names keep the free server boring and safe.
These files are an unexecuted sketch for a scratch repo.
They are not a log from a live customer system.
A local run must happen before any trust is assigned.
Bug: two posts of the same email create two rows.
Constraint: one normalized email may claim only one row.
Do not add network calls. Do not read environment secrets.
Files in scope: waitlist.py only.
The prompt above is a boundary, not a spell.
It keeps the free server on one file and one rule.
A wider paste invites a wider and quieter mistake.
The claim that must win twice
The claim function is short enough to read aloud.
It returns the old row when the email already exists.
It writes once, then reports that the write happened.
def normalize_email(email):
"""Fold case and space so one person keeps one stub."""
cleaned = email.strip().lower()
if "@" not in cleaned or cleaned.startswith("@") or cleaned.endswith("@"):
raise ValueError("email required")
return cleaned
def claim_signup(email, store):
"""Return the existing row when the email was already claimed."""
key = normalize_email(email)
prior = store.get(key)
if prior is not None:
return prior, False
row = {"email": key}
store[key] = row
return row, True
def maybe_send_welcome(row, created, outbox):
"""Append one welcome note only for a new claim."""
if not created:
return False
outbox.append({"to": row["email"], "kind": "welcome"})
return True
The store in this sketch is a plain dict.
A real app should use a unique database constraint.
The dict still teaches the branch the bug forgot.
Lowercasing the mail stops a case-only twin.
The strip call drops a pasted trailing space.
Those two cleanups belong inside the claim itself.
A blank email is a broken ticket, not a guest.
The function raises before a nameless row can land.
That failure is safer than a silent duplicate.
Proof that never dials out
The harness must run on a laptop without network.
A green model reply is not evidence of a fix.
The script below is the only evidence that counts.
from waitlist import claim_signup, maybe_send_welcome
def prove():
store = {}
first, created = claim_signup("Ada@Example.com ", store)
second, again = claim_signup("ada@example.com", store)
assert created is True
assert again is False
assert first["email"] == "ada@example.com"
assert second is first
assert len(store) == 1
outbox = []
assert maybe_send_welcome(first, created, outbox) is True
assert maybe_send_welcome(second, again, outbox) is False
assert len(outbox) == 1
other, other_created = claim_signup("lin@example.com", store)
assert other_created is True
assert other is not first
assert len(store) == 2
try:
claim_signup("not-an-email", store)
except ValueError:
bad_rejected = True
else:
bad_rejected = False
assert bad_rejected is True
print("waitlist claim held")
if __name__ == "__main__":
prove()
A missing import fails before any hosted draft runs.
A red assert fails closed and blocks the merge.
A quiet print line means the double click lost.
The shell steps are dull on purpose today.
The compile step runs, then the proof, then a stop.
Nothing in that chain should open a socket.
python3 -m venv .venv
. .venv/bin/activate
python3 -m py_compile waitlist.py prove_waitlist.py
python3 prove_waitlist.py
Let the free pass draft, then sit down
The founder pastes the function and the failing trace.
The ask stays small and names the second call.
The free server runs that narrow pass, then stops.
MonkeyCode is the drafting bench in this workflow.
It is not the database, the mailer, or the judge.
If the free server is dark, the harness still runs.
A hand-written guard is still a valid ship.
Speed comes from the small scope, not from magic.
The zero bill comes from refusing a new rental.
When the draft returns, the same script runs again.
A changed signature must fail the compile step first.
Only a green local run may touch the real branch.
The index that closes the door
A unique index turns the second insert into an error.
The handler must catch that error and read the winner.
A blind retry will recreate the double welcome note.
CREATE UNIQUE INDEX waitlist_email_key
ON waitlist_signups (email);
The SQL beside the function is short and blunt.
It refuses a second insert with the same email.
The app then reads the row it already stored.
def on_unique_violation(email, store):
row = store.get(normalize_email(email))
if row is None:
raise LookupError("winner missing after conflict")
return row, False
That helper is a sketch of the conflict path.
A real driver raises its own integrity error class.
The handler maps that class and leaves other errors alone.
The founder checks the index before blaming the button.
A missing index lets the app branch lie in comfort.
The database, not the button color, owns the rule.
Mail only on the first claim
Mail should send only when the created flag is true.
That rule stops the second welcome note cold.
The inbox story ends when one address gets one note.
The welcome helper lives beside the claim function.
A false created flag must leave the outbox alone.
The proof asserts a single note after both calls.
sent = maybe_send_welcome(second, False, outbox)
assert sent is False
assert len(outbox) == 1
A failed send should not roll back a stored claim.
The row is the source of truth after the unique write.
Mail can retry later without inserting a twin.
Bad patches that look clever
Three bad patches show up often in this lane.
One patch mints a fresh client key on every click.
Another patch sleeps, then hopes the race has ended.
A third patch catches the click in the browser only.
A fresh client key fails when the second click differs.
A sleep call fails when the network slows down.
A browser lock fails when two devices race apart.
The model may invent a lock the database cannot keep.
The model may skip the unique index and smile.
The assert on store length is the smile detector.
A unique email index fails closed, which is the point.
A green chat bubble cannot overrule that length check.
A note the week can trust
A tiny runner writes the result to a local log.
The log stores the date, the file hash, and the verdict.
That note is the memory when the week gets loud.
import hashlib
import subprocess
import sys
from datetime import date
from pathlib import Path
def file_hash(path):
data = Path(path).read_bytes()
return hashlib.sha256(data).hexdigest()[:12]
def main():
target = "waitlist.py"
digest = file_hash(target)
run = subprocess.run(
[sys.executable, "prove_waitlist.py"],
check=False,
)
verdict = "pass" if run.returncode == 0 else "fail"
line = f"{date.today().isoformat()} {digest} {verdict}\n"
with open("proof.log", "a", encoding="utf-8") as handle:
handle.write(line)
print(line.strip())
return run.returncode
if __name__ == "__main__":
raise SystemExit(main())
python3 -m py_compile note_proof.py
python3 note_proof.py
The hash stops a silent edit after a green run.
If the file changes, the old pass no longer counts.
The proof runs again and a fresh line is appended.
Who should walk past this door
Some days the free lane is the wrong door.
A repo full of payment secrets should stay offline.
A regulated customer file should never board that server.
A founder who needs a support contract needs a vendor.
A team that must audit every prompt needs another path.
This sketch has no legal review and no uptime promise.
A huge monorepo will not fit a short free pass.
The cut continues until one file tells the story.
If that cut fails, the guard is written by hand.
This path fits a solo founder who can read a diff.
It fits a scratch app with synthetic fixtures only.
It does not fit a team that shares live customer data.
The limits are accepted before the first prompt leaves.
Free access can throttle, pause, or change shape.
A dark lane is a cue to type the guard by hand.
Leave the invoice asleep
The production step is still a human merge.
The unique index belongs beside the claim function.
The bill stays quiet because no new machine started.
A reader can copy the harness into a scratch repo today.
The same reader confirms the repository, license, and live terms first.
The proof runs before any generated line is trusted.
Top comments (0)