Skip to content

Latest commit

 

History

History
649 lines (507 loc) · 21.7 KB

File metadata and controls

649 lines (507 loc) · 21.7 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Repository Overview

This is a NixOS/Home Manager dotfiles repository using the Denix framework for modular configuration management. It supports multiple platforms (NixOS, macOS via nix-darwin, Home Manager) with unified configuration.

Common Tools

Use the nixos and context7 mcp for background info (both of these should be used atleast 3 times anytime you edit a nix file)

Development Commands

All commands should be run using nix develop -c <command> to ensure the proper shell environment is loaded.

  • nix develop -c lint - Run linting tools (statix, deadnix, nix flake check) (if on nixos, should run nixos-rebuild build --flake . for better results)
  • nix develop -c dx - Edit the flake.nix file
  • nix fmt - Format code using treefmt

Testing

  • Individual program tests: cd modules/programs/<program-name> && nix build to test custom program builds
  • convert_img tests: cd modules/programs/convert_img && python -m pytest tests/
  • if you edit any program in modules/programs/, you should run the edited program manually to ensure it works in addition to the tests.

Installation/Rebuild

# macOS
darwin-rebuild switch --flake . --show-trace

# Linux (Home Manager only)
nix build .#homeConfigurations.x86_64-linux.activationPackage

# NixOS
nixos-rebuild build --flake .

Templates

Create development shells with:

nix flake init -t github:connerohnesorge/dotfiles#<template-name>

Available templates: devshell, rust-shell, go-shell, go-templ-shell, remix-js-shell, laravel-shell, phoenix-shell

Debugging and Troubleshooting

  • nix flake check - Validate flake outputs and check for errors
  • nix eval .#nixosConfigurations.<hostname>.config.system.build.toplevel - Check NixOS configuration evaluation
  • nix show-derivation - Inspect derivation details for debugging builds
  • nixos-rebuild build --flake . --show-trace - Build without switching to catch configuration errors

Architecture

Module System Overview

The repository uses the Denix framework for modular configuration management, providing type-safe, composable modules with automatic discovery and loading.

Module Types and Structure

Configuration Modules (modules/config/):

  • constants.nix - Read-only user constants (username, email, etc.)
  • user.nix - User account configuration for NixOS and Darwin
  • hosts.nix - Host type definitions and feature mapping system
  • args.nix - Shared arguments between nixos and home-manager configurations
  • home.nix - Home Manager configuration patterns
  • rices.nix - Theme system configuration

Feature Modules (modules/features/):

  • System-level capabilities that can be enabled per-host
  • Use delib.module with singleEnableOption false pattern
  • Platform-specific sections: nixos.ifEnabled, darwin.ifEnabled, home.ifEnabled
  • Examples: engineer.nix, hyprland.nix, nvidia.nix, audio.nix, bluetooth.nix

Custom Program Modules (modules/programs/):

  • Self-contained applications with source code and build expressions
  • Cross-platform deployment support (nixos/darwin)
  • Examples: catls/ (Ruby), cmbd/ (Go), convert_img/ (Python), dx/ (shell script)

Host Configurations (hosts/):

  • Use delib.host with type classification (desktop/laptop/server)
  • Feature enablement through myconfig.features.*
  • Platform-specific configuration sections

Theme Configurations (rices/):

  • Use delib.rice with Stylix integration
  • Consistent theming across applications using Base16 color schemes

Denix Framework Patterns

Module Creation:

delib.module {
  name = "feature-name";
  options.myconfig.features.featureName = singleEnableOption false;
  nixos.ifEnabled = { /* NixOS config */ };
  darwin.ifEnabled = { /* macOS config */ };
  home.ifEnabled = { /* Home Manager config */ };
}

Host Configuration:

delib.host {
  type = "desktop"; # or "laptop", "server"
  features = { featureName = true; };
  rice = "dark";
  nixos = { /* NixOS-specific config */ };
  darwin = { /* Darwin-specific config */ };
}

Custom Program Packaging:

delib.module {
  name = "program-name";
  nixos.ifEnabled.environment.systemPackages = [ pkgs.program-name ];
  darwin.ifEnabled.environment.systemPackages = [ pkgs.program-name ];
}

Module Discovery and Loading

The flake.nix uses Denix's auto-discovery system:

denix.lib.configurations {
  homeManagerUser = "connerohnesorge";
  paths = [./hosts ./modules ./rices]; # Auto-discovery paths
}

This automatically creates:

  • nixosConfigurations
  • homeConfigurations
  • darwinConfigurations

Feature Dependencies

Features automatically enable required programs:

  • engineer.enable = true → enables development tools (dx, convert_img, etc.)
  • hyprland.enable = true → enables Wayland desktop with supporting tools
  • Dependencies resolved through the Denix module system

Multi-Platform Support

Conditional Configuration: Separate platform sections in modules Shared Configuration: Maximize reuse between platforms where possible Platform Detection: Automatic handling of nixos vs darwin differences

Important Files

  • flake.nix - Main entry point with system configurations
  • modules/config/constants.nix - User constants (username, email, etc.)
  • modules/config/hosts.nix - Host type definitions and feature mappings
  • shell.nix - Development environment with custom scripts

Development Notes

Working with Modules

Creating New Feature Modules:

  1. Create a new .nix file in modules/features/
  2. Use the delib.module pattern with singleEnableOption false
  3. Define platform-specific configuration in nixos.ifEnabled, darwin.ifEnabled, home.ifEnabled sections
  4. Features are automatically discovered and can be enabled in host configurations

Creating Custom Program Modules:

  1. Create a directory in modules/programs/ with source code and default.nix
  2. Define the package derivation with cross-platform support
  3. Export the module using delib.module pattern
  4. Programs are automatically available after rebuild

Modifying Host Configurations:

  • Edit files in hosts/ to enable/disable features
  • Use myconfig.features.featureName = true to enable features
  • Platform-specific settings go in nixos or darwin sections

Best Practices

  • Module Isolation: Each module should be self-contained with minimal external dependencies
  • Platform Awareness: Always consider both nixos and darwin when creating modules
  • Feature Dependencies: Let the Denix system handle automatic dependency resolution
  • Theme Integration: Use Stylix-compatible configuration for consistent theming
  • Type Safety: Leverage Nix's type system through proper option definitions

Templates and Development

Templates provide isolated development environments for different languages/frameworks:

  • Each template is a complete flake.nix with development dependencies
  • Use for project-specific development without affecting system configuration
  • Available for: Go, Rust, Elixir/Phoenix, Laravel, Remix.js, Lua, and general devshell

Spectr Instructions

Instructions for AI coding assistants using Spectr for spec-driven development.

TL;DR Quick Checklist

  • Search existing work: spectr spec list --long, spectr list (use rg only for full-text search)
  • Decide scope: new capability vs modify existing capability
  • Pick a unique change-id: kebab-case, verb-led (add-, update-, remove-, refactor-)
  • Scaffold: proposal.md, tasks.md, design.md (only if needed), and delta specs per affected capability
  • Write deltas: use ## ADDED|MODIFIED|REMOVED|RENAMED Requirements; include at least one #### Scenario: per requirement
  • Validate: spectr validate [change-id] --strict and fix issues
  • Request approval: Do not start implementation until proposal is approved

Three-Stage Workflow

Stage 1: Creating Changes

Create proposal when you need to:

  • Add features or functionality
  • Make breaking changes (API, schema)
  • Change architecture or patterns
  • Optimize performance (changes behavior)
  • Update security patterns

Triggers (examples):

  • "Help me create a change proposal"
  • "Help me plan a change"
  • "Help me create a proposal"
  • "I want to create a spec proposal"
  • "I want to create a spec"

Loose matching guidance:

  • Contains one of: proposal, change, spec
  • With one of: create, plan, make, start, help

Skip proposal for:

  • Bug fixes (restore intended behavior)
  • Typos, formatting, comments
  • Dependency updates (non-breaking)
  • Configuration changes
  • Tests for existing behavior

Workflow

  1. Review spectr/project.md, spectr list, and spectr list --specs to understand current context.
  2. Choose a unique verb-led change-id and scaffold proposal.md, tasks.md, optional design.md, and spec deltas under spectr/changes/<id>/.
  3. Draft spec deltas using ## ADDED|MODIFIED|REMOVED Requirements with at least one #### Scenario: per requirement.
  4. Run spectr validate <id> --strict and resolve any issues before sharing the proposal.

Stage 2: Implementing Changes

Track these steps as TODOs and complete them one by one.

  1. Read proposal.md - Understand what's being built
  2. Read design.md (if exists) - Review technical decisions
  3. Read tasks.md - Get implementation checklist
  4. Implement tasks sequentially - Complete in order
  5. Confirm completion - Ensure every item in tasks.md is finished before updating statuses
  6. Update checklist - After all work is done, set every task to - [x] so the list reflects reality
  7. Approval gate - Do not start implementation until the proposal is reviewed and approved

Stage 3: Archiving Changes

After deployment, create separate PR to:

  • Move changes/[name]/changes/archive/YYYY-MM-DD-[name]/
  • Update specs/ if capabilities changed
  • Use spectr archive <change-id> --skip-specs --yes for tooling-only changes (always pass the change ID explicitly)
  • Run spectr validate --strict to confirm the archived change passes checks

Before Any Task

Context Checklist:

  • Read relevant specs in specs/[capability]/spec.md
  • Check pending changes in changes/ for conflicts
  • Read spectr/project.md for conventions
  • Run spectr list to see active changes
  • Run spectr list --specs to see existing capabilities

Before Creating Specs:

  • Always check if capability already exists
  • Prefer modifying existing specs over creating duplicates
  • Use spectr show [spec] to review current state
  • If request is ambiguous, ask 1–2 clarifying questions before scaffolding

Search Guidance

  • Enumerate specs: spectr spec list --long (or --json for scripts)
  • Enumerate changes: spectr list (or spectr change list --json - deprecated but available)
  • Show details:
    • Spec: spectr show <spec-id> --type spec (use --json for filters)
    • Change: spectr show <change-id> --json --deltas-only
  • Full-text search (use ripgrep): rg -n "Requirement:|Scenario:" spectr/specs

Quick Start

CLI Commands

# Essential commands
spectr list                  # List active changes
spectr list --specs          # List specifications
spectr show [item]           # Display change or spec
spectr validate [item]       # Validate changes or specs
spectr archive <change-id> [--yes|-y]   # Archive after deployment (add --yes for non-interactive runs)

# Project management
spectr init [path]           # Initialize Spectr
spectr update [path]         # Update instruction files

# Interactive mode
spectr show                  # Prompts for selection
spectr validate              # Bulk validation mode

# Debugging
spectr show [change] --json --deltas-only
spectr validate [change] --strict

Command Flags

  • --json - Machine-readable output
  • --type change|spec - Disambiguate items
  • --strict - Comprehensive validation
  • --no-interactive - Disable prompts
  • --skip-specs - Archive without spec updates
  • --yes/-y - Skip confirmation prompts (non-interactive archive)

Directory Structure

spectr/
├── project.md              # Project conventions
├── specs/                  # Current truth - what IS built
│   └── [capability]/       # Single focused capability
│       ├── spec.md         # Requirements and scenarios
│       └── design.md       # Technical patterns
├── changes/                # Proposals - what SHOULD change
│   ├── [change-name]/
│   │   ├── proposal.md     # Why, what, impact
│   │   ├── tasks.md        # Implementation checklist
│   │   ├── design.md       # Technical decisions (optional; see criteria)
│   │   └── specs/          # Delta changes
│   │       └── [capability]/
│   │           └── spec.md # ADDED/MODIFIED/REMOVED
│   └── archive/            # Completed changes

Creating Change Proposals

Decision Tree

New request?
├─ Bug fix restoring spec behavior? → Fix directly
├─ Typo/format/comment? → Fix directly
├─ New feature/capability? → Create proposal
├─ Breaking change? → Create proposal
├─ Architecture change? → Create proposal
└─ Unclear? → Create proposal (safer)

Proposal Structure

  1. Create directory: changes/[change-id]/ (kebab-case, verb-led, unique)

  2. Write proposal.md:

# Change: [Brief description of change]

## Why
[1-2 sentences on problem/opportunity]

## What Changes
- [Bullet list of changes]
- [Mark breaking changes with **BREAKING**]

## Impact
- Affected specs: [list capabilities]
- Affected code: [key files/systems]
  1. Create spec deltas: specs/[capability]/spec.md
## ADDED Requirements
### Requirement: New Feature
The system SHALL provide...

#### Scenario: Success case
- **WHEN** user performs action
- **THEN** expected result

## MODIFIED Requirements
### Requirement: Existing Feature
[Complete modified requirement]

## REMOVED Requirements
### Requirement: Old Feature
**Reason**: [Why removing]
**Migration**: [How to handle]

If multiple capabilities are affected, create multiple delta files under changes/[change-id]/specs/<capability>/spec.md—one per capability.

  1. Create tasks.md:
## 1. Implementation
- [ ] 1.1 Create database schema
- [ ] 1.2 Implement API endpoint
- [ ] 1.3 Add frontend component
- [ ] 1.4 Write tests
  1. Create design.md when needed: Create design.md if any of the following apply; otherwise omit it:
  • Cross-cutting change (multiple services/modules) or a new architectural pattern
  • New external dependency or significant data model changes
  • Security, performance, or migration complexity
  • Ambiguity that benefits from technical decisions before coding

Minimal design.md skeleton:

## Context
[Background, constraints, stakeholders]

## Goals / Non-Goals
- Goals: [...]
- Non-Goals: [...]

## Decisions
- Decision: [What and why]
- Alternatives considered: [Options + rationale]

## Risks / Trade-offs
- [Risk] → Mitigation

## Migration Plan
[Steps, rollback]

## Open Questions
- [...]

Spec File Format

Critical: Scenario Formatting

CORRECT (use #### headers):

#### Scenario: User login success
- **WHEN** valid credentials provided
- **THEN** return JWT token

WRONG (don't use bullets or bold):

- **Scenario: User login****Scenario**: User login     ❌
### Scenario: User login      ❌

Every requirement MUST have at least one scenario.

Requirement Wording

  • Use SHALL/MUST for normative requirements (avoid should/may unless intentionally non-normative)

Delta Operations

  • ## ADDED Requirements - New capabilities
  • ## MODIFIED Requirements - Changed behavior
  • ## REMOVED Requirements - Deprecated features
  • ## RENAMED Requirements - Name changes

Headers matched with trim(header) - whitespace ignored.

When to use ADDED vs MODIFIED

  • ADDED: Introduces a new capability or sub-capability that can stand alone as a requirement. Prefer ADDED when the change is orthogonal (e.g., adding "Slash Command Configuration") rather than altering the semantics of an existing requirement.
  • MODIFIED: Changes the behavior, scope, or acceptance criteria of an existing requirement. Always paste the full, updated requirement content (header + all scenarios). The archiver will replace the entire requirement with what you provide here; partial deltas will drop previous details.
  • RENAMED: Use when only the name changes. If you also change behavior, use RENAMED (name) plus MODIFIED (content) referencing the new name.

Common pitfall: Using MODIFIED to add a new concern without including the previous text. This causes loss of detail at archive time. If you aren't explicitly changing the existing requirement, add a new requirement under ADDED instead.

Authoring a MODIFIED requirement correctly:

  1. Locate the existing requirement in spectr/specs/<capability>/spec.md.
  2. Copy the entire requirement block (from ### Requirement: ... through its scenarios).
  3. Paste it under ## MODIFIED Requirements and edit to reflect the new behavior.
  4. Ensure the header text matches exactly (whitespace-insensitive) and keep at least one #### Scenario:.

Example for RENAMED:

## RENAMED Requirements
- FROM: `### Requirement: Login`
- TO: `### Requirement: User Authentication`

Troubleshooting

Common Errors

"Change must have at least one delta"

  • Check changes/[name]/specs/ exists with .md files
  • Verify files have operation prefixes (## ADDED Requirements)

"Requirement must have at least one scenario"

  • Check scenarios use #### Scenario: format (4 hashtags)
  • Don't use bullet points or bold for scenario headers

Silent scenario parsing failures

  • Exact format required: #### Scenario: Name
  • Debug with: spectr show [change] --json --deltas-only

Validation Tips

# Always use strict mode for comprehensive checks
spectr validate [change] --strict

# Debug delta parsing
spectr show [change] --json | jq '.deltas'

# Check specific requirement
spectr show [spec] --json -r 1

Happy Path Script

# 1) Explore current state
spectr spec list --long
spectr list
# Optional full-text search:
# rg -n "Requirement:|Scenario:" spectr/specs
# rg -n "^#|Requirement:" spectr/changes

# 2) Choose change id and scaffold
CHANGE=add-two-factor-auth
mkdir -p spectr/changes/$CHANGE/{specs/auth}
printf "## Why\\n...\\n\\n## What Changes\\n- ...\\n\\n## Impact\\n- ...\\n" > spectr/changes/$CHANGE/proposal.md
printf "## 1. Implementation\\n- [ ] 1.1 ...\\n" > spectr/changes/$CHANGE/tasks.md

# 3) Add deltas (example)
cat > spectr/changes/$CHANGE/specs/auth/spec.md << 'EOF'
## ADDED Requirements
### Requirement: Two-Factor Authentication
Users MUST provide a second factor during login.

#### Scenario: OTP required
- **WHEN** valid credentials are provided
- **THEN** an OTP challenge is required
EOF

# 4) Validate
spectr validate $CHANGE --strict

Multi-Capability Example

spectr/changes/add-2fa-notify/
├── proposal.md
├── tasks.md
└── specs/
    ├── auth/
    │   └── spec.md   # ADDED: Two-Factor Authentication
    └── notifications/
        └── spec.md   # ADDED: OTP email notification

auth/spec.md

## ADDED Requirements
### Requirement: Two-Factor Authentication
...

notifications/spec.md

## ADDED Requirements
### Requirement: OTP Email Notification
...

Best Practices

Simplicity First

  • Default to <100 lines of new code
  • Single-file implementations until proven insufficient
  • Avoid frameworks without clear justification
  • Choose boring, proven patterns

Complexity Triggers

Only add complexity with:

  • Performance data showing current solution too slow
  • Concrete scale requirements (>1000 users, >100MB data)
  • Multiple proven use cases requiring abstraction

Clear References

  • Use file.ts:42 format for code locations
  • Reference specs as specs/auth/spec.md
  • Link related changes and PRs

Capability Naming

  • Use verb-noun: user-auth, payment-capture
  • Single purpose per capability
  • 10-minute understandability rule
  • Split if description needs "AND"

Change ID Naming

  • Use kebab-case, short and descriptive: add-two-factor-auth
  • Prefer verb-led prefixes: add-, update-, remove-, refactor-
  • Ensure uniqueness; if taken, append -2, -3, etc.

Tool Selection Guide

Task Tool Why
Find files by pattern Glob Fast pattern matching
Search code content Grep Optimized regex search
Read specific files Read Direct file access
Explore unknown scope Task Multi-step investigation

Error Recovery

Change Conflicts

  1. Run spectr list to see active changes
  2. Check for overlapping specs
  3. Coordinate with change owners
  4. Consider combining proposals

Validation Failures

  1. Run with --strict flag
  2. Check JSON output for details
  3. Verify spec file format
  4. Ensure scenarios properly formatted

Missing Context

  1. Read project.md first
  2. Check related specs
  3. Review recent archives
  4. Ask for clarification

Quick Reference

Stage Indicators

  • changes/ - Proposed, not yet built
  • specs/ - Built and deployed
  • archive/ - Completed changes

File Purposes

  • proposal.md - Why and what
  • tasks.md - Implementation steps
  • design.md - Technical decisions
  • spec.md - Requirements and behavior

CLI Essentials

spectr list              # What's in progress?
spectr show [item]       # View details
spectr validate --strict # Is it correct?
spectr archive <change-id> [--yes|-y]  # Mark complete (add --yes for automation)

Remember: Specs are truth. Changes are proposals. Keep them in sync.