From puritan
Use when auditing a codebase against architectural doctrine. Triggers on "audit my code", "check architecture", "run doctrines", "review system design", "check pattern compliance".
How this skill is triggered — by the user, by Claude, or both
Slash command
/puritan:inquisitionThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This mode audits your codebase against architectural patterns defined in doctrine files,
This mode audits your codebase against architectural patterns defined in doctrine files, helping maintain consistency and catch violations early.
Before running an audit:
.architecture/config.yml must exist with doctrine configuration, if it doesn't guide user through creating one<plugin-root>/skills/doctrines/ — this SKILL.md lives at <plugin-root>/skills/inquisition/SKILL.md, so doctrines are the doctrines/ sibling within the same skills/ directoryThe mode automatically selects scope based on arguments:
| Invocation | Mode | Scope |
|---|---|---|
/puritan:inquisition (no args) | Report | Changed files (git diff against base branch) |
/puritan:inquisition full | Report | Entire codebase |
/puritan:inquisition interactive | Interactive | Entire codebase |
/puritan:inquisition <doctrine> | Report | Changed files, single doctrine only |
/puritan:inquisition interactive <doctrine> | Interactive | Entire codebase, single doctrine only |
| Called from hook/make | Report | Changed files |
| Mistake | Fix |
|---|---|
| Running full audit on every commit | Default mode scans changed files only — use full for periodic deep scans |
| Treating all violations as errors | Respect strictness levels — aspirational doctrines produce warnings, not errors |
| Auditing generated or vendored code | Exclude **/migrations/**, vendor/, *.generated.* in config |
| Ignoring the decisions.yml overrides | Team-approved exceptions are not heresies — check for overrides before reporting |
| Running without doctrines present | Verify doctrine files exist before dispatching subagents — warn and continue with what's available |
| Reporting violations without the actual code found | Always include the concrete line/import/pattern that triggered the violation |
# .architecture/config.yml (required)
doctrines:
- name: ddd
enabled: true
targets:
- domain/
- application/
- name: event-sourcing
enabled: true
targets:
- domain/events/
- infrastructure/event_store/
- name: cqrs
enabled: true
targets:
- domain/commands/
- infrastructure/projections/
layers:
domain:
- domain/
application:
- application/
infrastructure:
- infrastructure/
api:
- api/
# .architecture/decisions.yml (optional)
strictness:
ddd: strict # All violations are errors
event-sourcing: pragmatic # Allowed exceptions become warnings
cqrs: aspirational # All violations are warnings
overrides:
DDD-004: # Allow Pydantic in domain
severity: warning
reason: "Team decision: Pydantic for validation"
EVS-114: # Allow sync projections
severity: info
reason: "Read-after-write consistency required"
For each doctrine in config:
<plugin-root>/skills/doctrines/<name>.md existsReport Mode:
git diff --name-only $(git merge-base HEAD main) HEADInteractive Mode:
After determining scope, count the total files and unique directories before dispatching any subagents.
If the scope exceeds 100 files, pause and ask the user:
"Found N files across X directories matching your configured targets. This audit may consume significant tokens. How would you like to proceed?
- Proceed with full audit
- Focus on specific directories (list them)
- Run changed-files only (
git diffagainst base branch)- Audit a single doctrine only (which one?)"
If the scope is ≤ 100 files, proceed silently — no prompt needed.
For Report Mode (non-interactive), apply the same check but phrase it as a warning rather than a blocking question:
"⚠ Scope: N files across X directories. Proceeding with audit. Use
targets:in.architecture/config.ymlto narrow scope."
Report Mode (Parallel):
# Pseudo-code for parallel dispatch
async def run_report_audit(doctrines, scope):
tasks = []
for doctrine in doctrines:
task = dispatch_subagent(
doctrine_name=doctrine.name,
doctrine_content=doctrine.content,
files_to_audit=scope.files_for_doctrine(doctrine),
output_format="json"
)
tasks.append(task)
results = await gather(*tasks)
return collate_results(results)
Interactive Mode (Sequential):
# Pseudo-code for interactive audit
def run_interactive_audit(doctrines, scope):
for doctrine in doctrines:
print(f"\nAuditing {doctrine.name}...")
violations = audit_with_doctrine(doctrine, scope)
if not violations:
print(f"No {doctrine.name} violations found!")
continue
for violation in violations:
display_violation(violation)
response = ask_user([
"Fix this violation",
"Explain why this matters",
"Skip for now",
"Mark as allowed exception"
])
handle_response(response, violation)
Apply strictness levels and overrides:
def apply_strictness(violations, decisions):
for violation in violations:
doctrine_strictness = decisions.strictness.get(violation.doctrine, "pragmatic")
# Check for specific override
if violation.id in decisions.overrides:
violation.severity = decisions.overrides[violation.id].severity
violation.note = decisions.overrides[violation.id].reason
continue
# Apply doctrine-level strictness
if doctrine_strictness == "strict":
# Keep original severity
pass
elif doctrine_strictness == "pragmatic":
# Allowed exceptions become warnings
if violation.id in doctrine.allowed_exceptions:
violation.severity = "warning"
elif doctrine_strictness == "aspirational":
# Everything becomes warning
violation.severity = "warning"
return violations
Report Format:
Architecture Audit Report
=========================
Summary:
Files scanned: 47
Violations found: 12 (5 errors, 7 warnings)
Doctrines applied: ddd, event-sourcing, cqrs
Errors (5):
-----------
[DDD-001] Layer boundary violation
File: wayledger/domain/aggregates/loan.py:42
Rule: Domain must not import from infrastructure
Found: from wayledger.infrastructure.event_store import EventStore
[EVS-110] Event flow violation
File: wayledger/application/services/loan_service.py:127
Rule: Events must be persisted before publishing
Found: self.bus.publish(event) before self.store.append_events()
Warnings (7):
-------------
[DDD-015] Aggregate size warning
File: wayledger/domain/aggregates/loan.py
Rule: Aggregate should be <500 LOC
Found: LoanAggregate is 847 lines
Note: Team override - complex business logic justified
Clean files (35):
wayledger/domain/aggregates/account.py
wayledger/domain/commands/loan_commands.py
... (truncated for brevity)
Next steps:
1. Fix 5 errors before committing
2. Review warnings for potential improvements
3. Consider adding overrides in .architecture/decisions.yml
Each doctrine subagent MUST return this JSON structure:
{
"doctrine": "ddd",
"files_scanned": 12,
"violations": [
{
"id": "DDD-001",
"file": "wayledger/domain/aggregates/loan.py",
"line": 42,
"rule": "Domain must not import from infrastructure layer",
"actual": "from wayledger.infrastructure.event_store import EventStore",
"severity": "error",
"category": "layer-boundary"
}
],
"clean_files": ["wayledger/domain/aggregates/account.py"],
"notes": ["Unable to parse wayledger/broken.py - syntax error"]
}
#!/bin/bash
# .git/hooks/pre-push
echo "Running the Inquisition..."
puritan inquisition
if [ $? -ne 0 ]; then
echo "Heresies found. Fix before pushing."
exit 1
fi
# Makefile
audit:
@echo "Running the Inquisition..."
@puritan inquisition
audit-full:
@echo "Running the full Inquisition..."
@puritan inquisition full
audit-fix:
@echo "Running interactive Inquisition with fixes..."
@puritan inquisition interactive
pre-push: format lint test audit
@echo "All checks passed!"
# .github/workflows/audit.yml
name: Architecture Audit
on: [push, pull_request]
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run the Inquisition
run: |
puritan inquisition full
Do not use a hardcoded list. Discover available doctrines dynamically at runtime:
*.md files in <plugin-root>/skills/doctrines/, excluding any file whose basename starts with _ (e.g. _template.md) — this SKILL.md lives at <plugin-root>/skills/inquisition/SKILL.md, so the doctrines directory is the doctrines/ sibling within the same skills/ directory.md file is an available doctrine — its filename without extension is the doctrine name.architecture/config.yml but its file is missing, warn and continue with what is availableThis ensures new doctrines automatically participate in audits with no changes to this skill.
If .architecture/config.yml is not found, do not show a raw error. Instead, offer to hand off to Covenant's discovery mode:
"No
.architecture/config.ymlfound. The Inquisition cannot proceed without knowing what to audit or where to look.Would you like me to run
/puritan:covenant discoverfirst? It will scan your codebase structure, identify the patterns you appear to be using, and generate the config file — then the Inquisition can begin."
If the user agrees, invoke Covenant in discover mode. If they decline, show the manual template:
# .architecture/config.yml
doctrines:
- name: ddd
enabled: true
targets:
- domain/
layers:
domain:
- domain/
exclude:
- "**/migrations/**"
- "vendor/"
Warning: Doctrine file not found: doctrines/hexagonal.md
Configured in config.yml but file is missing.
Continuing with available doctrines: ddd, cqrs
Doctrine audit failed: ddd
Subagent error: Timeout scanning large file
Try: Increase timeout or exclude file in config
Warning: Could not parse 3 files:
- src/broken.py (syntax error line 42)
- src/invalid.ts (unsupported file type)
- src/huge.json (file too large)
/puritan:scriptorium<plugin-root>/skills/doctrines/<name>.md.architecture/config.yml/puritan:inquisition <name> to test# .architecture/config.yml
exclude:
- "**/*.generated.py"
- "**/migrations/**"
- "tests/**"
- "*.min.js"
# .architecture/decisions.yml
severity_mapping:
error: ["block", "critical", "error"]
warning: ["warn", "warning", "caution"]
info: ["info", "note", "suggestion"]
Q: Can I run specific doctrines only?
A: Yes, use /puritan:inquisition ddd or /puritan:inquisition cqrs saga for multiple.
Q: How do I suppress a false positive?
A: Add an override in .architecture/decisions.yml with your reasoning.
Q: Can I add project-specific rules? A: Create a custom doctrine file via Scriptorium and add project-specific violations.
Q: How do I handle legacy code? A: Set doctrine strictness to "aspirational" to convert all violations to warnings.
Q: Can I audit before each commit?
A: Yes, add to .git/hooks/pre-commit but use file scope for speed.
0 - No errors found (warnings allowed)1 - Errors found that must be fixed2 - Configuration or setup error3 - Subagent or parsing failureDeliver all findings in the voice of the Witchfinder — formally uncompromising, dramatically precise, with a knowing wink. Violations are heresies. Resolutions are absolution. The codebase is the sanctum.
See persona.md for full vocabulary and tone guidance if available, otherwise use the above as your guide.
npx claudepluginhub ppryde/pip-skills --plugin puritanMaps module dependencies, checks layering integrity, and flags structural decay across a codebase. Includes onboarding mode for codebase tours.
Analyzes codebase architecture via multi-agent specialists on structure, coupling, integration, error handling, security; verifies findings, reports strengths and flaws with evidence.
Enforces architectural rules (layer responsibilities, dependency direction, structure) when generating or reviewing code. Supports clean/hexagonal/onion/monolith architectures via loaded rule sets.