code-governance
Stop spaghetti imports. Enforce module boundaries in Python and TypeScript.
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. Run each command separately — pasting them all at once will concatenate into a single broken URL:
/plugin marketplace add useparadigm/code-governance-plugin
/plugin install code-governance
/reload-plugins
Then run:
/governance-init
Without
/reload-plugins, the new commands won't be registered yet and/governance-initwill fail withUnknown command.
What happens:
- Claude installs
code-governancein your project - Scans your codebase — discovers modules, maps every import
- Shows you the dependency map: "I found 8 modules.
apiimports fromdbdirectly — should it?" - You discuss architecture: which dependencies are intentional, which are spaghetti
- Claude creates
governance.tomlbased on your answers - Enables rules:
no_cycles,enforce_cannot_depend_on, optionallyenforce_layers - If existing violations: "Want a baseline? I'll accept these and only fail on new ones."
- Sets up GitHub Actions — CI check on every PR +
/governance fixcommand - 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
pip install code-governance
Quick scan — no config needed, instant results:
$ governance-ast --auto src/
Governance Report (python)
Modules: 8 | Files scanned: 47
Violations (1):
[E] [no_cycles] Circular dependency: payments -> notifications -> payments
FAILED
Works on TypeScript too — language auto-detected from source:
$ governance-ast --auto src/
Governance Report (typescript)
Modules: 6 | Files scanned: 82
...
.ts, .tsx, .js, .jsx, .mts, .cts, .mjs, .cjs all supported. tsconfig.json path aliases (@/*, extends chains) are honored.
Full setup — generate config, review, enforce:
# 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:
- uses: actions/checkout@v4
- name: Governance
uses: useparadigm/code-governance@main
with:
config: governance.toml
diff: origin/main
See governance-fix workflow to enable the /governance fix PR command.
GitLab CI — add to .gitlab-ci.yml:
governance:
image: python:3.12-slim
variables:
GIT_DEPTH: 0
before_script:
- pip install code-governance
script:
- governance-ast --config governance.toml --diff origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
See GitLab MR comments to post the report as an MR note.
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
repos:
- repo: https://github.com/useparadigm/code-governance
rev: main
hooks:
- id: governance-check
- id: governance-diff
Adopting on legacy codebases
governance-ast --save-baseline .governance-baseline.json
governance-ast --baseline .governance-baseline.json
Accept existing violations. Only fail on new ones.
Transitive detection
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
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
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 |
Choose tach if you need interface enforcement or visibility control. Choose code-governance if you want transitive detection, AI-guided setup, CI integration, or zero-config scanning.
Governance fix workflow
Add to .github/workflows/governance-fix.yml:
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."
GitLab MR comments
Posts the governance report as a note on the merge request. Requires a Project Access Token with api scope, exposed as masked CI variable GOVERNANCE_GITLAB_TOKEN.
governance:
image: python:3.12-slim
variables:
GIT_DEPTH: 0
before_script:
- pip install code-governance
- apt-get update && apt-get install -y --no-install-recommends curl jq && rm -rf /var/lib/apt/lists/*
script:
- set +e
- governance-ci-report --config governance.toml --json > report.json
- set -e
- PASSED=$(jq -r '.passed' report.json)
- jq -r '.markdown' report.json | tee report.md
- |
if [ "$CI_PIPELINE_SOURCE" = "merge_request_event" ] && [ -n "$GOVERNANCE_GITLAB_TOKEN" ]; then
BODY=$(jq -Rs . < report.md)
curl -sf --request POST \
--header "PRIVATE-TOKEN: $GOVERNANCE_GITLAB_TOKEN" \
--header "Content-Type: application/json" \
--data "{\"body\": $BODY}" \
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID/notes"
fi
- test "$PASSED" = "true"
artifacts:
when: always
paths: [report.json, report.md]
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
/governance fix is GitHub-only for now — GitLab equivalent would need a Note webhook listener.
Updating
There are two pieces to update independently:
CLI (PyPI package):
pip install --upgrade code-governance
Check the installed version against the flags — if you don't see --language {auto,python,typescript} in governance-ast --help, you're on a pre-0.4 CLI and the TypeScript feature won't work.
Claude Code plugin (skills):
/plugin marketplace update useparadigm-code-governance-plugin
/reload-plugins
Or enable auto-update once: /plugin → Marketplaces → toggle Enable auto-update. Then every session picks up the latest skills automatically.
License
MIT
Metadata
Release files for code-governance 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| code_governance-0.4.1.tar.gz | 237.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| code_governance-0.4.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 283.2 kB
Release files / code_governance-0.4.1.tar.gz
| Download URL | code_governance-0.4.1.tar.gz |
|---|---|
| Size | 237.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
40e2f11e4b2b36111704d2a35f678443c6be470c81d811e43fe26a9b8b4fe3ab
|
|
BLAKE2b-256 checksum How to use checksums |
cb4350d383833c38772773b3c8f7b2831d99bd3f07e1f26873be44f8437ea31a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.0
|
Release files / code_governance-0.4.1-py3-none-any.whl
| Download URL | code_governance-0.4.1-py3-none-any.whl |
|---|---|
| Size | 45.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0eb892ea15c2d2d77ccdbe289061ad388a2cede88790610760eec61db5b6adc0
|
|
BLAKE2b-256 checksum How to use checksums |
498fd054beacb2603c3d2f1fa7fc7412f8c93afe1c436a5d1701875d53cc7a2b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.0
|