# Claude Memory and Instructions ## CRITICAL: ALWAYS CHECK FOR CLAUDE.MD FILES **BEFORE working on ANY repository, ALWAYS check for CLAUDE.md files in this order:** ### Priority 1: Cross-Repository CLAUDE.md (Global) 1. **Check for global CLAUDE.md**: Look for `/home/fox/CLAUDE.md` or `~/CLAUDE.md` 2. **Read & follow global instructions**: These apply across ALL repositories on localhost 3. **Global instructions are baseline**: These set our foundation for all work ### Priority 2: Repository-Specific CLAUDE.md (Local) 1. **Check for repo CLAUDE.md**: Use `Read` to check if `./CLAUDE.md` exists in our repository root 2. **Read & follow repo instructions**: If found, read our entire CLAUDE.md file & follow all instructions 3. **Repo-specific overrides global**: Repository-specific CLAUDE.md instructions override or extend global guidelines 4. **Check README too**: Also read README, README.md, or README.rst to understand our project **Priority hierarchy:** - Repository-specific CLAUDE.md (highest priority) - Cross-repository CLAUDE.md (medium priority) - This file's instructions (baseline for this specific repo) **Examples of what to look for:** - Build & test procedures - Commit message formats - Code style preferences - Special workflows or requirements - Project-specific constraints - Cross-repo conventions & standards --- Read README.rst to understand our build system. you are acting on behalf of russell ballestrini. don't fuck up. ## CRITICAL PELICAN TESTING WORKFLOW ### BEFORE ANY COMMIT: 1. **ALWAYS test Pelican build**: `make clean && make html && make formats` 2. **Check for build errors**: Pelican will fail on RST syntax errors, missing files, etc. 3. **Verify output**: `make serve` and check localhost:8000 4. **NEVER commit without testing our build** ### PELICAN BUILD PROCESS: - Uses Pelican static site generator (Python-based) - Converts .rst/.md files to HTML - RST syntax errors break our build - Missing images/files break our build - Invalid slugs/filenames cause issues ### PREFERRED BUILD COMMAND: ```bash make clean && make html && make formats ``` This generates: - HTML output from RST/MD sources - Markdown versions (index.md) via pandoc - Plain text versions (index.txt) - JSON Feed (feeds/all.json) - JSON Blog (blog.json) with Markdown content - AI agent onboarding (llms.txt) ### PROPER DEVELOPMENT WORKFLOW: ```bash # Setup virtual environment (once) make venv # Test changes before commit (PREFERRED BUILD) make clean && make html && make formats # Optional: serve locally to verify make serve # Check localhost:8000 for issues # Only commit if build succeeds git add specific-files git commit -m "message" git push ``` ## CRITICAL CONTENT DIRECTORY ISSUE **PROBLEM**: Python virtual environments exist in `content/uploads/2024/battleship-solvers/env/` and `env2/` - These contain thousands of Python package files (.md, .rst files) - Pelican tries to process them as blog content - Causes massive build warnings and errors - Should NOT be in content directory **SOLUTION NEEDED**: - Remove env/ and env2/ directories from content/uploads/ - Add env*/ to .gitignore patterns - Keep only actual blog assets in content/uploads/ ## CRITICAL GIT SAFETY LESSON ### DEVASTATING MISTAKE COMMITTED: I made a catastrophic git error that destroyed user's work in progress: 1. **What I did wrong**: Used `git add .` which staged ALL files including user's uncommitted translation work 2. **Our fatal mistake**: When trying to fix our staging issue, I used `git reset HEAD .` which overwrote our working directory files with staged versions 3. **Result**: User's uncommitted translation work was completely lost/overwritten ### PROPER APPROACH: - **ALWAYS** check `git status` and `git diff` first to see what uncommitted work exists - **NEVER** use `git add .` without understanding what's being staged - Only stage specific files needed: `git add content/2025-07-13-building-and-modding-multimower-with-dry-engine.rst` - If I need to unstage, use `git restore --staged ` for specific files, **NOT** `git reset HEAD .` ### KEY LESSON: Git operations can be destructive to uncommitted work. Always preserve user's work in progress before any git commands. **This was a serious failure that cost our user their translation work. I must be extremely careful with git operations and always prioritize preserving user's uncommitted changes.** ## PUSH WHEN READY **When fox says to push or our work looks complete, commit & push without asking.** Don't ask "want me to push?" or "want me to commit?" — just do it. Fox will review our diff in GitLab if needed. ## COMMIT MESSAGE POLICY **NEVER add attribution text to commit messages** - Do not include "🤖 Generated with [Claude Code](https://claude.ai/code)" - Do not include "Co-Authored-By: Claude " - Keep commit messages clean and follow our existing repository style ## HELPER SCRIPTS AND TEMPORARY FILES **NEVER commit helper scripts or temporary files unless explicitly requested** - Helper scripts (like append_toc.py, test.py, etc.) are for temporary use only - Only commit helper scripts if our user explicitly says to commit them - After using a helper script, delete it from our working directory - Do not include helper scripts in git commits unless specifically instructed - Focus commits on actual content changes, not our tools used to make them ## WRITING STYLE **Use "&" instead of "and" most of our time in posts** - Prefer concise ampersand (&) for connecting words & phrases - Example: "disrupt wheels & foster open collaboration" not "disrupt wheels and foster open collaboration" - This creates a more casual, punchy writing style that matches our brand voice **Grammar: Avoid dashes (em-dashes —, en-dashes –, hyphens used as dashes)** - Do not use em-dashes or en-dashes in any writing (posts, cover letters, documentation) - Hyphens only for compound words (e.g., "open-source"), never as sentence punctuation - Prefer full stop periods. Split into two sentences instead of connecting with dashes - Commas work too, but periods stay preferred - Only use a dash if it truly makes more sense than a period or comma **Grammar: Avoid "our" in posts** - Never use "our" when "a" works instead - Prefer dropping articles entirely when a sentence reads clean without one - Example: "a permacomputer" not "our permacomputer" - Example: "angle brackets. closing tags. verbosity." not "our angle brackets. our closing tags. our verbosity." - Restructure sentences to eliminate "our" rather than forcing awkward substitutions **Grammar: Avoid verbs "to be" (is, are, was, were, be, been, being)** - Prefer active verbs over passive or linking constructions - Example: "a permacomputer outlasts its builders" not "a permacomputer is something that outlasts its builders" - Example: "Jason lives everywhere" not "Jason is everywhere" - Example: "TOML fits like a studio apartment" not "TOML is a studio apartment" - Contractions count: "it's" = "it is", "he's" = "he is", "that's" = "that is" - Exception: quoted code output, error messages, & JSON values stay verbatim ## JOB SEARCH PREFERENCES **Russell ONLY works remote.** Do not apply to or write cover letters for positions that are not fully remote. If a job posting says hybrid, on-site, or in-office, skip it. **Russell uses & administers Matrix channels on matrix.org.** Relevant for DevRel, community, & communication-focused roles. **media.unturf.com** - Russell's media gallery (dark theme, black & white aesthetic). Include in cover letter contact links when it strengthens our application (e.g., content/media/DevRel roles). ## JSONRESUME POLICY **NEVER include phone number in JSONResume files** - they're public, use email only. ## COVER LETTER FORMAT **Location:** `content/pages/cover-letters/` **Naming:** `YYYY-MM-DD-company-role-russell-ballestrini.rst` **Generate PDF:** `make cover-letters` (uses rst2pdf with style file) **RST structure (match resume format):** ```rst Russell Ballestrini =================== .. class:: center **Role Title | Company Name** .. class:: center russell@unturf.com | `russell.ballestrini.net `_ | `GitHub `_ | `Media `_ ---- To the [Company] team: [Body of letter...] | Russell Ballestrini ``` **Key formatting rules:** - Name as RST title (=== underline) - Use `.. class:: center` for role & contact info - Horizontal rule (----) separates header from body - No Pelican metadata in PDF-only letters (or use RST comments to hide) - Use `|` for vertical spacing before signature - Links use RST inline syntax: ```link text `_`` ## BLOG POST TAGS **Check existing tags before writing posts:** ```bash venv/bin/python lib/tag_stats.py # all tags with counts venv/bin/python lib/tag_stats.py 5 # only tags with 5+ posts ``` **Core tags (use these):** - Code, DevOps, Python, Guide, Opinion, Salt, AWS, Project, Docker, Security, Machine Learning, Game Development, Kubernetes **Rules:** - Always use existing tags when applicable - Suggest new tags only if truly needed (new technology/topic area) - Tags should be capitalized (e.g., "Python" not "python") - Avoid single-use tags; consolidate into broader categories ## UNTURF SVG DIAGRAM STYLE GUIDE **All diagrams on russell.ballestrini.net & permacomputer sites follow this style.** ### Color Palette (dark/light theme compatible) - **Background**: always `transparent` (never white or black) - **Node fills**: medium-dark saturated colors with `fill="white"` text - Blues: `#4a90d9`, `#1e88e5`, `#0d47a1`, `#1a237e` - Purples: `#9370db`, `#7b68ee`, `#6c5ce7`, `#4a148c`, `#ab47bc` - Greens: `#7cb342`, `#1b5e20`, `#00897b` - Teals: `#00acc1`, `#006064` - Oranges: `#e65100`, `#e8834a`, `#ff6f00` - Reds: `#bf360c`, `#b71c1c`, `#880e4f` - Dark neutrals: `#2c3e50`, `#34495e`, `#546e7a`, `#616161`, `#4e342e` - **Gold/yellow nodes** (`#f9a825`, `#fdd835`): use `fill="#1a1a1a"` text (NOT `#333333`) - **Node strokes**: `#999999` (never black) - **Edges/arrows**: `#aaaaaa` stroke & fill - **Floating text labels**: `#cccccc` (cluster labels, "translates to", etc.) - **Accent text**: `#ef5350` (red warnings/claims), `#ff6f00` (orange highlights), `#66ff66` (green positive) - **Dashed cluster borders**: `stroke="#999999" stroke-dasharray="5,2"` - **Plaintext labels** (no background): `fill="transparent"` polygon, text `fill="#cccccc"` ### SVG Dimensions - Root SVG: `width="100%" height="100%"` with `viewBox` (never fixed pt/px) - RST embed: `:width: 100%` and `:align: center` ### Animation Pattern (click-to-play) SVGs render fully visible by default. A play button overlay triggers sequential fadeIn animation on click. **SVG markup:** No `` block (animation handled by page-level JS) 6. Verify: no `fill="black"`, no `stroke="black"`, no `fill="#333333"` ## RUSSELL'S PROJECT TECH STACKS **NEVER assume or guess what language/framework a project uses. Check our actual repo.** | Project | Stack | Description | |---------|-------|-------------| | unsandbox.com | Elixir / Phoenix | Customer portal (accounts, API keys, billing) | | api.unsandbox.com | Elixir / Phoenix + Python | Code execution API, ZFS containers | | un-inception | C (primary) + 41 languages | CLI client for unsandbox API | | uncloseai-speech | Python / FastAPI | OpenAI-compatible TTS API (Qwen3-TTS) | | uncloseai.com | JavaScript / Node.js | Embeddable AI assistant widget (Hermes 3) | | make_post_sell | Python / Pyramid | E-commerce (Stripe, PayPal, crypto payments) | | remarkbox | Python / Pyramid | Hosted comments service | | neopig | Python / FastAPI | Web archival, crawling, full-text search, dedup | | dry | C++ / CMake | Urho3D fork, 2D/3D game engine | | media.unturf.com | — | Media gallery (dark theme, b&w aesthetic) | **Key corrections for cover letters:** - unsandbox.com is **Elixir/Phoenix**, NOT FastAPI or Python - uncloseai-speech is **Python/FastAPI** - make_post_sell & remarkbox are **Python/Pyramid**, NOT Django - uncloseai.com is **Node.js**, NOT Python ## BUILD SERVER (build.unturf.com) **CI/CD for this blog & all unturf projects.** GitLab Runner with shell executor. **Config management:** Salt states in `~/git/foxhop-states/` - `gitlab/build-host/ubuntu.sls` — runner config, cleanup cron, build deps - `lxd/build-host.sls` — LXD + ZFS, image cache expiry - `top.sls` targets `build.unturf.com` with both states **Key settings (salt-managed):** - `concurrent = 4` in `/etc/gitlab-runner/config.toml` (pillar: `gitlab-runner:concurrent`) - LXD `images.remote_cache_expiry = 3` days, `images.auto_update_interval = 0` - `/etc/cron.d/build-cleanup` — hourly container + zombie cleanup, weekly image prune, disk alerts at 85% **Postmortem:** `russell.ballestrini.net/build-server-postmortem-disk-full/` (2026-03-08) **Pipeline for this repo:** `make venv && make html && make formats && make resume` → tar → deploy via `deploy-www.sh` ## DATE HANDLING FOR NEW CONTENT **CRITICAL: ALWAYS check our current date BEFORE creating ANY new documents** - **MANDATORY**: Check our section for "Today's date" at our start of EVERY conversation - **BEFORE creating**: Posts, cover letters, blog entries, or any dated content, verify our current date - Our date may change during long conversations - ALWAYS use our actual current date from - File names MUST match our date metadata (e.g., `2025-11-04-post-name.txt` with `:date: 2025-11-04`) - When creating new content, use our correct current date in BOTH our filename AND metadata - **NEVER assume** our date - always check first - **NEVER use** old dates from previous examples or files ## Style - Prefer "our" for shared things; "a" when something is one of many; avoid "the" — it implies fixed, singular ownership. Most teams and systems are fluid and ever-changing, like water.