DEV Community

Charlie Hu
Charlie Hu

Posted on

Weekend Build Log: A Slim Context Pack for Off-Laptop AI Patches

AI coding sessions pollute a working tree faster than they produce a mergeable patch. A weekend-sized fix is to compile a slim context pack, run the model work on a disposable server, and import only allowlisted paths. The local repo stays the source of truth. The remote box stays throwaway.

This log records a scoped side project: a pack compiler, a patch importer, and a short decision table for what never leaves the laptop. Fancy review scoring, commit-message generation, and debt heatmaps were left out on purpose. Those problems already have other notes. This one is about isolation and context waste.

Core constraints for the weekend

The build had three hard limits. No new SaaS account. No rewrite of the host application. No claim that a model “understands” architecture because a README was pasted into a prompt.

  • Pack size is measured in files and lines, not in vibes.
  • The remote session may write anything. The laptop only accepts paths on an allowlist.
  • Unexecuted examples are labeled. No fabricated latency, token burn, or pass-rate numbers appear below.

Those limits forced a boring shape: Python 3, git, and a few shell commands. The interesting part is the contract between pack, remote, and import.

The failure this tool targets

Free-tier models do not fail only by writing wrong code. They fail by reading the wrong tree. A whole monorepo dump mixes generated clients, vendored snapshots, and half-finished spikes. The patch that comes back then touches lockfiles, CI, and a public API in one sitting.

A second failure is local mixing. Generated files land next to real work. git diff becomes a negotiation. Reverts get personal.

The pack-and-import loop treats both as the same bug: unbounded context in, unbounded paths out.

What shipped in the demo

The working demo is three files plus a constraints document.

  1. constraints/PACK.md — human rules the pack compiler also parses.
  2. pack.py — walks a frozen file list and emits context_pack.json.
  3. import_patch.py — applies a unified diff only if every path is allowlisted.
  4. allowlist.txt — the only paths that may return from the remote session.

A reader can run the compiler and the importer without a model. That was the acceptance test for the weekend: the harness is useful even when the remote side is a stub.

PACK.md as a machine-readable freeze

PACK.md is not marketing copy. It is a freeze of what the model is allowed to know.

# PACK freeze
include:
  - src/billing/api.py
  - src/billing/types.py
  - tests/test_billing_api.py
exclude_globs:
  - "**/generated/**"
  - "**/*lock*"
  - ".env*"
max_file_lines: 400
public_symbols_only: true
never_send:
  - secrets
  - production hostnames
  - customer fixtures
Enter fullscreen mode Exit fullscreen mode

The compiler refuses to pack a file that is not listed under include. Globs in exclude_globs are a second fence, not a substitute for the explicit list. Explicit beats clever when the failure mode is “the model saw a private key example.”

pack.py

The script below is a complete, runnable compiler for the freeze format above. It is a proposal for a weekend tool, not a report of production traffic.

#!/usr/bin/env python3
"""Compile a slim context pack from PACK.md. Unexecuted against private repos."""
from __future__ import annotations

import json
import re
from pathlib import Path

ROOT = Path(".")
PACK_MD = ROOT / "constraints" / "PACK.md"
OUT = ROOT / "context_pack.json"

INCLUDE_RE = re.compile(r"^\s+-\s+(\S+)")
GLOB_RE = re.compile(r'^\s+-\s+"([^"]+)"')


def parse_pack_md(text: str) -> dict:
    section = None
    spec = {"include": [], "exclude_globs": [], "max_file_lines": 400,
            "public_symbols_only": False, "never_send": []}
    for raw in text.splitlines():
        line = raw.rstrip()
        if line.startswith("include:"):
            section = "include"
            continue
        if line.startswith("exclude_globs:"):
            section = "exclude_globs"
            continue
        if line.startswith("never_send:"):
            section = "never_send"
            continue
        if line.startswith("max_file_lines:"):
            spec["max_file_lines"] = int(line.split(":", 1)[1].strip())
            section = None
            continue
        if line.startswith("public_symbols_only:"):
            spec["public_symbols_only"] = line.split(":", 1)[1].strip() == "true"
            section = None
            continue
        if section == "include":
            m = INCLUDE_RE.match(line)
            if m:
                spec["include"].append(m.group(1))
        elif section in {"exclude_globs", "never_send"}:
            m = GLOB_RE.match(line) or INCLUDE_RE.match(line)
            if m:
                spec[section].append(m.group(1))
    return spec


def public_slice(src: str) -> str:
    keep = []
    for line in src.splitlines():
        stripped = line.strip()
        if stripped.startswith(("def ", "class ", "async def ", "@", "from ", "import ")):
            keep.append(line)
        elif stripped.startswith(("#", '"""', "'''")) and keep:
            keep.append(line)
    return "\n".join(keep)


def main() -> None:
    spec = parse_pack_md(PACK_MD.read_text(encoding="utf-8"))
    files = []
    for rel in spec["include"]:
        path = ROOT / rel
        if not path.is_file():
            raise SystemExit(f"missing include: {rel}")
        text = path.read_text(encoding="utf-8", errors="replace")
        lines = text.splitlines()
        if len(lines) > spec["max_file_lines"]:
            text = "\n".join(lines[: spec["max_file_lines"]]) + "\n# [truncated]\n"
        if spec["public_symbols_only"] and path.suffix == ".py":
            text = public_slice(text)
        files.append({"path": rel, "bytes": len(text.encode()), "text": text})
    payload = {
        "spec": {k: spec[k] for k in spec if k != "never_send"},
        "file_count": len(files),
        "total_bytes": sum(f["bytes"] for f in files),
        "files": files,
        "instruction": (
            "Edit only files listed in allowlist.txt. "
            "Return a unified git diff. Do not invent paths."
        ),
    }
    OUT.write_text(json.dumps(payload, indent=2), encoding="utf-8")
    print(f"wrote {OUT} files={payload['file_count']} bytes={payload['total_bytes']}")


if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

A typical compile looks like this:

python3 pack.py
python3 - <<'PY'
import json
from pathlib import Path
p = json.loads(Path("context_pack.json").read_text())
print(p["file_count"], p["total_bytes"])
for f in p["files"]:
    print(f["path"], f["bytes"])
PY
Enter fullscreen mode Exit fullscreen mode

If total_bytes is still large, the include list is wrong. The fix is to cut files, not to “summarize the repo” with another model. Summaries hide the exact signatures the patch has to match.

Off-laptop session, on-laptop import

The remote side can be any disposable box the operator already trusts: a free server option, a spare VM, or a container that is deleted after the diff is copied out. The laptop never mounts the working tree as writable for the model.

Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode is relevant here only as one place that currently offers free model access and a free server option for this kind of scratch session. No model names, quotas, hardware, or durability claims are attached to that mention. The scripts above do not depend on it.

A minimal remote loop, labeled as a proposed workflow:

# on the laptop
scp context_pack.json allowlist.txt user@scratch:~/session/
ssh user@scratch 'mkdir -p ~/session/work && cd ~/session/work && git init -q'

# on the scratch box (operator-supplied editor or model session)
# write files, then:
cd ~/session/work
git add -A
git diff --cached > ~/session/ai.patch

# back on the laptop
scp user@scratch:~/session/ai.patch ./incoming.patch
python3 import_patch.py incoming.patch allowlist.txt
Enter fullscreen mode Exit fullscreen mode

The importer is the actual product of the weekend. Everything else is packaging.

import_patch.py

#!/usr/bin/env python3
"""Apply a unified diff only when every path is allowlisted."""
from __future__ import annotations

import subprocess
import sys
from pathlib import Path

FORBIDDEN_PREFIXES = (
    ".git/", ".env", "id_rsa", "id_ed25519", "secrets/", "prod/"
)


def paths_from_diff(diff: str) -> list[str]:
    found: list[str] = []
    for line in diff.splitlines():
        if line.startswith("+++ b/"):
            found.append(line[6:])
        elif line.startswith("diff --git "):
            parts = line.split()
            if len(parts) >= 4 and parts[3].startswith("b/"):
                found.append(parts[3][2:])
    return sorted(set(p for p in found if p != "/dev/null"))


def main() -> None:
    if len(sys.argv) != 3:
        raise SystemExit("usage: import_patch.py incoming.patch allowlist.txt")
    diff = Path(sys.argv[1]).read_text(encoding="utf-8", errors="replace")
    allow = {
        line.strip()
        for line in Path(sys.argv[2]).read_text(encoding="utf-8").splitlines()
        if line.strip() and not line.startswith("#")
    }
    paths = paths_from_diff(diff)
    if not paths:
        raise SystemExit("no file paths in patch")
    blocked = [p for p in paths if p not in allow]
    dangerous = [p for p in paths if p.startswith(FORBIDDEN_PREFIXES) or p in {"/etc/passwd"}]
    if dangerous:
        raise SystemExit(f"refusing dangerous paths: {dangerous}")
    if blocked:
        raise SystemExit(f"paths not on allowlist: {blocked}")
    subprocess.run(["git", "apply", "--check", sys.argv[1]], check=True)
    subprocess.run(["git", "apply", sys.argv[1]], check=True)
    print("imported", ", ".join(paths))


if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

git apply --check is the cheap safety net. The allowlist is the policy. Policy first, git second.

A tight allowlist for the billing example:

# allowlist.txt — nothing else returns from the scratch box
src/billing/api.py
src/billing/types.py
tests/test_billing_api.py
Enter fullscreen mode Exit fullscreen mode

If the remote patch also rewrites pyproject.toml, import fails. That failure is the feature. Dependency edits belong on a later weekend with a lockfile review, not in the same blast as a handler change.

Decision table used while cutting scope

Candidate feature Shipped? Reason
PACK.md freeze + compiler Yes Directly cuts context
Path allowlist importer Yes Directly cuts blast radius
git apply --check Yes Already on the machine
Token or cost dashboard No Needs vendor-specific counters
Multi-file architecture map No Weekend would become a static-analysis product
Auto-merge on green tests No Hides the import policy
Chat UI on the scratch box No Not required to prove the loop
Secret scanning beyond prefixes Partial Prefix denylist only

The cuts were not aesthetic. A dashboard would have turned the post into a vendor bake-off. An architecture map would have duplicated work already sitting in other write-ups. Auto-merge would have deleted the only human checkpoint the design still has.

A reproducible test plan (not executed here)

Label: proposed tests. No pass/fail metrics are claimed.

  1. Happy path. Pack three listed files. Create a patch that edits src/billing/api.py only. Import succeeds. git status shows that one file.
  2. Lockfile sneak. Same pack. Patch also edits poetry.lock. Import exits non-zero. Working tree unchanged.
  3. Path invention. Patch adds src/billing/admin_backdoor.py. Import exits non-zero.
  4. Truncation. A listed file exceeds max_file_lines. Pack contains a # [truncated] marker. No silent omission.
  5. Missing include. PACK.md lists a file that does not exist. Compiler exits non-zero before any remote copy.
  6. Round trip. After a good import, git diff --stat matches the allowlisted paths exactly.

Commands for tests 1–3:

python3 pack.py
# construct fixtures/good.patch and fixtures/lockfile.patch by hand
python3 import_patch.py fixtures/good.patch allowlist.txt
! python3 import_patch.py fixtures/lockfile.patch allowlist.txt
Enter fullscreen mode Exit fullscreen mode

The ! is a shell assertion: a non-zero importer is success for the negative case.

What this does not solve

The pack compiler does not understand domain rules. It will happily ship a public function that must never change. Humans still own the include list.

The importer does not prove behavioral correctness. A patch can stay inside the allowlist and still break billing. Tests on the laptop remain mandatory after import.

Public-symbol slicing can drop comments that a human reviewer needed. That is a trade for smaller packs, not a quality upgrade.

A free remote box is not an isolation boundary against a determined leak. It is a convenience fence against accidental mixing. Secrets should not be on that box at all.

Who should skip this approach

  • Teams that already generate patches inside a reviewed agent sandbox with path policies.
  • Repos where the public surface is the whole tree and an include list would be dishonest.
  • Work that cannot leave the laptop for compliance reasons. A “free server” does not change data-handling rules.
  • Anyone hoping the pack will replace design. A freeze file is a fence, not an architecture.

Weekend residue

Kept: the freeze file, the compiler, the importer, and the habit of copying a diff instead of cloning a dirty tree back onto a laptop.

Skipped: model routing, reviewer scoring, session replay, and any dashboard that would have required invented quotas.

The next small step, if any, is a test that fails when PACK.md and allowlist.txt drift apart. That check is ten lines and belongs in CI. It does not need a product name to be worth running. Operators who already have a scratch box — MonkeyCode’s free server option is one such box — can drop context_pack.json there and keep the same import gate at home.

Top comments (0)