Your local changelog helper just printed a clean note. You want that same script on a free server before lunch. Would you ship the swap with no receipt at all?
I would not ship that swap on a hunch. A local green run is not a real cutover. This card is the receipt I would require first.
I am not inventing an outage, a bill, or a benchmark. Treat the script below as a proposal you can paste. Run it in a scratch repo before you trust the exit code.
The decision in front of you
You are allowing one swap, and only that swap. The dry-run may call a hosted model on a free path. It may also sit on a free server option.
It may not write to main or open a pull request. It may not touch customer notes or private keys. Does that scope feel almost too small to bother?
Small is the point of a solo cutover. You can finish the card before the laptop sleeps. If the five proof files will not fit in one sitting, abandon the attempt.
Where the hosted path enters
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
I place MonkeyCode in one spot in this workflow. It is the free model path and the free server option. The operator supplied those two availability claims only.
I will not invent model names or token caps here. I will not invent box sizes or a free-tier duration. Need the live numbers before you boot anything?
Read the project docs on the day you actually run. A token headline from a launch post goes stale fast. Treat that headline as a claim you must recheck.
Pull the product name out and these gates still stand. Any hosted model with a staging box can use them. The operator also describes the project as open source.
I am not pasting a star count or a repo URL. Inspect the tree yourself if you care about the license. A badge is not evidence for this cutover.
Files this card needs
Keep the cutover card inside the repo you already ship. Three paths are enough for a one-person job. You do not need a platform team for this.
The status file stores the gate flags you flip. The evidence folder stores the proof files beside it. The checker exits non-zero when any gate stays open.
No proof file means the cutover fails closed. A missing key means the cutover fails closed too. A production-looking host means the same hard stop.
Would you rather crash in CI than explain a bad note? I would take that early crash every time.
1. Freeze one fixture
Do not point the first remote call at a live ticket. Use one public line you can commit without fear. A changelog fragment is enough fuel for this drill.
mkdir -p cutover/fixtures cutover/evidence
printf '%s\n' 'feat: add retry budget to the importer' > cutover/fixtures/input.txt
Can a teammate rerun that file next week alone? If the answer is no, this gate stays shut. A fixture only you can find is not frozen.
2. Save the offline note
Run your helper with the network disabled first. If you have no helper yet, write the note by hand. The gate cares about the file, not a vendor SDK.
printf '%s\n' 'Add a retry budget to the importer.' > cutover/evidence/local.txt
test -s cutover/evidence/local.txt
Keep stdout only, and drop every secret before saving. Empty output leaves this gate closed on purpose. A note that names a customer leaves it closed too.
You want a dull sentence, not a private diary. The handwritten line above is a stand-in for your real offline run. Swap it for real stdout when you have it.
3. Shadow one hosted reply
Call the free model once, then stop calling it. Save the reply next to the local note. Do not post either file to chat or to main.
printf '%s\n' 'Add a retry budget to the importer now.' > cutover/evidence/remote.txt
diff -u cutover/evidence/local.txt cutover/evidence/remote.txt > cutover/evidence/diff.txt || true
That printf is a stand-in, not a live API call. Replace it with your real one-shot command when the docs check out. Did the remote note invent a version number?
Did it drop the fixture verb you froze earlier? Leave the remote flag false until both answers are no. One extra adjective in the note is fine.
A brand new release claim is not fine. Spend one remote call on this fixture, then stop. A second call is already past this card's cost boundary.
4. Scan your own fixture
The free server may run the checker for you. It still must not hold your production keys. Write the scan result even when the scan looks clean.
if grep -R -n -E 'sk-|API_KEY|BEGIN PRIVATE' cutover/fixtures cutover/evidence; then
echo HIT > cutover/evidence/secret_scan.txt
else
echo CLEAN > cutover/evidence/secret_scan.txt
fi
A hit is not a task you fix after launch. Delete the bad file, rotate the key, and stop. Why keep a cutover alive after a leak line?
This grep is a tripwire for your own drill files. It is not a company-wide scanner and it will miss clever leaks. If your fixture needs a clever scanner, you are holding the wrong text.
5. Pin a staging host
Write the host name you will actually boot. Reject names that look like the production box. A free server option is not a shared-hostname license.
printf '%s\n' 'staging.example.test' > cutover/evidence/host.txt
A leading prod label fails the checker immediately. The word production inside the name fails it too. Use a name you can delete without paging anyone.
6. Fill the status file
Flip a flag only after the matching proof exists. Leave every unfinished flag set to false for now. The sample below is supposed to fail the run.
{
"fixture_frozen": true,
"local_saved": true,
"remote_saved": false,
"no_secrets": true,
"staging_host_only": false,
"abandon_if": "any gate false or any evidence file missing"
}
Do you see the false fields in the sample? That card is not allowed to deploy anywhere. A half-true JSON file is how quiet launches happen.
The flag must be JSON true, not the string true. A string true will fail the checker on purpose. Save this file beside the checker before you run anything.
7. Run the fail-closed checker
Paste the script into the checker path shown below. It is a proposal, not a service I host for you. I have not timed it, and I will not pretend I did.
#!/usr/bin/env python3
"""Fail closed unless every cutover gate has evidence."""
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent
STATUS = ROOT / "status.json"
EVIDENCE = {
"fixture_frozen": ROOT / "fixtures" / "input.txt",
"local_saved": ROOT / "evidence" / "local.txt",
"remote_saved": ROOT / "evidence" / "remote.txt",
"no_secrets": ROOT / "evidence" / "secret_scan.txt",
"staging_host_only": ROOT / "evidence" / "host.txt",
}
REQUIRED = list(EVIDENCE)
def fail(msg: str) -> None:
print(f"CUTOVER_FAIL {msg}")
sys.exit(2)
def main() -> None:
if not STATUS.is_file():
fail("missing status.json")
try:
data = json.loads(STATUS.read_text())
except json.JSONDecodeError:
fail("status.json is not valid JSON")
if not isinstance(data, dict):
fail("status.json must be an object")
for gate in REQUIRED:
if gate not in data or data[gate] is not True:
fail(f"gate closed {gate}")
path = EVIDENCE[gate]
if not path.is_file() or path.stat().st_size == 0:
fail(f"empty evidence {path.name}")
scan = (ROOT / "evidence" / "secret_scan.txt").read_text()
if "HIT" in scan:
fail("secret scan hit")
host = (ROOT / "evidence" / "host.txt").read_text().strip()
if host.startswith("prod.") or "production" in host:
fail("host looks like production")
print("CUTOVER_OK")
if __name__ == "__main__":
main()
python cutover/check_cutover.py; echo "exit=$?"
You want exit code 2 while the remote flag is false. That red exit is the drill, not a bug. Only a full card should print the ok line.
8. Break a proof file on purpose
Rename the host proof, run the checker, then restore it. This rename is the failure fixture for the card. A checker you never break is an untested checker.
mv cutover/evidence/host.txt cutover/evidence/host.txt.bak
python cutover/check_cutover.py; echo "exit=$?"
mv cutover/evidence/host.txt.bak cutover/evidence/host.txt
Did the job stay green after that rename? Then the checker is not actually on the job. Wire it to the step that would boot the server.
How to read the red line
A missing status file means you never started the card. A closed remote flag means the shadow note is not accepted yet. An empty host file means the host pin is still missing.
A secret scan hit means you stop and rotate keys. A production-looking host means you rename before any boot. None of these lines are warnings you click through.
Abandon rules
Stop when any gate flag is still false. Stop when any evidence file is missing or empty. Stop when the secret scan file contains a hit.
Stop when the host name looks like production. Also stop after two identical errors on the free path. Do not loop until a token headline is exhausted.
A retry storm is a quiet cost, even on a free option. Set a clock for one sitting, then walk away. If the shadow diff is still unexplained, abandon the cutover.
Come back later with a smaller fixture file. That exit is a clean stop, not a personal failure. Rollback is just the false flags plus no server boot.
Who should skip this card
Skip this card if you need a contractual uptime promise. A free server option is availability, not an SLA. Skip it if you need a signed audit trail tomorrow.
Skip it when the prompt holds customer text or private keys. This card never makes that kind of input safe. Skip it if the job must write files or push tags.
Those actions need a different permit than this one. Skip it if your team will ignore a red exit code. A checklist that nobody runs is just theater.
Limits to say out loud
The checker does not score the summary quality at all. A human still has to read the saved diff. I am not publishing accuracy numbers in this draft.
I did not run a benchmark, so I will not fake one. Free model access can change without a fresh blog post. The free server option can change the same quiet way.
The host check is only a string tripwire, not DNS policy. A weird alias can still slip past that string. Write the doc date only after you open the docs.
Do not commit a date you did not actually check. If that free path is already in your toolbox, use it here. Run this card before the first server boot.
Which gate fails first when you try this on your repo?
Top comments (0)