russell.ballestrini.net/CLAUDE.md
russell@unturf.com 524542c33a update postmortem with salt state details, update CLAUDE.md
postmortem now documents permanent fixes via salt states,
added salt states section with top.sls targeting. CLAUDE.md
updated: build server docs, SVG animation pattern changed
from scroll-driven to click-to-play, post-processing checklist
updated.
2026-03-07 22:16:03 -05:00

337 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 the 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 the repository root
2. **Read & follow repo instructions**: If found, read the 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 the 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 the 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 the build**
### PELICAN BUILD PROCESS:
- Uses Pelican static site generator (Python-based)
- Converts .rst/.md files to HTML
- RST syntax errors break the build
- Missing images/files break the 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. **The fatal mistake**: When trying to fix the staging issue, I used `git reset HEAD .` which overwrote the 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 <file>` 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 the user their translation work. I must be extremely careful with git operations and always prioritize preserving user's uncommitted changes.**
## 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 <noreply@anthropic.com>"
- Keep commit messages clean and follow the 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 the user explicitly says to commit them
- After using a helper script, delete it from the working directory
- Do not include helper scripts in git commits unless specifically instructed
- Focus commits on actual content changes, not the tools used to make them
## WRITING STYLE
**Use "&" instead of "and" most of the 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 the 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 "the" in posts**
- Never use "the" when "a" works instead
- Prefer dropping articles entirely when a sentence reads clean without one
- Example: "angle brackets. closing tags. verbosity." not "the angle brackets. the closing tags. the verbosity."
- Restructure sentences to eliminate "the" 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: "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 the 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 <https://russell.ballestrini.net>`_ | `GitHub <https://github.com/russellballestrini>`_ | `Media <https://media.unturf.com>`_
----
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 <URL>`_``
## 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 `<style>` block needed. Just wrap groups in `<g class="anim dN">` where N controls sequence order.
**JS controller** (added once per page via `raw:: html`):
- Finds all SVGs with `.anim` elements, wraps each in a container, adds play button
- On click: hides all `.anim` groups (opacity 0), then animates them in sequence using `@keyframes svgFadeIn` with calculated delays based on `dN` class values
- Total animation spread scales to ~15s regardless of group count (`step = 15 / (maxD + 1)`)
- After animation completes, restores full opacity & shows play button again
**Previous approach (scroll-driven CSS view-timeline) was removed** because browser support proved unreliable. See royal-we post for the working click-to-play implementation.
- Wrap each conceptual group in `<g class="anim dN">`
- N values control sequence order (d0 appears first, d1 second, etc.)
- Multiple groups can share the same dN class to appear simultaneously
### Graphviz Dot Conventions
```dot
bgcolor="transparent"
node [fontname="Helvetica" fontsize=12 style="filled,rounded" shape=box color="#999999"]
edge [color="#AAAAAA" arrowsize=0.6 fontcolor="#AAAAAA"]
```
### Post-Processing Checklist
1. Strip `<title>` tags: `sed -i 's/<title>[^<]*<\/title>//g'` (prevents tooltip artifacts)
2. Replace fixed dimensions: `width="100%" height="100%"` keeping viewBox
3. Change cluster label fontcolor from `#aaaaaa` to `#cccccc`
4. Wrap groups with animation classes (`<g class="anim dN">`)
5. Remove `<defs><style>...</style></defs>` 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 the 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 the current date BEFORE creating ANY new documents**
- **MANDATORY**: Check the <env> section for "Today's date" at the start of EVERY conversation
- **BEFORE creating**: Posts, cover letters, blog entries, or any dated content, verify the current date
- The date may change during long conversations - ALWAYS use the actual current date from <env>
- File names MUST match the date metadata (e.g., `2025-11-04-post-name.txt` with `:date: 2025-11-04`)
- When creating new content, use the correct current date in BOTH the filename AND metadata
- **NEVER assume** the date - always check <env> first
- **NEVER use** old dates from previous examples or files