skills/docs/BEST-PRACTICES-single-source-of-truth.md
dan 5fea49b7c0 feat(tufte-press): evolve skill to complete workflow with JSON generation and build automation
- Transform tufte-press from reference guide to conversation-aware generator
- Add JSON generation from conversation context following strict schema
- Create build automation scripts with Nix environment handling
- Integrate CUPS printing with duplex support
- Add comprehensive workflow documentation

Scripts added:
- skills/tufte-press/scripts/generate-and-build.sh (242 lines)
- skills/tufte-press/scripts/build-card.sh (23 lines)

Documentation:
- Updated SKILL.md with complete workflow instructions (370 lines)
- Updated README.md with usage examples (340 lines)
- Created SKILL-DEVELOPMENT-STRATEGY-tufte-press.md (450 lines)
- Added worklog: 2025-11-10-tufte-press-skill-evolution.org

Features:
- Agent generates valid JSON from conversation
- Schema validation before build (catches errors early)
- Automatic Nix shell entry for dependencies
- PDF build via tufte-press toolchain
- Optional print with duplex support
- Self-contained margin notes enforced
- Complete end-to-end testing

Workflow: Conversation → JSON → Validate → Build → Print

Related: niri-window-capture, screenshot-latest, worklog skills
2025-11-10 15:03:44 -08:00

465 lines
11 KiB
Markdown

# Best Practices: Skills as Single Source of Truth
**Philosophy**: This repository is the canonical source for all AI agent skills. All deployments consume via Nix flake inputs.
## Core Principles
### 1. Skills Repo is Authoritative
**What this means**:
- All skill development happens in `dan/skills` repository
- No permanent local copies in consumer projects
- Changes must be committed here to be deployed
**Why**:
- Clear ownership and maintenance responsibility
- Version control provides audit trail
- Reproducible deployments across all environments
- Prevents drift and divergence
### 2. Modern Nix with Flakes
**What this means**:
- Use `flake.nix` for all configuration
- Consumers add skills repo as flake input
- Leverage flake.lock for reproducibility
- Use NixOS modules for deployment
**Why**:
- Declarative configuration
- Version pinning with flake.lock
- Reproducible builds
- Composable modules
### 3. Temporary Local Copies Only During Development
**What this means**:
- OK to copy skill to consumer for testing during initial development
- Must switch to flake input once skill is stable
- Duration: Hours to 1-2 days max, not weeks
- Always transition to production pattern (flake input)
**Why**:
- Fast iteration during development
- Clean production deployments
- Prevents forgetting to sync changes
- Clear distinction between dev and prod patterns
## Workflow Patterns
### Adding a New Skill
**Development phase** (1-2 days):
```bash
# 1. Create skill in skills repo
cd ~/proj/skills
mkdir -p skills/new-skill/{scripts,examples,templates}
# ... create SKILL.md, README.md, etc ...
# 2. Commit early (even if WIP)
git add skills/new-skill/
git commit -m "WIP: Add new-skill (initial structure)"
# 3. Optional: Copy to consumer for rapid testing
cp -r skills/new-skill ~/proj/ops-dev/skills/
# Test, iterate, modify in place
# 4. When working, sync back to skills repo
cp -r ~/proj/ops-dev/skills/new-skill skills/
git add skills/new-skill/
git commit -m "Complete new-skill implementation"
```
**Production phase** (permanent):
```bash
# 5. Add to flake packages (if not already)
# Edit flake.nix, add to packages output
# 6. Consumer switches to flake input
cd ~/proj/ops-dev
# Edit flake.nix:
# services.ai-skills.selectedSkills = [ "new-skill" ];
nix flake lock --update-input skills
# Deploy to VM
# 7. Remove local copy
rm -rf ~/proj/ops-dev/skills/new-skill
git add -A
git commit -m "Switch new-skill to flake input"
```
**Key point**: Don't skip step 6-7. Leaving skills as local copies defeats the purpose.
### Updating an Existing Skill
**Always in skills repo**:
```bash
# 1. Make changes in skills repo
cd ~/proj/skills
# Edit skills/tufte-press/SKILL.md or scripts
# 2. Commit with clear message
git commit -am "tufte-press: Add example for concurrent equations"
# 3. Push to remote (Forgejo)
git push origin master
# 4. Consumers update when ready
cd ~/proj/ops-dev
nix flake lock --update-input skills
# Deploy to VM
```
**Never**:
- ❌ Make changes in consumer's local copy
- ❌ Keep local modifications "just for us"
- ❌ Fork the skill for project-specific tweaks
**Instead**:
- ✅ Make changes in skills repo
- ✅ If change is project-specific, use configuration options
- ✅ If broadly useful, add to skills repo for everyone
### Testing Changes Before Committing
**Option A: Test in skills repo directly**:
```bash
cd ~/proj/skills
# Make changes
vim skills/worklog/scripts/suggest-filename.sh
# Test locally (if skill has test scripts)
./skills/worklog/scripts/suggest-filename.sh test-input.org
# Commit when working
git commit -am "worklog: Fix filename suggestion for long titles"
```
**Option B: Test in consumer with local override**:
```bash
# Temporarily point to local path for testing
cd ~/proj/ops-dev
# Edit flake.nix:
# inputs.skills.url = "path:/home/dan/proj/skills";
nix flake lock --update-input skills
# Deploy to VM and test
# When working, revert to git URL
# Edit flake.nix:
# inputs.skills.url = "git+http://192.168.1.108:3000/dan/skills.git";
nix flake lock --update-input skills
```
**Option C: Use a branch**:
```bash
cd ~/proj/skills
# Create feature branch
git checkout -b feature/worklog-improvements
# Make changes, commit
git commit -am "worklog: Add weekly summary aggregation"
git push origin feature/worklog-improvements
# Consumer tests the branch
cd ~/proj/ops-dev
# Edit flake.nix:
# inputs.skills.url = "git+http://192.168.1.108:3000/dan/skills.git?ref=feature/worklog-improvements";
nix flake lock --update-input skills
# Test on VM
# When stable, merge to master
cd ~/proj/skills
git checkout master
git merge feature/worklog-improvements
git push origin master
# Consumer switches back to master
cd ~/proj/ops-dev
# Edit flake.nix: back to master URL
nix flake lock --update-input skills
```
## Anti-Patterns (Don't Do This)
### ❌ Permanent Local Copies
**Wrong**:
```bash
# Consumer repo permanently has skills/ directory
ops-dev/
skills/
tufte-press/ # Local copy
worklog/ # Local copy
```
**Why wrong**:
- Must manually sync changes
- Risk of divergence
- Unclear which version is deployed
- Defeats purpose of single source of truth
**Right**:
```nix
# Consumer repo uses flake input
inputs.skills.url = "git+http://192.168.1.108:3000/dan/skills.git";
services.ai-skills.selectedSkills = [ "tufte-press" "worklog" ];
```
### ❌ Project-Specific Forks
**Wrong**:
```bash
# Creating modified copy for one project
ops-dev/skills/tufte-press-custom/
# Modified version just for ops-dev
```
**Why wrong**:
- Others can't benefit from improvements
- Maintenance burden (must merge upstream changes)
- Creates fragmentation
**Right**:
- Add configuration options to skill
- Make skill flexible via environment variables or config files
- Keep one version that works for everyone
### ❌ Manual File Copying
**Wrong**:
```bash
# Regularly doing this
scp skills/new-feature.sh root@vm:/etc/opencode/skills/worklog/scripts/
```
**Why wrong**:
- Bypasses version control
- Not reproducible
- No audit trail
- Breaks on next rebuild
**Right**:
```bash
# Commit to skills repo, update flake
cd ~/proj/skills
git commit -am "worklog: Add new feature"
git push
cd ~/proj/ops-dev
nix flake lock --update-input skills
# Deploy via nixos-rebuild
```
### ❌ Long-Lived Development Branches
**Wrong**:
```bash
# Branch exists for weeks/months
git checkout -b dan/experimental-features
# ... months pass ...
# Never merged, consumers stuck on old version
```
**Why wrong**:
- Others can't use improvements
- Becomes hard to merge later
- Defeats shared repository purpose
**Right**:
- Short-lived feature branches (days, not weeks)
- Merge to master frequently
- Use feature flags if needed for WIP features
- Or commit to master with "WIP" markers in docs
## Version Control Practices
### Commit Messages
**Good examples**:
```
tufte-press: Add support for margin figure citations
worklog: Fix filename generation for dates with slashes
screenshot-latest: Improve error handling when no screenshots found
niri-window-capture: Add security audit logging
```
**Pattern**: `<skill-name>: <brief description>`
**Why**: Clear which skill changed, easy to scan git log
### When to Commit
**Commit frequently**:
- ✅ After adding new script
- ✅ After fixing bug
- ✅ After updating documentation
- ✅ After testing confirms it works
**Don't wait for**:
- ❌ "Perfect" state (commit early, improve later)
- ❌ All skills to be updated (commit per-skill changes separately)
- ❌ "Big batch" of changes (many small commits better)
### Branching Strategy
**Simple approach**:
- `master` branch is always deployable
- Feature branches for significant changes
- Merge to master when working
- Delete branch after merge
**Example**:
```bash
# New skill
git checkout -b add-sql-formatter-skill
# ... work ...
git push origin add-sql-formatter-skill
# ... test ...
git checkout master
git merge add-sql-formatter-skill
git push origin master
git branch -d add-sql-formatter-skill
```
## Deployment Best Practices
### Pinning Versions
**Default: Track latest**:
```nix
inputs.skills.url = "git+http://192.168.1.108:3000/dan/skills.git";
# flake.lock tracks specific commit
# Update with: nix flake lock --update-input skills
```
**When to pin**: Production systems that need stability
```nix
inputs.skills.url = "git+http://192.168.1.108:3000/dan/skills.git?rev=abc123def";
# Locked to specific commit, won't change until manually updated
```
### Update Cadence
**Development VMs**: Update frequently
```bash
# Daily or when skills change
nix flake lock --update-input skills
```
**Production VMs**: Update intentionally
```bash
# Weekly or after testing in dev
nix flake lock --update-input skills
# Test thoroughly before deploying
```
### Rollback Strategy
**If skill breaks**:
```bash
# Option 1: Remove from selectedSkills temporarily
services.ai-skills.selectedSkills = [
"tufte-press"
# "worklog" # Disabled due to bug
];
# Option 2: Pin to older commit
cd ~/proj/ops-dev
nix flake lock --override-input skills git+http://.../skills.git?rev=<old-commit>
# Option 3: Fix in skills repo and update
cd ~/proj/skills
# Fix bug
git commit -am "worklog: Fix critical bug"
git push
cd ~/proj/ops-dev
nix flake lock --update-input skills
```
## Multi-User Coordination
### Communication
**Before making breaking changes**:
1. Announce in team chat/IRC
2. Create issue in Forgejo
3. Use feature branch for testing
4. Get feedback before merging
**After making significant changes**:
1. Update CHANGELOG in skill directory
2. Notify consumers
3. Document migration steps if needed
### Handling Conflicts
**If two people modify same skill**:
1. Coordinate via git (branches, PRs)
2. Use git merge/rebase to combine changes
3. Test combined changes before deploying
4. Modern git handles this well
### Shared vs Personal Skills
**Shared skills** (in skills repo):
- General-purpose capabilities
- Useful to multiple people/projects
- Maintained collaboratively
- Examples: tufte-press, worklog, screenshot-latest
**Personal skills** (project-local):
- Truly project-specific
- Not useful to others
- Rapid iteration needed
- Can live in `.opencode/command/` or `.claude/skills/` locally
**Default**: If in doubt, put in skills repo. Easy to share is better than hidden.
## Summary Checklist
**When developing new skill**:
- [x] Create in skills repo (even if WIP)
- [x] Commit frequently
- [x] OK to copy to consumer for testing (temporarily)
- [x] Switch to flake input when stable (within 1-2 days)
- [x] Remove local copy after switching
**When updating existing skill**:
- [x] Make changes in skills repo only
- [x] Commit with clear message
- [x] Push to remote
- [x] Consumers update via flake lock
- [x] Never modify in consumer's local copy
**When deploying**:
- [x] Use flake input (Model 1)
- [x] Import ai-skills module
- [x] Select skills via configuration
- [x] Update with `nix flake lock --update-input skills`
- [x] No manual file copying
**Red flags**:
- [ ] Permanent `skills/` directory in consumer repo
- [ ] Manual `scp` of skill files
- [ ] "Custom" versions of skills
- [ ] Long-lived local modifications
- [ ] Forgetting to sync changes back
**Good signs**:
- [x] All skills in skills repo
- [x] Consumers use flake inputs
- [x] Clear git history
- [x] Frequent small commits
- [x] Fast update cycle (commit → push → update → deploy)
---
**The goal**: Make skills as easy to use and update as possible, while maintaining single source of truth and version control.