You open two take-homes on Sunday night. Same PDF. Same failing test in the zip. One repo grew a Helm chart, a kind cluster, and a README that starts with just k3d up. The other is a 40-line patch and a terminal log. The first submission looks senior. The second one actually fits the box you run in production.
That is the quiet failure of AI-era take-homes. You equalized the prompt. You did not equalize the machine. One candidate ran a paid model on a quiet workstation. The other wrote on a laptop in a cafe with a flaky VPN. You cannot grade those loops against each other, because you never issued the same constraints.
Stop asking whether they used a model. Assume they did. The interview question is whether they can ship a small, inspectable change inside a workspace you can replay. If the environment is private, the replay is a story. If the environment is shared, the replay is evidence.
The packet, not the essay
You are hiring someone who will touch a file-backed downloader that already exists. You are not hiring a platform team for a weekend. The take-home below is meant to be completed in a shared editor on a shared server, with a model allowed and extra infrastructure forbidden. Think of it as giving every pianist the same upright, then listening for the piece, not for who owns a concert grand.
The workspace you hand out can be modest. A free server option and free model access are enough, because the point is sameness, not luxury. Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode is one place you can issue that shared box without asking a candidate to paste a card; the packet still works if you host the same contract on your own VM. Grade the diff against the contract. Do not grade the brand of the editor.
Here is the brief you paste into the workspace README. Label it as a take-home, not as production onboarding.
TAKE-HOME: resume a partial download without inventing a platform
You get a directory named /work. It already contains fetch.py,
test_fetch.py, and submit_check.py. You may use the model in this
workspace. You may not add services, containers, queues, or cloud
accounts.
Fix fetch.py so that a mid-file crash can resume.
Rules:
- Keep bytes on disk as /work/data.bin.part until the file is complete.
- A 206 response must append at the offset you asked for.
- A 200 response that ignores your Range header is not a resume.
Start over only after truncating the partial file.
- Do not follow redirects.
- Do not shell out. Do not open URLs except the one in the test.
- When you are done, run: python submit_check.py && pytest -q
Submit: the patch, the pytest output, and commands.log from this box.
Anything that requires another machine fails the packet.
The brief is short on purpose. Long briefs invite the model to architect. Short briefs invite the model to guess. Your job is to make guessing expensive by putting the real constraint in tests, not in prose.
A small failing client
Drop this into fetch.py as the starter. It looks careful. It is not. It trusts Content-Length, it treats every 200 as success, and it never writes a partial file that a second process can resume.
# fetch.py — starter, intentionally wrong
from __future__ import annotations
from pathlib import Path
import urllib.error
import urllib.request
DEST = Path("/work/data.bin")
PART = Path("/work/data.bin.part")
class ResumeError(RuntimeError):
pass
def fetch(url: str) -> Path:
# Looks resumable. It is not. A crash leaves DEST half-written
# and the next call downloads from byte zero on top of it.
req = urllib.request.Request(url, method="GET")
with urllib.request.urlopen(req, timeout=10) as resp:
if resp.status not in (200, 206):
raise ResumeError(f"unexpected status {resp.status}")
body = resp.read()
DEST.write_bytes(body)
return DEST
The test file is the real prompt. Candidates who only chat with the model will rewrite fetch.py into a framework. Candidates who read the test will notice you care about three states: empty disk, partial disk, complete disk.
# test_fetch.py
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path
import threading
import pytest
import fetch
PAYLOAD = b"ABCDEFGHIJ" # 10 bytes, easy to split
class RangeHandler(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
def do_GET(self):
range_hdr = self.headers.get("Range")
if range_hdr == "bytes=4-":
chunk = PAYLOAD[4:]
self.send_response(206)
self.send_header("Content-Range", "bytes 4-9/10")
self.send_header("Content-Length", str(len(chunk)))
self.end_headers()
self.wfile.write(chunk)
return
# Deliberate trap: ignore Range and send a full 200.
self.send_response(200)
self.send_header("Content-Length", str(len(PAYLOAD)))
self.end_headers()
self.wfile.write(PAYLOAD)
def log_message(self, fmt, *args):
return
@pytest.fixture
def server():
httpd = HTTPServer(("127.0.0.1", 0), RangeHandler)
t = threading.Thread(target=httpd.serve_forever, daemon=True)
t.start()
yield f"http://127.0.0.1:{httpd.server_address[1]}/file"
httpd.shutdown()
def test_full_download_when_nothing_on_disk(server, tmp_path, monkeypatch):
monkeypatch.setattr(fetch, "DEST", tmp_path / "data.bin")
monkeypatch.setattr(fetch, "PART", tmp_path / "data.bin.part")
path = fetch.fetch(server)
assert path.read_bytes() == PAYLOAD
assert not fetch.PART.exists()
def test_resume_from_partial(server, tmp_path, monkeypatch):
monkeypatch.setattr(fetch, "DEST", tmp_path / "data.bin")
monkeypatch.setattr(fetch, "PART", tmp_path / "data.bin.part")
fetch.PART.write_bytes(PAYLOAD[:4])
path = fetch.fetch(server)
assert path.read_bytes() == PAYLOAD
assert not fetch.PART.exists()
def test_full_200_does_not_append_garbage(server, tmp_path, monkeypatch):
# Handler returns 200 + full body even if Range was sent.
# Appending would duplicate bytes. Truncate, then write.
monkeypatch.setattr(fetch, "DEST", tmp_path / "data.bin")
monkeypatch.setattr(fetch, "PART", tmp_path / "data.bin.part")
fetch.PART.write_bytes(b"XXXX")
# Force the client to send Range; the server will ignore it.
path = fetch.fetch(server)
assert path.read_bytes() == PAYLOAD
Then add a gate that fails the packet if they escaped the box. This is not security theater. It is a fairness check. If they needed Redis to move ten bytes, they did not take the assignment you wrote.
# submit_check.py
from pathlib import Path
import sys
ROOT = Path("/work")
ALLOWED = {
"fetch.py",
"test_fetch.py",
"submit_check.py",
"commands.log",
"README.md",
"data.bin",
"data.bin.part",
}
FORBIDDEN_SNIPPETS = (
"docker",
"kubernetes",
"terraform",
"redis",
"boto3",
"subprocess",
"os.system",
)
def main() -> int:
extras = []
for path in ROOT.rglob("*"):
if path.is_file() and path.name not in ALLOWED and path.suffix != ".pyc":
extras.append(str(path))
text = (ROOT / "fetch.py").read_text(encoding="utf-8").lower()
hits = [s for s in FORBIDDEN_SNIPPETS if s in text]
if extras or hits:
print("workspace contract failed", extras, hits)
return 1
print("workspace contract ok")
return 0
if __name__ == "__main__":
sys.exit(main())
Run it the way you will ask them to run it.
python submit_check.py && pytest -q
You should see the resume test fail on the starter. That red line is the assignment. Everything else is costume.
A sample solution you can live with
Label this as a sample, not as the only correct patch. It keeps state in PART, sends Range only when that file exists, and refuses to append when the server answers 200.
# fetch.py — sample solution, for the rubric, not a library
from __future__ import annotations
from pathlib import Path
import urllib.error
import urllib.request
DEST = Path("/work/data.bin")
PART = Path("/work/data.bin.part")
class ResumeError(RuntimeError):
pass
def fetch(url: str) -> Path:
existing = PART.read_bytes() if PART.exists() else b""
offset = len(existing)
req = urllib.request.Request(url, method="GET")
if offset:
req.add_header("Range", f"bytes={offset}-")
try:
resp = urllib.request.urlopen(req, timeout=10)
except urllib.error.HTTPError as exc:
raise ResumeError(f"http {exc.code}") from exc
try:
status = getattr(resp, "status", 200)
body = resp.read()
if status == 206:
PART.write_bytes(existing + body)
elif status == 200:
# Server ignored Range. Do not append. Replace.
PART.write_bytes(body)
else:
raise ResumeError(f"unexpected status {status}")
PART.replace(DEST)
return DEST
finally:
resp.close()
The interesting line is the 200 branch. Models like to treat "we got bytes" as "resume worked." Your test makes that lie visible. A candidate who pastes a generic downloader will append XXXX to the payload and still smile at a green 200.
Grade the loop, then the patch
You already have pytest. That is necessary and not sufficient. Two candidates can both turn the tests green. One did it by deleting the Range header. One did it by honoring 206 and resetting on 200. If you only grade the final tree, you hired a coin flip.
Read commands.log first. You want a short loop: open the test, run it, change fetch.py, run it again. A log that begins with pip install celery is not curiosity. It is a candidate leaving the piano you provided. A log that never runs pytest is a model essay with a .py extension.
Then read the patch as a decision, not as style. Did they keep the partial file out of DEST until the body was complete? Did they treat 200-on-range as a reset? Did they add a lock file you did not ask for? Extra correctness is not extra credit when it pulls a new runtime into a 10-byte problem.
A compact rubric you can apply in ten minutes looks like this. Keep it next to the packet so you do not reinvent taste at 11 p.m.
| Signal in the workspace | Hire leaning | Walk-away leaning |
|---|---|---|
| pytest green, contract green, log stays in /work | Strong | — |
| pytest green after deleting the 206 test | — | Strong |
| Redis, docker, or a second host in the log | — | Strong |
| Model used, but each step reruns the failing test | Fine | — |
| Huge client, no commands.log | — | Strong |
| Extra checksum you did not ask for, still in /work | Neutral | Neutral |
Notice what is missing. There is no row for "used AI" versus "did not." That row rewards people who hide the tool. You issued the tool. Hide-and-seek is not a skill you need on call.
How candidates actually fail this
The first failure is cinematic infrastructure. The model proposes MinIO, then S3, then a queue "so it can scale." The candidate accepts the story because it sounds like work they have seen on LinkedIn. Your submit_check.py should be boring enough that this path dies in one command, not in a debate.
The second failure is silent append. They send Range, the fixture answers 200 with a full body, and they concatenate. The file grows. The happy-path test may still pass if they never seed PART. That is why the third test exists. If you omit it, you will hire a client that corrupts on the first uncooperative CDN.
The third failure is a clean tree with no replay. They paste a finished fetch.py from another machine and run pytest once. You cannot tell whether they understood 206 or whether a model did. Same prompt, different laptop, again. Reject the missing log the way you would reject a missing test. You asked for the recording, not for a magic trick.
A fourth failure shows up in strong seniors. They rewrite urllib into httpx plus a session pool plus retries, all still inside /work. The tests pass. The contract may pass. You still fail it if the diff cannot be reviewed in a lunch break. The assignment was resume semantics, not a new HTTP stack. Taste is a constraint. Say that out loud in the brief if your bar is ruthless about surface area.
Who should not use this packet
Do not use a shared-box take-home to hire a staff engineer whose job is to choose the platform. This exercise punishes platform thinking on purpose. That is a feature for an IC role on an existing service. It is malpractice for a role whose first month is a storage RFP.
Do not put customer data, licensed corpora, or real credentials in the workspace. A free server is still someone else's disk. Give them a ten-byte fixture. If your legal team cannot accept candidate code landing on a vendor VM, host the same contract yourself and keep the rubric. The idea is equal machines, not a particular vendor.
Do not replace a pairing session with this packet if the role is mostly collaboration under time pressure. A log shows sequence. It does not show whether they can say "I am stuck" out loud. And do not pretend submit_check.py is an isolation boundary. It is a courtesy fence. A determined candidate can smuggle work from another laptop. You are filtering sloppy inflation, not running a CTF.
Time-box it. Ninety minutes on a shared machine is plenty for this bug. If a candidate needs a weekend, they are building a product you did not ask for, or they never ran the test.
What you are actually buying
Sunday night gets simpler when both zips came from the same kind of box. You stop comparing a concert grand to an upright. You compare two performances of the same piece. The model is allowed. The cluster is not. The resume either honors 206 or it does not.
Issue the machine with the prompt. Grade the log, the contract, and the 200-versus-206 branch. Everything else is a story about laptops you never saw.
Top comments (0)