Seven Defense Rules: 90 Days of 'Defaults Are Untrustworthy'
Seven Defense Rules: 90 Days of "Defaults Are Untrustworthy"
Why this article
Between March and June 2026, I lost 17,000 characters of long-term memory, almost published three live API keys to a public GitHub repo, and burned 15 minutes on a phantom "git log garbled" issue that was just PowerShell lying to me about UTF-8.
Each incident had a different trigger. Each had a different fix. But the pattern was the same: I (or some tool) trusted a default that wasn't safe to trust.
This article consolidates the 7 defense rules I extracted from those incidents into one teaching reference. If you write code, run cron jobs, or maintain long-running context (human or AI), this is for you.
The one principle at four layers
"Defaults are untrustworthy" sounds abstract. It becomes concrete when you see it operating at four different layers of the same stack:
-m truncates multi-line messages. git log renders correct UTF-8 bytes as mojibake on Chinese Windows..gitignore that ignores build artifacts but not "non-business paths" lets auto-commit push secrets to the wrong repo..md files is faster than using a secret manager — until the file ends up in a public commit. Not rotating keys is fine — until one leaks.Each of the 7 rules below addresses one specific instance of "default assumption failed". Together they form a defense-in-depth stack.
The seven rules
Rule 0 — Trust defaults = trust disaster
The first rule is the meta-rule: all the other rules are applications of this one. The moment you trust a default — tool, platform, process, or your own memory — you've created a future disaster.
This rule isn't enforceable by code. It's a posture. Before any operation that mutates state, ask: "What default am I trusting here, and what happens if it's wrong?"
If you can't answer that, stop and verify before proceeding.
Rule 1 — UTF-8 files: Node tools only
For files like MEMORY.md, SOUL.md, AGENTS.md, and other UTF-8 source files:
read / edit / write tools (Node.js implementation, correct UTF-8)Get-Content / Set-Content / Out-File on these filesPowerShell 5.1's default system encoding is ANSI (GBK on Chinese Windows, Shift-JIS on Japanese Windows). A UTF-8 file with no BOM gets decoded as GBK. Every Chinese character becomes two bytes of garbage. Round-trip through Set-Content -Encoding UTF8 and the file is locked — any UTF-8 tool now reads mojibake.
Incident: 2026-06-12, three Set-Content operations on MEMORY.md turned 63,635 bytes into 17000 characters of permanent data loss. Backup was zero. Daily memory was the only reconstructable source.
Rule 2 — Backup check before destructive ops
Before any operation that destroys or rewrites state:
git status to see what's changedgit log to see the recent backup stateIncident: Same 6/12 disaster. The 17000 characters were lost forever because MEMORY.md had never been committed to git. "I assume it's backed up" is not a backup.
Rule 3 — Bulk delete: anchors + line ranges + user confirm
Before bulk-replacing a long section in a file:
Select-String (or Node equivalent) to find the anchorNever blind-replace. The "the line numbers shifted" or "the regex matched the wrong place" bug always hits during bulk operations.
Incident: 6/12 MEMORY.md cleanup, the auto-replace tool picked up a date string instead of the actual line, replacing the wrong content. The file was recoverable from daily memory — barely.
Rule 4 — Write-after-read-verify
For any file mutation:
The "read-write-verify" cycle catches encoding mismatches, scope mistakes, and silent corruption at the cheapest moment — before the file is closed and the error is lost.
Incident: If I'd done this on 6/12, I would have caught the GBK decoding after the first Set-Content and stopped. Three-step verification would have cost 5 seconds. Recovery cost 3 days.
Rule 5 — MEMORY.md lives in git
MEMORY.md, SOUL.md, AGENTS.md, USER.md, TOOLS.md, IDENTENTITY.md, memory/*.md — all in git.
This is non-negotiable for any file that holds long-term context. Without git:
Incident: 6/12 disaster. The fact that memory/2026-05-17.md through memory/2026-06-12.md survived in git (as part of a different commit accidentally) was the only reason the post-disaster reconstruction was possible.
Rule 6 — git commit with CJK content: Node wrapper only
For any git commit that carries Chinese, Japanese, or Korean content (commit messages OR batched file adds):
Use the Node wrapper:
node tools/git-stage-and-commit-utf8.js <workspace> -m <msg-file> <files...>
This wrapper:
git commit -F - via stdin (bypasses file path / code page)git -c i18n.commitEncoding=UTF-8 + LC_ALL=C.UTF-8Never use:
git commit -m "..." from PowerShell — only takes first line as subject, body lostgit commit -F from PowerShell — file is read using GBK code pagegit add + git commit (PowerShell native) — same problem, commit message goes through ANSIIncident: 6/15 22:30–22:45, I thought my commit messages were garbled. Tried Python, chcp 65001, six different stdin strategies, six amendment cycles. All wasted — the bytes were always correct, PowerShell was lying about the display. See Rule 6a.
Rule 6a — PowerShell output is NOT a reliable UTF-8 oracle
This is the corollary to Rule 6. PowerShell's terminal renders UTF-8 bytes using the system code page. On Chinese Windows, that's CP936 (GBK). On Japanese Windows, that's CP932 (Shift-JIS).
So git log and git cat-file display correct UTF-8 commit messages as mojibake — even though the bytes in git are 100% correct.
The only reliable way to verify a commit's UTF-8:
node -e "const b=require('child_process').execSync('git cat-file commit HEAD',{encoding:'buffer'}); const sep=b.indexOf(Buffer.from('\n\n')); console.log(b.slice(sep+2).toString('utf8').substring(0,300))"
If the first 300 characters render as correct Chinese/Japanese, the commit is correct. Don't trust git log for this.
Incident: 6/15 22:30, I spent 15 minutes re-amending commits because git log showed "garbled" characters. Six amendment cycles, three different tools, all to fix a problem that never existed. Verified after the fact: every commit was 100% clean.
Rule 7 — All automated actions must be explicitly scoped + secret-scan zero hits before push
Extends Rule 6/6a from "git commands" to the entire automated behavior surface:
cd $WORKSPACE && git add , never find . or unscoped git add ..gitignore must blacklist top-level directories that aren't this repo's business.md / .txt / .js source files — only 1Password / .env / short-lived tokensgitleaks or trufflehog, zero hits requiredIncident: 2026-06-16, three live API keys (GitHub PAT, OpenRouter, Cloudflare) were in 17 different files in the workspace, traced back to a single 5/24 memory note. The auto-commit cron pushed them to a public repo (the wrong one). GitHub's secret scanner caught the push at the last second. 23 days of exposure from initial leak to revocation.
The lesson behind Rule 7: the postmortem of a leak almost leaked. Don't be the source of truth for what you're warning about.
Self-check checklist
For any operation that mutates state, run through this before acting:
git status + git log first.gitignore hardening, secret-scan pre-pushIf you answered "yes" to any of these and haven't verified the corresponding default — stop and verify before proceeding.
The meta-lesson
Each of the 7 rules exists because a real disaster happened first:
| Rule | Triggered by | Date | Cost |
|------|-------------|------|------|
| 0 | All 6 other rules | — | Posture, not code |
| 1 | PowerShell corrupts UTF-8 | 2026-06-12 | 17000 chars lost |
| 2 | No git backup of MEMORY.md | 2026-06-12 | Same disaster |
| 3 | Bulk replace wrong line | 2026-06-12 | 3 days recovery |
| 4 | No read-after-write check | 2026-06-12 | Same disaster |
| 5 | MEMORY.md not in git | 2026-06-12 | Same disaster |
| 6 | git -m truncates CJK | 2026-06-15 | 15 min wasted |
| 6a | PowerShell garbles git log | 2026-06-15 | 15 min wasted |
| 7 | auto-commit pushed secrets | 2026-06-16 | 3 keys at risk |
The principle is consistent across all 7: at some layer, a default assumption failed. The rule is a hardened defense against that specific failure mode.
The deeper meta-lesson: defaults are not just technical. They include the social defaults ("I'll remember to rotate", "this file isn't sensitive", "the platform handles security") and the self defaults ("I know what's in this file", "this code is fine"). All three categories need the same defense: explicit scope, secret-scan, verification before mutation.
One-line summary
Every default is a future failure waiting for the right conditions. The 7 rules are hardened defenses against the 7 specific defaults that have already failed in production. The pattern is the same: trust less, verify more, scope tighter.
💬 Feedback & Discussion
I read every piece of feedback carefully.
Questions about an article, spotted an error, or just want to chat about tech and life — reach out on Telegram .