# CLAUDE.md

This file provides guidance to Claude Code when working with the RTMX Go CLI codebase.

## Overview

This is the Go implementation of the RTMX CLI, providing a single static binary for requirements traceability management. It is a port of the Python CLI (`rtmx-ai/rtmx`).

## Quick Commands

```bash
make build        # Build the binary
make test         # Run tests
make lint         # Run linter (golangci-lint v2 required)
make hooks        # Install pre-commit hooks
make dev          # Build with race detector
make build-all    # Build for all platforms
make parity       # Run parity tests against Python CLI
```

## Local CI Parity

**CRITICAL: Local pre-commit hooks must match remote CI checks.** Run `make hooks` after cloning to install `.githooks/pre-commit` which runs build, test, lint, and vet before each commit. This prevents pushing code that fails CI.

Install golangci-lint v2 if not present:
```bash
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin
```

## Project Structure

```
rtmx/
├── cmd/rtmx/           # Main entry point
├── internal/
│   ├── cmd/            # CLI commands (Cobra)
│   ├── config/         # Configuration management (Viper)
│   ├── database/       # CSV parsing, Requirement model
│   ├── graph/          # Tarjan's SCC, topological sort, critical path
│   ├── output/         # Tables, colors, progress bars
│   ├── adapters/       # GitHub, Jira, MCP integrations
│   ├── tui/            # Bubble Tea interactive terminal UI
│   │   └── views/      # TUI view models (table, detail, graph, kanban, agents)
│   ├── dashboard/      # Embedded web assets (HTML/CSS/JS via embed.FS)
│   │   ├── static/     # JS (htmx, Alpine, D3), CSS (Tailwind)
│   │   └── templates/  # HTML layout and partials
│   ├── orchestration/  # Multi-agent coordination (claims, heartbeat)
│   └── sync/           # CRDT sync and remotes
├── pkg/rtmx/           # Public API for Go integration
└── testdata/           # Golden files and fixtures
```

## Key Design Decisions

1. **Cobra for CLI** - Standard Go CLI framework with subcommands
2. **Viper for config** - YAML/JSON/ENV config management
3. **No CGO** - Pure Go for static binary distribution
4. **Internal packages** - Implementation details not exported
5. **Golden file tests** - Ensure output parity with Python CLI
6. **Bubble Tea for TUI** - charmbracelet/bubbletea Model-View-Update architecture
7. **htmx + Alpine.js for GUI** - Server-rendered HTML-over-the-wire, no Node.js
8. **embed.FS for web assets** - Single binary deployment, zero external dependencies

## Required Skills for Domain Work

**MANDATORY: Invoke the appropriate skill BEFORE writing any code in these domains.**

| Domain | Skill | Trigger |
|--------|-------|---------|
| Terminal UI | `/tui-dev` | Any work on `internal/tui/`, REQ-TUI-*, Bubble Tea models |
| Web Dashboard | `/gui-dev` | Any work on `internal/dashboard/`, REQ-DASH-*, htmx partials, embedded SPA |

These skills encode the correct persona, architecture, BDD+TDD methodology,
testing patterns, and dependency isolation rules for each domain. Skipping
the skill leads to incorrect patterns (e.g., testing TUI via real terminal
instead of model tests, or introducing Node.js tooling into the GUI).

### Dependency Isolation

- `internal/tui/` may import: `database`, `graph`, `orchestration`, `config`
- `internal/tui/` must NOT import: `cmd`, `adapters`, `sync`, `dashboard`
- `internal/dashboard/` contains ONLY static assets (HTML/CSS/JS)
- Bubble Tea deps (bubbletea, bubbles, lipgloss) must NOT leak into non-TUI packages
- No npm, no Node.js, no JavaScript bundler -- all web assets are static files in embed.FS

## Development Workflow

### Adding New Features or Requirements

**CRITICAL: Fully elaborate the requirement specification and dependency relationships BEFORE commencing any implementation work.** This means:

1. Check `make backlog` for prioritized requirements
2. **Elaborate the requirement specification** in `.rtmx/requirements/<CATEGORY>/`:
   - Write complete acceptance criteria with testable conditions
   - Define all files to create/modify
   - Identify all dependency relationships (blocks/blocked-by)
   - Update `.rtmx/database.csv` with the requirement entry and dependencies
   - Verify no circular dependencies with `rtmx cycles`
3. **Write BDD feature specs** in `features/` when the requirement involves user-facing behavior
4. Write failing tests with proper markers (TDD Red phase)
5. Implement minimal code to pass tests (TDD Green phase)
6. Refactor while keeping tests green
7. Run `make test && make lint`
8. Run `rtmx verify --update` to close the loop
9. Commit with requirement ID in message

### Adding a New Command

1. Create `internal/cmd/<command>.go`
2. Add command to `internal/cmd/root.go` in `init()`
3. Create test file `internal/cmd/<command>_test.go`
4. Add golden files to `testdata/` if needed

### Testing Parity

Every command must produce identical output to the Python CLI:

```bash
# Run parity tests
make parity

# Manual comparison
python -m rtmx status > /tmp/py.out
./bin/rtmx status > /tmp/go.out
diff /tmp/py.out /tmp/go.out
```

### Build & Release

```bash
# Local snapshot build
make snapshot

# Check release config
make release-check

# Release (triggered by tag)
git tag v0.1.0
git push origin v0.1.0
```

## Version Management

Version, commit, and date are injected via ldflags:

```go
// internal/cmd/root.go
var (
    Version = "dev"
    Commit  = "none"
    Date    = "unknown"
)
```

Build with:
```bash
go build -ldflags "-X github.com/rtmx-ai/rtmx/internal/cmd.Version=v0.1.0 ..."
```

## Versioning Policy

RTMX follows semantic versioning (MAJOR.MINOR.PATCH). The public surface
that determines version bumps is: CLI commands and flags, CSV database
format, config file format, exit codes, and --json output schema.

### MAJOR bump (breaking change)

A major bump is required when any of these change in a non-backward-compatible way:

- Removal or rename of a CLI command or flag
- Change to CSV column names or column order that breaks existing databases
- Change to config file schema that rejects previously valid configs
- Change to exit codes for existing commands
- Change to --json output schema (field removal, type change, structural change)
- Removal of a public Go API in pkg/rtmx/

### MINOR bump (new feature, backward compatible)

A minor bump is used when new functionality is added without breaking existing behavior:

- New CLI commands or subcommands (e.g., `rtmx release`)
- New flags on existing commands (e.g., `--version`)
- New CSV columns (additive; existing databases still parse correctly)
- New config file fields (additive; existing configs still valid)
- New --json output fields (additive; existing consumers unaffected)
- New database methods or FilterOptions fields
- New schema plugin support

### PATCH bump (bug fix, no new features)

A patch bump is used for fixes that do not add or change public surface:

- Bug fixes to existing commands
- Test improvements and coverage increases
- Documentation updates
- CI/CD pipeline fixes
- Performance improvements with no behavioral change
- Internal refactoring with no public API change

### Release Workflow

Every tagged release must pass the release gate:

```bash
# 1. Assign requirements to the target version
rtmx release assign v0.3.0 REQ-PLAN-003 REQ-PLAN-005 ...

# 2. Implement and verify
rtmx verify --update

# 3. Check release scope
rtmx release scope v0.3.0

# 4. Gate the release (must exit 0)
rtmx release gate v0.3.0

# 5. Tag and push (pre-tag hook enforces gate)
git tag -s v0.3.0 -m "v0.3.0: <summary>"
git push origin v0.3.0
```

The `.githooks/pre-tag` hook runs `rtmx release gate` automatically.
Tags that fail the gate are rejected.

## Compatibility Requirements

- **Config files**: Must read rtmx.yaml/.rtmx/config.yaml identically to Python
- **CSV format**: Must read/write rtm_database.csv identically to Python
- **Output format**: Tables, colors, progress bars must match Python
- **Exit codes**: Must match Python exit codes
- **JSON output**: Must match Python JSON schema exactly

## Common Patterns

### Error Handling

```go
func runCommand(cmd *cobra.Command, args []string) error {
    config, err := loadConfig()
    if err != nil {
        return fmt.Errorf("failed to load config: %w", err)
    }
    // ...
}
```

### Color Output

```go
if !noColor && isTerminal() {
    output = colorize(output, "green")
}
```

### Progress Display

```go
bar := output.NewProgressBar(total)
for _, item := range items {
    process(item)
    bar.Increment()
}
bar.Finish()
```

## Dependencies

- `github.com/spf13/cobra` - CLI framework
- `github.com/spf13/viper` - Configuration
- `gopkg.in/yaml.v3` - YAML parsing

Minimize dependencies to keep binary size small (<15MB target).

## Testability Requirements

### Coverage Targets

| Package | Target | Enforcement |
|---------|--------|-------------|
| internal/adapters | 100% | Coveralls + CI gate |
| internal/cmd | 100% | Coveralls + CI gate |
| internal/database | 90% | Coveralls |
| internal/graph | 90% | Coveralls |
| internal/config | 90% | Coveralls |
| internal/output | 90% | Coveralls |
| cmd/rtmx | 100% | E2E tests |
| **Overall** | >90% | CI fails below 80% |

### Architecture Patterns for Testability

**ALL adapters and commands MUST be fully testable:**

1. **HTTPClient Interface** - All HTTP operations via interface
   ```go
   type HTTPClient interface {
       Do(req *http.Request) (*http.Response, error)
   }

   func NewGitHubAdapter(cfg *config.GitHubConfig, opts ...AdapterOption) (*GitHubAdapter, error)

   // Test with mock
   adapter, _ := NewGitHubAdapter(cfg, WithHTTPClient(mockClient))
   ```

2. **FileSystem Interface** - All file I/O via interface
   ```go
   type FileSystem interface {
       ReadFile(path string) ([]byte, error)
       WriteFile(path string, data []byte, perm os.FileMode) error
       Stat(path string) (os.FileInfo, error)
       MkdirAll(path string, perm os.FileMode) error
       Remove(path string) error
   }
   ```

3. **CommandContext** - Dependency injection for commands
   ```go
   type CommandContext struct {
       Config      *config.Config
       Database    *database.Database
       Output      io.Writer
       ErrOutput   io.Writer
       Input       io.Reader
       GetEnv      func(string) string
       FileSystem  FileSystem
   }
   ```

4. **Environment Abstraction** - Never call `os.Getenv` directly
   ```go
   // WRONG
   token := os.Getenv("GITHUB_TOKEN")

   // RIGHT
   func NewAdapter(cfg *Config, opts ...Option) *Adapter
   func WithEnvGetter(fn func(string) string) Option
   ```

### Test Patterns

1. **Table-Driven Tests** - Standard Go pattern for all test cases
   ```go
   func TestFeature(t *testing.T) {
       tests := []struct {
           name     string
           input    Input
           expected Output
           wantErr  bool
       }{...}

       for _, tt := range tests {
           t.Run(tt.name, func(t *testing.T) {...})
       }
   }
   ```

2. **Golden File Tests** - For output formatting
   ```go
   golden.Assert(t, "status_output", actualOutput)
   // Update: go test -update
   ```

3. **Mock HTTP Server** - For adapter testing
   ```go
   server := testutil.NewMockServer()
   server.ExpectRequest("GET", "/repos/owner/repo/issues", MockResponse{...})
   defer server.Close()
   ```

4. **Fuzz Tests** - For parsing functions
   ```go
   func FuzzCSVParse(f *testing.F) {
       f.Add([]byte("header\nrow"))
       f.Fuzz(func(t *testing.T, data []byte) {
           ParseCSV(bytes.NewReader(data)) // must not panic
       })
   }
   ```

5. **Property-Based Tests** - For algorithm invariants
   ```go
   // Graph algorithms must satisfy invariants
   // - Tarjan finds ALL SCCs
   // - Topological sort respects ALL edges
   // - Critical path is actually critical
   ```

### Test Infrastructure Packages

```
internal/
├── adapters/
│   └── testutil/
│       ├── mock_server.go    # HTTP mock server
│       └── fixtures.go       # Adapter test data
├── testutil/
│   ├── fixtures.go           # Test database factories
│   ├── golden.go             # Golden file assertions
│   └── tempdir.go            # Temp directory helpers
└── cmd/
    └── testutil/
        └── context.go        # CommandContext factory
```

### CI Integration

```yaml
# Coverage is checked on every PR
- name: Run tests with coverage
  run: go test -coverprofile=coverage.out -covermode=atomic ./...

- name: Upload to Coveralls
  uses: coverallsapp/github-action@v2

- name: Check coverage threshold
  run: |
    COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | tr -d '%')
    if (( $(echo "$COVERAGE < 80" | bc -l) )); then
      echo "Coverage $COVERAGE% is below 80% threshold"
      exit 1
    fi
```

### What NOT to Do

- **NO global state** - No package-level variables that affect behavior
- **NO direct os.Getenv** - Use injected environment getter
- **NO direct http.DefaultClient** - Use injected HTTPClient
- **NO direct file operations** - Use FileSystem interface
- **NO untested code paths** - Every error path must have a test

## Contact

- RTMX Engineering: dev@rtmx.ai
- Issues: https://github.com/rtmx-ai/rtmx/issues
