Metadata-Version: 2.4
Name: code-governance
Version: 0.3.1
Summary: AST-based governance engine for enforcing module boundaries and architecture rules in Python codebases
Project-URL: Homepage, https://github.com/useparadigm/code-governance
Project-URL: Repository, https://github.com/useparadigm/code-governance
Project-URL: Issues, https://github.com/useparadigm/code-governance/issues
Author-email: Paradigm <hello@useparadigm.app>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.10
Requires-Dist: ast-grep-py>=0.28.1
Requires-Dist: httpx<1,>=0.25
Requires-Dist: pydantic<3,>=2.0
Requires-Dist: tomli>=2.0; python_version < '3.11'
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == 'dev'
Description-Content-Type: text/markdown

<p align="center">
  <img src="code-governance.jpg" alt="code-governance" width="200">
</p>

<h1 align="center">code-governance</h1>

<p align="center"><b>Stop spaghetti imports. Enforce module boundaries in Python.</b></p>

<p align="center">
  <a href="https://pypi.org/project/code-governance/"><img src="https://img.shields.io/pypi/v/code-governance?style=flat&color=orange" alt="PyPI"></a>
  <a href="https://github.com/useparadigm/code-governance/actions"><img src="https://img.shields.io/github/actions/workflow/status/useparadigm/code-governance/tests.yml?style=flat&label=tests" alt="Tests"></a>
  <a href="https://github.com/useparadigm/code-governance/blob/main/LICENSE"><img src="https://img.shields.io/github/license/useparadigm/code-governance?style=flat" alt="License"></a>
</p>

---

## Before / After

| Without governance | With governance |
|---|---|
| `api/` imports from `billing/`, `db/`, `auth/`, `utils/`, `migrations/`... | `api/` has `cannot_depend_on = ["billing", "migrations"]` — forbidden imports blocked |
| One refactor breaks 14 files across 6 modules | Boundaries are explicit, changes stay local |
| New dev: "Can I import this here?" "Uhh... maybe?" | Config says yes or no. CI enforces it. |

## Setup

### Option 1: With Claude Code (recommended)

Install the plugin — Claude handles everything interactively:

```
/plugin marketplace add useparadigm/code-governance-plugin
/plugin install code-governance
```

Then run:

```
/governance-init
```

**What happens:**

1. Claude installs `code-governance` in your project
2. Scans your codebase — discovers modules, maps every import
3. Shows you the dependency map: *"I found 8 modules. `api` imports from `db` directly — should it?"*
4. You discuss architecture: which dependencies are intentional, which are spaghetti
5. Claude creates `governance.toml` based on your answers
6. Enables rules: `no_cycles`, `enforce_cannot_depend_on`, optionally `enforce_layers`
7. If existing violations: *"Want a baseline? I'll accept these and only fail on new ones."*
8. Sets up GitHub Actions — CI check on every PR + `/governance fix` command
9. Done. Every PR is now gated on architecture rules.

**After setup, two more commands available:**

| Command | What it does |
|---------|-------------|
| `/governance-check` | Run checks, explain violations, fix them interactively |
| `/governance-audit` | Architecture health review — metrics, hotspots, suggestions |

### Option 2: Manual

```bash
pip install code-governance
```

**Quick scan** — no config needed, instant results:

```bash
$ governance-ast --auto src/

Governance Report (python)
Modules: 8 | Files scanned: 47

Violations (1):
  [E] [no_cycles] Circular dependency: payments -> notifications -> payments

FAILED
```

**Full setup** — generate config, review, enforce:

```bash
# Generate config: detect modules + seed cannot_depend_on by locking down
# every module pair that does not currently import from each other.
governance-ast --generate --source-root src/

# See the dependency map
governance-ast --discover

# Edit governance.toml — remove dependencies you don't want, add layers
# Then enforce:
governance-ast
```

**Set up CI** — add to `.github/workflows/governance.yml`:

```yaml
- uses: actions/checkout@v4
- name: Governance
  uses: useparadigm/code-governance@main
  with:
    config: governance.toml
    diff: origin/main
```

See [governance-fix workflow](#governance-fix-workflow) to enable the `/governance fix` PR command.

## What it catches

| Rule | Example |
|------|---------|
| `enforce_cannot_depend_on` | `api` imports `billing` but `billing` is in `cannot_depend_on` |
| `no_cycles` | `payments` -> `notifications` -> `payments` |
| `enforce_layers` | `db` (infrastructure) imports from `api` (presentation) |
| `max_public_surface` | 80% of `core`'s symbols used externally — too exposed |
| `min_cohesion` | `utils` imports 90% from other modules — grab-bag module |

## What happens in CI

Every PR gets a governance comment:

```
❌ Governance — 1 violation, 1 new module

🔴 🔗 core: Forbidden dependency: 'core' imports 'db' (cannot_depend_on: ['db'])
  core/service.py:2

📦 New module exporters (exporters/) — not in governance.toml

Reply /governance fix to apply.

🤖 AI: The core→db import creates tight coupling. Extract shared
types into a common module, or remove "db" from core's cannot_depend_on.
```

**New module detected?** Reply `/governance fix` — bot adds it to config, commits to your branch.

**Violation?** Fix the import or update `governance.toml`. Use `/governance-check` in Claude Code for interactive help.

### Pre-commit

```yaml
repos:
  - repo: https://github.com/useparadigm/code-governance
    rev: main
    hooks:
      - id: governance-check
      - id: governance-diff
```

### Adopting on legacy codebases

```bash
governance-ast --save-baseline .governance-baseline.json
governance-ast --baseline .governance-baseline.json
```

Accept existing violations. Only fail on new ones.

### Transitive detection

```bash
governance-ast --transitive
```

Detects indirect violations through dependency chains. If `api` has `cannot_depend_on = ["db"]` and `api → service → db`, the transitive check catches it. Also works with layer enforcement. Enable permanently in config with `transitive = true` under `[rules]`.

## LLM advice

```bash
export OPENAI_API_KEY=sk-...   # or ANTHROPIC_API_KEY
governance-ast --advise
```

Analyzes violations and suggests: accept the dependency, restructure the code, or extract a shared module. Works with OpenAI and Anthropic.

## HTML report

```bash
governance-ast --format html > report.html
```

Self-contained dependency matrix with module metrics. Drop any governance JSON into it.

## Comparison

| | code-governance | tach | import-linter |
|---|---|---|---|
| **Zero-config scan** | `--auto` — instant | No | No |
| **Config generation** | `--generate` from source | `tach init` (interactive TUI) | Manual |
| **LLM advice** | `--advise` — explains + suggests fix | No | No |
| **CI comments** | PR comments with explanations | No | No |
| **Auto-fix from PR** | `/governance fix` | No | No |
| **AI agent plugin** | Claude Code plugin | No | No |
| **Diff mode** | `--diff HEAD` | No | No |
| **Baseline** | Accept existing, fail on new | No | No |
| **Module metrics** | Cohesion, surface, symbols | No | No |
| **HTML report** | Dependency matrix viewer | `tach show` | Browser UI |
| **JSON output** | Yes | Yes | No |
| **Scans source directly** | Yes | Yes | No — must be importable |
| **Interface enforcement** | No | Yes | No |
| **Transitive imports** | Full chain (`--transitive`) | Direct only | Full chain |
| **Forbidden imports** | `cannot_depend_on` | `cannot_depend_on` | Forbidden contract |
| **Visibility control** | No | Yes | No |
| **Speed (Django)** | ~1.2s | Sub-second (Rust) | ~0.1s (grimp) |
| **Config syntax** | Simple TOML lists | TOML with regex | INI or TOML |
| **Maintenance** | Active | Abandoned by original team | Active, slow |

**Choose tach** if you need interface enforcement or visibility control (note: unmaintained).
**Choose code-governance** if you want transitive detection, AI-guided setup, CI integration, or zero-config scanning.

---

<details>
<summary><h3>Governance fix workflow</h3></summary>

Add to `.github/workflows/governance-fix.yml`:

```yaml
name: Governance Fix

on:
  issue_comment:
    types: [created]

permissions:
  contents: write
  pull-requests: write

jobs:
  apply-fix:
    name: Apply config fix
    runs-on: ubuntu-latest
    if: |
      github.event.issue.pull_request &&
      contains(github.event.comment.body, '/governance fix')
    steps:
      - name: Acknowledge
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \
            -X POST -f content=eyes

      - uses: actions/checkout@v4

      - name: Checkout PR branch
        env:
          GH_TOKEN: ${{ github.token }}
        run: gh pr checkout ${{ github.event.issue.number }}

      - name: Find and apply fix
        id: fix
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          PAYLOAD=$(gh api repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments \
            --jq '.[] | select(.body | contains("<!-- governance-fix:")) | .body' \
            | grep -oP '<!-- governance-fix:\K[A-Za-z0-9+/=]+' | tail -1)

          if [ -z "$PAYLOAD" ]; then
            gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \
              -X POST -f content=confused
            exit 1
          fi

          DECODED=$(echo "$PAYLOAD" | base64 -d)
          CONFIG_PATH=$(echo "$DECODED" | python3 -c "import sys,json; print(json.load(sys.stdin)['config_path'])")
          echo "$DECODED" | python3 -c "import sys,json; print(json.load(sys.stdin)['updated_config'])" > "$CONFIG_PATH"

          pip install code-governance
          echo "Config updated"

          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git add "$CONFIG_PATH"
          git diff --cached --quiet && exit 0

          git commit -m "governance: add new modules to config"
          git push
          echo "sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT

      - name: Confirm
        if: steps.fix.outputs.sha
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \
            -X POST -f content='+1'
          gh pr comment ${{ github.event.issue.number }} \
            --body "✅ Config fix applied in \`${{ steps.fix.outputs.sha }}\`. Re-run checks to verify."
```

</details>

## License

MIT
