{"slug": "pin-the-entrypoint-snapshot-before-any-internal-refactor", "title": "Pin the Entrypoint Snapshot Before Any Internal Refactor", "summary": "A developer advises locking an entrypoint's observable outputs before refactoring internal code, warning that helper tests miss script-level behavior such as print text, file bytes, and CLI argument order. The post uses a teaching fixture to demonstrate building golden snapshots from frozen inputs and re-running the full command after each edit.", "body_md": "A messy-repo refactor fails when helpers move first.\n\nLock the entrypoint's observable outputs before any edit.\n\nThen change one internal function and nothing else.\n\nMost AI patches target a small function in isolation.\n\nThat function often looks cleaner after the edit.\n\nCallers still depend on files, globals, and print order.\n\nA passing helper test does not protect the script.\n\nThe script is what operators actually run.\n\nTreat that script as the contract, not the helper.\n\nCheap model output makes large diffs easy to produce.\n\nIt does not make those diffs safe to merge.\n\nSafety still comes from frozen, replayable observables.\n\nThe listing below is a labeled teaching fixture.\n\nIt is not production code from a live system.\n\nIt mixes pricing, tax, and invoice file writes.\n\n``` python\n# messy_invoice.py — teaching fixture, not production\nfrom pathlib import Path\nimport json\nimport sys\n\nTAX = 0.08\nOUT = Path(\"out\")\n_last = {}\n\ndef load_items(path):\n    rows = []\n    for line in Path(path).read_text().splitlines():\n        sku, qty, price = line.split(\",\")\n        rows.append({\"sku\": sku, \"qty\": int(qty), \"price\": float(price)})\n    _last[\"rows\"] = rows\n    return rows\n\ndef subtotal(rows):\n    s = 0.0\n    for r in rows:\n        s += r[\"qty\"] * r[\"price\"]\n        if r[\"qty\"] >= 10:\n            s -= r[\"price\"]  # implicit bulk rule\n    _last[\"subtotal\"] = s\n    return s\n\ndef tax_on(amount, region):\n    rate = TAX\n    if region == \"EU\":\n        rate = 0.19\n    if region == \"EXEMPT\":\n        rate = 0.0\n    return round(amount * rate, 2)\n\ndef write_invoice(rows, region, dest):\n    OUT.mkdir(exist_ok=True)\n    sub = subtotal(rows)\n    tax = tax_on(sub, region)\n    total = round(sub + tax, 2)\n    payload = {\n        \"region\": region,\n        \"lines\": len(rows),\n        \"subtotal\": sub,\n        \"tax\": tax,\n        \"total\": total,\n    }\n    Path(dest).write_text(json.dumps(payload, indent=2) + \"\\n\")\n    print(f\"WROTE {dest} total={total}\")\n    return payload\n\ndef main(argv):\n    src = argv[1]\n    region = argv[2]\n    dest = argv[3]\n    rows = load_items(src)\n    return write_invoice(rows, region, dest)\n\nif __name__ == \"__main__\":\n    main(sys.argv)\n```\n\nThree hazards sit inside that short teaching module.\n\nThe global `_last` dict stores implicit process state.\n\nThe subtotal loop hides a bulk discount rule.\n\nHelper tests miss print text and file bytes.\n\nThey also miss argument order on the CLI.\n\nA whole-run snapshot catches those observables in one gate.\n\nBuild a golden directory from a frozen input corpus.\n\nStore stdout, stderr, exit code, and output hashes.\n\nRe-run the same command after every internal edit.\n\nKeep the input fixtures tiny and committed to git.\n\nOne CSV is enough for the first gate.\n\nAdd a second CSV only after the first stays green.\n\n```\n# fixtures/items_basic.csv\nA,1,10.00\nB,10,2.50\nC,3,4.00\n# fixtures/items_eu.csv\nD,2,40.00\nE,10,1.00\n```\n\nHand totals keep the first goldens honest.\n\nDo not trust the script to mark its own exam.\n\nThe US basic case should resolve as follows.\n\nLine A contributes `1 * 10.00 = 10.00`.\n\nLine B contributes `10 * 2.50 = 25.00`.\n\nQuantity 10 then subtracts `2.50` as bulk credit.\n\nLine C contributes `3 * 4.00 = 12.00`.\n\nSubtotal is `10.00 + 22.50 + 12.00 = 44.50`.\n\nUS tax is `round(44.50 * 0.08, 2) = 3.56`.\n\nTotal is `round(44.50 + 3.56, 2) = 48.06`.\n\nStdout must read `WROTE out/inv.json total=48.06`.\n\nAny later extract must preserve those exact bytes.\n\nThe EU bulk case should resolve next.\n\nLine D contributes `2 * 40.00 = 80.00`.\n\nLine E contributes `10 * 1.00 - 1.00 = 9.00`.\n\nSubtotal is `89.00` before region tax.\n\nEU tax is `round(89.00 * 0.19, 2) = 16.91`.\n\nTotal is `round(89.00 + 16.91, 2) = 105.91`.\n\nRun the entrypoint under a clean working directory.\n\nDo not reuse leftover output files between cases.\n\nCapture the process, not a Python function call.\n\n``` python\n# char_harness.py — teaching fixture\nfrom __future__ import annotations\n\nimport hashlib\nimport json\nimport os\nimport shutil\nimport subprocess\nimport sys\nfrom pathlib import Path\n\nROOT = Path(__file__).resolve().parent\nGOLDEN = ROOT / \"golden\"\nCASES = [\n    {\n        \"name\": \"us_basic\",\n        \"args\": [\"fixtures/items_basic.csv\", \"US\", \"out/inv.json\"],\n    },\n    {\n        \"name\": \"eu_bulk\",\n        \"args\": [\"fixtures/items_eu.csv\", \"EU\", \"out/inv.json\"],\n    },\n    {\n        \"name\": \"exempt_basic\",\n        \"args\": [\"fixtures/items_basic.csv\", \"EXEMPT\", \"out/inv.json\"],\n    },\n]\n\ndef sha256(path: Path) -> str | None:\n    if not path.is_file():\n        return None\n    h = hashlib.sha256()\n    h.update(path.read_bytes())\n    return h.hexdigest()\n\ndef run_case(case: dict) -> dict:\n    work = ROOT / \"work\" / case[\"name\"]\n    if work.exists():\n        shutil.rmtree(work)\n    work.mkdir(parents=True)\n    shutil.copytree(ROOT / \"fixtures\", work / \"fixtures\")\n    dest_rel = Path(case[\"args\"][2])\n    proc = subprocess.run(\n        [sys.executable, str(ROOT / \"messy_invoice.py\"), *case[\"args\"]],\n        cwd=work,\n        capture_output=True,\n        text=True,\n        env={**os.environ, \"PYTHONHASHSEED\": \"0\"},\n    )\n    out_file = work / dest_rel\n    return {\n        \"name\": case[\"name\"],\n        \"exit_code\": proc.returncode,\n        \"stdout\": proc.stdout,\n        \"stderr\": proc.stderr,\n        \"out_sha256\": sha256(out_file),\n        \"out_text\": out_file.read_text() if out_file.is_file() else None,\n    }\n\ndef record() -> None:\n    GOLDEN.mkdir(exist_ok=True)\n    for case in CASES:\n        snap = run_case(case)\n        (GOLDEN / f\"{case['name']}.json\").write_text(\n            json.dumps(snap, indent=2) + \"\\n\"\n        )\n        print(f\"recorded {case['name']}\")\n\ndef check() -> int:\n    failed = 0\n    for case in CASES:\n        got = run_case(case)\n        path = GOLDEN / f\"{case['name']}.json\"\n        want = json.loads(path.read_text())\n        keys = [\"exit_code\", \"stdout\", \"stderr\", \"out_sha256\", \"out_text\"]\n        for key in keys:\n            if got[key] != want[key]:\n                failed += 1\n                print(f\"DRIFT {case['name']} {key}\")\n                print(f\"  want={want[key]!r}\")\n                print(f\"  got ={got[key]!r}\")\n    if failed:\n        print(f\"{failed} field(s) drifted\")\n        return 1\n    print(\"snapshot gate green\")\n    return 0\n\nif __name__ == \"__main__\":\n    cmd = sys.argv[1] if len(sys.argv) > 1 else \"check\"\n    if cmd == \"record\":\n        record()\n    else:\n        raise SystemExit(check())\n```\n\nEach case records five comparable fields on disk.\n\nThose fields are the only pass signal.\n\nPretty logs outside the snapshot are noise.\n\nA gate that never fails is not a gate.\n\nBreak one print on purpose after recording.\n\nConfirm that check reports DRIFT on stdout.\n\n```\npython char_harness.py record\npython char_harness.py check\n```\n\nEdit the print format, then run check again.\n\nRestore the print before any real refactor.\n\nThe restored run must return exit code 0.\n\nA sample drift block looks like this.\n\nThe numbers below match a one-cent rounding slip.\n\nTreat that slip as a failed extract, not noise.\n\n```\nDRIFT us_basic stdout\n  want='WROTE out/inv.json total=48.06\\n'\n  got ='WROTE out/inv.json total=48.06\\n'\nDRIFT us_basic out_text\n  want='{\\n  \"region\": \"US\",\\n  \"lines\": 3,\\n  \"subtotal\": 44.5,\\n  \"tax\": 3.56,\\n  \"total\": 48.06\\n}\\n'\n  got ='{\\n  \"region\": \"US\",\\n  \"lines\": 3,\\n  \"subtotal\": 44.5,\\n  \"tax\": 3.56,\\n  \"total\": 48.05\\n}\\n'\n```\n\nDo not rename files in the same patch.\n\nDo not move CLI flags in the same patch.\n\nDo not retune tax rounding in the same patch.\n\nThe smallest safe change in this module is extraction.\n\nPull the bulk rule out of `subtotal`.\n\nKeep write_invoice output byte-identical after the extract.\n\n``` python\ndef bulk_credit(row):\n    if row[\"qty\"] >= 10:\n        return row[\"price\"]\n    return 0.0\n\ndef subtotal(rows):\n    s = 0.0\n    for r in rows:\n        s += r[\"qty\"] * r[\"price\"]\n        s -= bulk_credit(r)\n    _last[\"subtotal\"] = s\n    return s\n```\n\nRe-run the snapshot gate after that extract.\n\nGreen means the entrypoint still writes the same bytes.\n\nRed means the extract changed pricing or print text.\n\n`python char_harness.py check` once.\nSkip any patch that fails one checklist row.\n\nWide cleanup is not a first-cycle goal.\n\nQueue extra extracts for later green cycles.\n\n| Drift field | Likely cause | Safe action | \n|---|---|---|\n| `exit_code` | uncaught exception or new `sys.exit` | stop; inspect traceback | \n| `stdout` | print format or call order | stop unless format was the goal | \n| `stderr` | new warning or log line | treat as contract unless documented | \n| `out_sha256` | numeric rounding or key order | stop; compare `out_text` | \n| `out_text` | tax, bulk rule, or region mapping | revert; split the change | \n\nUse one row as a stop rule, not a suggestion.\n\nAny unexplained drift must block the current patch.\n\nDo not rewrite goldens to match a guessed refactor.\n\nInvoice totals look like business rules, not formatting.\n\nA one-cent drift is a failed extract.\n\nDo not round-trip floats through new types in the same patch.\n\nDo not switch json.dumps settings during extract.\n\nIndent, separators, and key order are contract bytes.\n\nHash equality will fail if those settings move.\n\nThe working directory leaks into relative output paths.\n\nSet PYTHONHASHSEED to keep hash walks stable.\n\nCopy fixtures into a fresh work tree every case.\n\nPath separators can drift across operating systems.\n\nKeep dest arguments in POSIX form inside cases.\n\nRun the gate on one OS, not two, per corpus.\n\nGrow new cases only from escaped production bugs.\n\nThis teaching fixture records only three named cases.\n\nThose three cases will still miss many branches.\n\nAdd a case when a real bug escapes, not before.\n\nEach new case must fail once before it is recorded.\n\nA case that never failed is an untested assertion.\n\nThe coding model proposes the internal extract only.\n\nThe snapshot gate accepts or rejects that extract.\n\nDo not let the model refresh golden files.\n\nDisclosure: This article was prepared as part of MonkeyCode's product outreach.\n\nMonkeyCode offers free model access and a free server option.\n\nThose two options can host this harness beside the messy module.\n\nThe model sees the frozen cases and the current function body.\n\nIt does not get permission to rewrite golden snapshots.\n\nKeep the model prompt narrow, mechanical, and single-purpose.\n\nThe block below is an unexecuted prompt template.\n\nIt is not a log from a real session.\n\nPaste it only after `check` is already green.\n\n```\nPreserve golden/* byte-for-byte.\nDo not edit fixtures, CLI args, or char_harness.py.\nExtract bulk_credit from subtotal in messy_invoice.py.\nReturn one patch. Stop if any snapshot field would drift.\n```\n\nAsk for one extract that preserves snapshot bytes.\n\nReject patches that touch fixtures, goldens, or CLI args.\n\nIf you try that workflow, run the harness locally first.\n\nThen point a free MonkeyCode session at the same gate.\n\nIt does not prove thread safety or performance.\n\nIt does not prove unknown regions or empty files.\n\nIt does not prove tax law, only current bytes.\n\nHash equality is brittle with unstable key order.\n\nJSON dumps must keep stable separators and indent.\n\nTimestamps inside invoices will break this design.\n\nHidden network calls will also escape this gate.\n\nSo will clock reads and unordered set iteration.\n\nStrip those sources before recording the first corpus.\n\nSkip this if you still lack a runnable entrypoint.\n\nSkip this if outputs include raw secrets or PII.\n\nSkip this if the script is nondeterministic by design.\n\nSkip this for greenfield modules with no users.\n\nThose modules need designed tests, not snapshots.\n\nCharacterization is for behavior you cannot rewrite from memory.\n\nDo not use this as a license for large rewrites.\n\nThe method allows one internal change per cycle.\n\nWide cleanups belong after many green cycles, not before.\n\nStart at the script the operator actually runs.\n\nRecord stdout, exit code, and output file hashes.\n\nExtract one helper only after that gate is green.", "url": "https://wpnews.pro/news/pin-the-entrypoint-snapshot-before-any-internal-refactor", "canonical_source": "https://dev.to/hackrs_6393/pin-the-entrypoint-snapshot-before-any-internal-refactor-2k95", "published_at": "2026-09-07 14:36:16+00:00", "updated_at": "2026-09-07 14:57:13.926109+00:00", "lang": "en", "topics": ["developer-tools"], "entities": [], "alternates": {"html": "https://wpnews.pro/news/pin-the-entrypoint-snapshot-before-any-internal-refactor", "markdown": "https://wpnews.pro/news/pin-the-entrypoint-snapshot-before-any-internal-refactor.md", "text": "https://wpnews.pro/news/pin-the-entrypoint-snapshot-before-any-internal-refactor.txt", "jsonld": "https://wpnews.pro/news/pin-the-entrypoint-snapshot-before-any-internal-refactor.jsonld"}}