# Quality Checks & Pre-commit Hooks

**When to use:** Setting up your dev environment, understanding CI/local parity, or debugging pre-commit failures.

## Prerequisites

- Rust toolchain installed (`rustup`)
- `jscpd` for duplication checks: `npm install -g jscpd`
- Go toolchain (for SDK work)
- `goimports`: `go install golang.org/x/tools/cmd/goimports@latest`
- `golangci-lint` (optional): `go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest`

## Quick Start

```bash
# Run all quality checks (same as CI)
make quality

# Auto-fix formatting (Rust)
make fmt

# Auto-fix formatting (Go)
make go-fmt

# See clippy errors
make lint

# Lint Go SDK
make go-lint
```

## Pre-commit Hook (v2)

The pre-commit hook at `.git/hooks/pre-commit` uses a **two-phase approach**:

### Phase 1: Auto-fix
- Runs formatters (`cargo fmt`, `gofmt`, `goimports`)
- Re-stages formatted files automatically
- Transparent to the developer

### Phase 2: Verify
- Checks formatting (should pass after phase 1)
- Runs linters (`cargo clippy`, `go vet`, `golangci-lint`)
- Checks file length (max 500 lines)
- Blocks commit if any check fails

### Languages Supported

| Language | Format | Lint | File Length |
|----------|--------|------|-------------|
| Rust | `cargo fmt` | `cargo clippy` | ✅ |
| Go | `gofmt` + `goimports` | `go vet` + `golangci-lint` | ✅ |

### Smart Detection

- Only checks staged files (fast feedback)
- Auto-detects Go modules (walks up to find `go.mod`)
- Skips checks if no Rust/Go files are staged
- Re-stages files after formatting

### Installing the Hook

The hook should already exist. If not:

```bash
chmod +x .git/hooks/pre-commit
```

### Bypassing (Emergency Only)

```bash
# Skip pre-commit hook (logged, use sparingly)
git commit --no-verify -m "emergency fix"
```

## What Gets Checked

### Rust

| Check | Command | What it catches |
|-------|---------|-----------------|
| Format | `cargo fmt --check` | Inconsistent formatting |
| Lint | `cargo clippy -- -D warnings` | Code smells, potential bugs |
| Duplication | `jscpd` (CI only) | Copy-pasted code blocks |
| Tests | `cargo test` (CI only) | Broken functionality |

### Go (SDK)

| Check | Command | What it catches |
|-------|---------|-----------------|
| Format | `gofmt -l` | Inconsistent formatting |
| Imports | `goimports` | Unorganized imports |
| Vet | `go vet ./...` | Suspicious constructs |
| Lint | `golangci-lint run` | Code quality issues |

### Universal

| Check | Threshold | What it catches |
|-------|-----------|-----------------|
| File length | 500 lines max | Files too large to reason about |

## Enforced Lints

These are set to `deny` in `Cargo.toml` (CI will fail):

| Lint | Why |
|------|-----|
| `clippy::unwrap_used` | Panics are forbidden in production code |
| `clippy::expect_used` | Panics are forbidden in production code |
| `clippy::panic` | Explicit panics are forbidden in production code |

**Tests are exempt** via `clippy.toml`:
- `allow-unwrap-in-tests = true`
- `allow-expect-in-tests = true`
- `allow-panic-in-tests = true`

To add a new enforced lint, update `[workspace.lints.clippy]` in root `Cargo.toml`.

## Troubleshooting

### "Format check failed"

```bash
make fmt  # Auto-fix Rust
make go-fmt  # Auto-fix Go
git add -u  # Re-stage (hook does this automatically)
```

### "Clippy warnings treated as errors"

Fix the warnings. Common ones:

```rust
// Bad: unused variable
let x = 5;

// Good: prefix with underscore
let _x = 5;
```

### "go vet failed"

Check for suspicious constructs:

```bash
cd sdk/go/steme && go vet ./...
```

### "File too long"

Split into smaller modules. Keep files under 500 lines for maintainability.

### "Duplication detected"

Refactor the duplicated code into a shared function or module.

## CI/Local Parity

The pre-commit hook runs a subset of what CI runs (format, lint, file length). Full CI also runs:
- Tests (`cargo test`, `go test`)
- Duplication detection (`jscpd`)
- Security audit (`cargo audit`)

## Performance

Target: **<10 seconds** on staged files

| Scenario | Time |
|----------|------|
| No Rust/Go files staged | <0.2s |
| Go files only (warm) | <2s |
| Rust files only (warm) | <3s |
| Both (warm) | <5s |
| Cold compile | ~11s |

## Related

- [Testing Guide](./testing.md) - Running tests
- [Rust Guidelines](../backend/rust-guidelines.md) - Code standards
