Pipeline Lessons — Aggregated from every app build. Each lesson includes the project it came from, the symptom, root cause, and verified fix.

Deploy & Build

General
#1
Commit Before Deploy

The deploy script runs git stash -u which removes untracked files (new blog posts, app HTML files) from the working tree. After stash, git diff HEAD~1..HEAD finds nothing, so the build skips the new content.

Fix: Always git add -A && git commit -m BEFORE running the deploy script. This prevents stash from capturing the new files.

General
#2
New Post Not Detected by Deploy Script

git diff --quiet used alone misses new files. The deploy script checks for changes with git diff HEAD~1..HEAD, but if new files were never committed (stashed away by the same script), the diff comes up empty and the build skips the new post.

Fix: Run git add -A && git diff --cached --quiet to verify new files are staged. Always commit changes before invoking the deploy script.

General
#3
Build in Stages, Not One Shot

Multi-step builds that try to do everything in one session hit tool call limits mid-generation. Half-written files produce broken script tags, missing initialization, wrong CSS classes.

Fix: Split into isolated stages: Generate → Verify → Update → Deploy. Each stage has its own tool budget. If a step introduces 3+ new systems, split into smaller steps.

Content Quality

General
#4
Post Word Count Gate: 400 Minimum

The pre-commit hook enforces 400-word minimum on body content. Posts under 400 words are blocked on deploy. The gate works as designed — it catches thin posts before they drag the blog's average below floor.

Fix: Add concrete examples or real-world scenarios to hit 400+. The "What Goes Wrong" or implementation-detail section is the best place to add depth. Never bypass with --no-verify — add words instead.

General
#5
heroImage: Empty String Breaks Build

The content schema defines heroImage as z.string().url().optional(). An empty string (heroImage: "") fails .url() validation and breaks the build. Omitting the field entirely is fine.

Fix: If image generation fails, strip the entire heroImage line from frontmatter. Never leave heroImage: "".

General
#6
Quality Ratchet False Positives on Frontmatter

The unsourced_claims_avg check flags raw numbers in YAML frontmatter description: fields — but frontmatter can't carry inline citations. Stats, percentages, and dollar amounts in descriptions trigger false alerts.

Fix: Strip raw stats/percentages/$ amounts from descriptions. Use prose summaries instead.

Pipeline

General
#7
Switch Model After 2 Failed Fixes

After 2+ failed attempts to fix a bug on the same step, the iteration tax exceeds the cost of switching to a stronger model. Each retry with the same model introduces new bugs.

Fix: After 2 failed rewrites, switch from Flash to Pro (3× cost << iteration tax of 4+ buggy rewrites). Once verified working, save lesson to skill.

General
#8
Backup Before Every Edit

Patching generated code is fragile — every patch assumes the file is clean. A broken patch can corrupt the entire file, requiring a full rebuild from scratch.

Fix: Save a cp backup to /tmp/ before any change (cp file.html /tmp/file.html.bak). One cp restores the last known-good state.

General
#9
Deploy Script Pipefail on Missing heroImage

deploy-verify-images.sh has set -euo pipefail. When a post has no heroImage: line, grep -m1 '^heroImage:' exits 1, and -o pipefail kills the entire script before reaching the [ -n "$URL" ] || continue guard.

Fix: Append || true to the grep-pipe: URL=$(grep ... | sed ... || true).

9 lessons