# Crash Test Dev -- Agent Instructions

## Standing Rules

### Voice Linting Is Mandatory Before Publishing

Every piece of written content (blog posts, articles, lessons, patterns) MUST
pass through the voice-check pipeline before it can be published. No exceptions.

**Before creating or modifying any content in `src/content/posts/`:**

1. **Draft the content**
2. **Run mechanical lint:** `./scripts/lint-voice.sh <file>`
   - Must have 0 FAILURES to proceed
   - Warnings are acceptable but should be acknowledged
3. **Run LLM judge:** Delegate to self with `model_role: "critique"` using the
   voice-check prompt at `.amplifier/skills/voice-check/SKILL.md`
   - Evaluates 8 dimensions: em-dash addiction, conviction posture, evidence
     instinct, narrative engine match, structural parallelism, opening quality,
     ending quality, personality presence
   - Must reach PUBLISH or REVISE verdict (not REWRITE)
4. **Fix all failures** before committing
5. **Re-run both checks** after fixes to confirm

**If the mechanical lint or LLM judge is skipped, the content is not published.**
This applies even if the user says "just publish it." Run the checks, report
the results, and let the user decide whether to fix issues or publish anyway
with full awareness of what's wrong.

### What the Checks Catch

**Mechanical lint (`scripts/lint-voice.sh`):**
- Em-dash density (>5 per post = FAIL, Claude fingerprint)
- AI trigger words (delve, tapestry, nuanced, landscape, etc.)
- Hedging phrases ("it's worth noting", "one might argue")
- Unnecessary negative contrast ("doesn't just X. They Y" -- Claude crutch)
- Filler transitions ("in conclusion", "that being said")
- Resolution closers ("the key takeaway", "at the end of the day")
- Missing receipts (0 links + 0 code + 0 URLs in 500+ words = WARN)
- Sentence length uniformity (>70% same length = WARN)

**LLM judge (voice-check skill):**
- Voice match against `.amplifier/VOICE.md` profile
- Narrative engine (essay mode = AI default, enemy-narrative = Ken's default)
- Structural parallelism (too-clean bullet lists, tricolon patterns)
- Opening quality (problem/hook vs generic framing)
- Ending quality (restating intro = anti-pattern)
- Personality presence (humor, asides, self-awareness)

### Voice Profile Reference

Ken's voice profile lives at `.amplifier/VOICE.md`. Key traits:
- **Conviction:** High, earned. States conclusions, doesn't hedge.
- **Evidence:** Receipt-shower. Links, code, data. Non-negotiable.
- **Humor:** Playful escalation + dry observation, sparse.
- **Sentence rhythm:** Short/long alternation, not uniform.
- **Narrative:** Enemy-narrative diagnostic is the default blog engine.
- **Endings:** End when done. Never restate the intro.

### Em-Dash Budget

Ken's real writing uses 0-2 em-dashes per 1000 words. Claude defaults to
10-15 per 1000. The budget for this blog:

- **0-2 per post:** Clean
- **3-5 per post:** Acceptable, review each one
- **6+:** FAIL. Kill at least half. Replace with periods, colons, or restructure.

When you find yourself typing an em-dash, stop and ask: would a period work here?
It almost always does.

### Visualization Pass Is Mandatory Before Publishing

Every post gets a visualization pass. The question is not "does this post need
a diagram?" The question is "what would illustrate this better than prose?"

**For every section of a post, ask:**

1. Am I reaching for a bullet list? A diagram might replace it entirely.
   Bullet lists are a Claude default. Ken's real writing uses prose or visuals,
   not lists. If you have 3+ items that relate spatially or sequentially, draw
   them instead of listing them.

2. Am I describing a system, flow, or architecture? Draw it. Code an inline
   SVG following the diagram design spec, or use a `<Diagram>` component.

3. Am I comparing two things? A two-column visual or a before/after diagram
   beats a paragraph explaining the contrast.

4. Am I showing a progression or timeline? A flow diagram with labeled steps
   beats a numbered list every time.

5. Would a concept illustration anchor the section? Even a simple symbolic
   SVG (like the masterclass philosophy cards: hub-and-spoke, concentric
   circles, crossed-out icons) gives the reader a visual anchor.

**What the visualization pass produces:**
- A list of sections that would benefit from diagrams
- For each: what type (architecture, flow, comparison, concept) and a brief
  description of what the SVG should show
- Generate the SVGs inline using the diagram design spec (when it exists)
  or flag them for the author to describe

**The rule:** If a post has 1000+ words and zero visuals, that's a warning.
Not every post needs diagrams, but every post should have been *considered*
for them. The consideration is mandatory. The diagrams are not.

**How visuals are made:**
- Claude hand-codes SVGs using primitives (`<rect>`, `<line>`, `<text>`,
  `<circle>`) following the diagram design spec constraints
- Use the warm palette from DESIGN.md (paper background, brown-black ink,
  terracotta accent)
- Diagrams break wider than prose (840px vs 720px) using the code-block
  breakout pattern
- For interactive architecture graphs: DOT source + d3-graphviz runtime
- For concept art / visual targets: use image generation, then code SVG
  approximations toward the target

## Project Context

- **Site:** Crash Test Dev (Astro blog at blog.ampbox.io / kenotron.github.io/crashtestdev)
- **Author:** Ken Chau (kenotron on GitHub, @kenneth_chau on Twitter)
- **Content types:** Posts (war stories), Patterns (reference), Lessons (progressive)
- **Design system:** DESIGN.md at project root (Google DESIGN.md spec-compliant)
- **Aesthetic:** "Workbench, not gallery" -- warm, grounded, un-digital
- **Voice profile:** .amplifier/VOICE.md
- **Design specs:** .design/AESTHETIC-GUIDE.md, .design/specs/

## Content Pipeline

```
Idea / source material
  |
  v
Plan visuals FIRST: what diagrams, animations, or visuals
does this piece need? Static SVG? Animated (Remotion)?
Interactive (d3-graphviz)? Plan them alongside the outline,
not as an afterthought.
  |
  v
Draft post (src/content/posts/slug.md)
  |
  v
Mechanical lint: ./scripts/lint-voice.sh <file>
  |
  v
LLM judge: voice-check (8 dimensions)
  |
  v
Visualization pass: what would illustrate this better than prose?
  |
  v
Fix failures, add diagrams, re-check
  |
  v
Rebuild preview: rebuild /tmp/blog-dist, serve on :4321
  |
  v
Visual check at blog.ampbox.io
  |
  v
Commit + push
```

## Content Organization: Book, Not Blog

The site is organized as a **book of investigations**, not a reverse-chronological
feed. Inspired by makingsoftware.com. Key principles:

**Topics, not dates.** Content is grouped by investigation theme (e.g., "Rage:
Rebuilding the Build System" is a 5-post series, not 5 unrelated entries). Dates
are secondary metadata, not the primary navigation.

**The homepage is a pitch, not an index.** Lead with a compelling excerpt, a
diagram, a question that creates intrigue. Show the reader what the content
quality is like before presenting any navigation. A flat list of article cards
is a cop-out. The homepage should make you curious.

**Series are first-class.** Multi-post series (like the Rage build system series)
get their own landing page with a visual overview, progress indication, and
cross-links between posts. Individual posts link to their series context.

**Figure density matters.** Target 1 visual per 300-500 words for technical posts.
Not decoration, but content: architecture diagrams, flow charts, before/after
comparisons, concept illustrations. If 500+ words pass without a visual, ask
what would replace them.

**Lead with outcomes, not internals.** Nobody cares about a fingerprinting
algorithm. They care that their builds stop lying. Nobody cares about ld.so
analogies. They care about 50% less CDN bandwidth. The mechanism is the proof,
not the headline. Name the result in the title. Explain the how in the body.

**No blog chrome.** No comments, no share buttons, no "related posts" sidebar,
no newsletter popups, no social proof widgets. The content quality is the
credibility signal. If it needs a "clap" button, the writing isn't good enough.

## Current Content Plan: Rage Series

Source: kenotron-ms/rage repo + ~/workspace/family/Work/Reference/BuildXL spec

**Lead with outcomes, explain internals as the "how."** Nobody cares about a
fingerprinting algorithm. They care that their builds stop lying. The mechanism
is the proof, not the headline.

| # | Title | Angle | Visuals Needed |
|---|-------|-------|----------------|
| 1 | Your Build Cache Is Lying to You | The silent stale cache problem. Make it concrete. | Flow: declared vs observed inputs. Before/after diagram of cache miss. |
| 2 | How We Made the Cache Stop Lying | The fix: observed inputs replace declared ones. Two-phase fingerprinting is the mechanism. | Architecture: the two-phase lookup flow. Code walkthrough diagrams. |
| 3 | Zero-Config File Watching Across Three Operating Systems | You don't configure what files to watch. The sandbox just watches. How: three OS-specific hooks. | Side-by-side: macOS interpose / Linux eBPF / Windows Detours. Animated (Remotion): syscall interception flow. |
| 4 | TypeScript 7 Is 10x Faster. Your Builds Aren't. | tsgo is 10x faster on one machine. Builds don't run on one machine. The bottleneck moved. | Comparison: single-machine vs distributed. The parallelism table as a visual. |
| 5 | 90 Seconds to 300 Milliseconds: Fixing CI Install Times | Per-package content-addressed storage. 1 miss + 1,499 hardlinks instead of re-downloading everything. | Flow: lockfile → CAS → hardlink restore. Before/after CI times. |
| 6 | The Architecture Mistake I Made in Lage | File access was an afterthought, not a primitive. What that cost, and what starting over taught me. | Timeline: lage → ceiling → rage. Before/after architecture diagram. |

Each post stands alone but gains context from the series. The series landing page
shows progress (like makingsoftware.com's progress bar) and creates anticipation
for upcoming posts.

## Current Content Plan: Cloudpack Series

Source: ~/workspace/wundler/ (kenotron-ms/cloudpack on GitHub) + architect brief + exec summary

**The outcome:** 604ms warm rebuilds at 50k modules. 50% less CDN bandwidth per
deploy. Dead code that actually stays dead.

**The novel concept:** "The module graph is the software. The bundle is a query."
Traditional bundlers are compilers: source in, file out, start over next time.
Cloudpack is a database: the module graph is the schema, bundle requests are
queries, and every output form (dev server, CI artifact, production chunks, ABS
delta manifest) is a materialization of the same graph. One truth, many views.
No separate dev/prod pipeline. No divergence. The graph is the truth. Files are
derived views. This concept is the unifying thread of the entire series.

| # | Title | Angle | Visuals Needed |
|---|-------|-------|----------------|
| 1 | 604ms Rebuilds at 50,000 Modules | The outcome first. Then how: the bundle is a query, not a file. Linker innovations (ThinLTO, mold, BOLT) mapped to JS. | Architecture: the 3-phase pipeline. Before/after: traditional bundler vs cloudpack. |
| 2 | 50KB Down to 2KB: Why We Stopped Parsing Source Files | 25x compression. The module summary replaces full parsing. Content-addressed cache means unchanged files cost zero. | Diagram: source (50KB) → summary (2KB). The parallel loop. Memory budget at 50k modules. |
| 3 | We Shipped 40% Less JavaScript and Nobody Noticed | Two-layer tree-shaking that actually works. Module-level conservative + function-level aggressive via call-edge graph. | Flow: reachability → SCC → DCE walk. Before/after bundle sizes. |
| 4 | 50% Less Bandwidth Per Deploy | Users re-download the whole bundle when 1% changed. Delta manifests fix this. Browser says what it has, server returns what's missing. | Architecture: delta computation flow. Before/after CDN bandwidth. |
| 5 | Your Users Are Already Telling You How to Bundle | Real browser sessions drive chunk grouping. No rebuild required. The optimization loop runs on live traffic. | The feedback loop: sessions → co-request matrix → clustering → manifest hints. |
| 6 | A Compromised Server Can't Inject Code | Ed25519 signing of chunk hashes. The threat model: what an attacker can and can't do if they own the manifest server. | Security model diagram: what's signed vs what's advisory. Attack surface comparison. |

Key numbers for receipts:
- 10k modules warm rebuild: 604ms analysis-only
- 50k modules in memory as summaries: ~100MB (fits L3 cache)
- ABS delivery: ~50% CDN bandwidth saved per deploy
- 50k modules, 10k DAU, 1% weekly churn: saves ~47 GB/week

## Rage Series: Author's Motivation

The real story behind rage is not "I rewrote lage in Rust." It's: "I've wanted
to combine lage's developer ergonomics with BuildXL's correctness at scale for
years, and I finally got to do it." Lage gives you the dev experience. BuildXL
gives you the guarantees. They've never existed in the same tool. Rage is the
synthesis Ken has been thinking about since he co-created lage. Frame the series
around that tension: ergonomics vs. correctness, and how you get both.

## Cloudpack: Benchmarking With Synthetic Data

Cloudpack benchmarks against synthetic codebases generated from observed
attributes of real shipping code. The pipeline:

1. **Observe**: `wundler-bench` scans a real repo and produces a `BenchProfile`
   JSON capturing its statistical fingerprint: file counts by extension,
   average file sizes, directory distribution, package structure.

2. **Generate**: `corpus_gen` creates a deterministic synthetic codebase matching
   the profile. Same seed = byte-identical output. TypeScript modules are
   generated in three variants (barrel, intermediate, leaf) with realistic
   import structures, side effects, and ambient refs. JSON configs, markdown
   docs, and fallback files fill out the shape.

3. **Verify**: `corpus_verify` confirms the generated corpus matches the profile
   within tolerances (default: 2% file count, 5% byte/line count).

4. **Benchmark**: Run the pipeline against the synthetic corpus at various
   scales (100, 1k, 5k, 10k, 50k modules) and measure.

This approach solves the "we can't share the real codebase" problem. The
synthetic corpus has the same statistical properties (file sizes, dependency
fan-out, side-effect density) as the real Teams codebase without containing
any proprietary code. The benchmarks are reproducible, scalable, and public.

This is worth a blog post on its own: "How We Benchmark a Bundler Without
Sharing the Codebase." The profile schema, the archetype generators (barrel
vs intermediate vs leaf modules), the determinism guarantees (same seed =
identical bytes), and the conformance checks are all interesting.
