# 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.
