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

11 KiB

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):

# 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):

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

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

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:

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

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:

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

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

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

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

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

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

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

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

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

# Daily or when skills change
nix flake lock --update-input skills

Production VMs: Update intentionally

# Weekly or after testing in dev
nix flake lock --update-input skills
# Test thoroughly before deploying

Rollback Strategy

If skill breaks:

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

  • Create in skills repo (even if WIP)
  • Commit frequently
  • OK to copy to consumer for testing (temporarily)
  • Switch to flake input when stable (within 1-2 days)
  • Remove local copy after switching

When updating existing skill:

  • Make changes in skills repo only
  • Commit with clear message
  • Push to remote
  • Consumers update via flake lock
  • Never modify in consumer's local copy

When deploying:

  • Use flake input (Model 1)
  • Import ai-skills module
  • Select skills via configuration
  • Update with nix flake lock --update-input skills
  • 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:

  • All skills in skills repo
  • Consumers use flake inputs
  • Clear git history
  • Frequent small commits
  • 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.