From predicate
Audits API surface coherence and type safety by iteratively enumerating public exports, types, functions, and traits with human checkpoints.
How this skill is triggered — by the user, by Claude, or both
Slash command
/predicate:api-auditThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
A thorough, piecemeal framework for auditing code API surfaces. Designed to maintain agent coherence by working iteratively through the codebase with explicit human checkpoints.
A thorough, piecemeal framework for auditing code API surfaces. Designed to maintain agent coherence by working iteratively through the codebase with explicit human checkpoints.
Adversarial path anchor. This lens is invoked on the Verification Dual's adversarial path (rules.md §2 Invariant 1): when no deterministic evaluator can close an API-surface correctness condition, context-free agents using this protocol supply the decorrelated review. See skills/refine/SKILL.md AUDIT §"Sibling Skills Consultation" for the wiring point.
Guiding Principle: An ideal API is minimal, well-scoped, type-safe, elegantly composable, and monosemic. It leverages language features to make error states unrepresentable.
Before beginning, establish the audit scope with the user. Confirm: target language and version; type system strength; public API entry points; explicit exclusions (generated code, vendored deps); user-defined constraints (e.g., no breaking changes, no-std compatibility).
Objective: Build a complete mental model of the public API surface before any analysis.
For each entry point, catalog:
Produce a structured inventory:
## Module: `crate::auth`
### Types
- `Principal` (struct) — L23-L45
- `AuthState` (enum) — L47-L62
### Functions
- `Principal::verify(&self, sig: &Signature) -> Result<()>` — L67
- `validate_token(token: &str) -> Result<Claims>` — L89
### Traits
- `Authenticator` — L12-L21
- `fn authenticate(&self, credentials: &Credentials) -> Result<Session>`
Checkpoint: Present surface map to user. Ask:
Objective: Audit each logical component systematically, one at a time.
Coherence Strategy: Analyze ONE component per iteration. Present findings. Await user acknowledgment before proceeding to the next.
[PASS | WARN | FAIL]The API should expose only what external consumers need.
| Check | Description | Anti-Pattern |
|---|---|---|
| A.1 | Internal implementation details are hidden | pub on helper functions; _internal prefixes on public items |
| A.2 | Configuration uses sensible defaults | Requiring 10 parameters when 2 would suffice |
| A.3 | No "god objects" with excessive responsibility | Single type with 50+ methods across unrelated concerns |
| A.4 | Private fields with controlled accessors where appropriate | All fields pub when mutation should be constrained |
| Remediation Patterns: |
pub(crate), internal, private)Leverage the type system to make invalid states impossible to construct.
| Check | Description | Anti-Pattern |
|---|---|---|
| B.1 | Domain values use newtypes, not primitives | user_id: String instead of UserId(String) |
| B.2 | Enums are exhaustive over valid states | Magic strings like status: "pending" vs Status::Pending |
| B.3 | Result/Option used properly; no null/nil abuse | Returning null for "not found" vs Option<T> |
| B.4 | Builder/factory patterns prevent invalid construction | Partially-constructed objects allowed to exist |
| B.5 | Phantom types or typestate for protocol enforcement | State machine transitions not enforced at compile time |
| B.6 | Deserialization targets concrete types | Deserializing to Map<String, Any> then validating at runtime |
| Language-Specific Checks: |
#[non_exhaustive] usage; no unwrap in library code; correct Send/Sync boundsany; discriminated unions over string literals#[must_use] on Result-returning functionsHigher-level APIs should compose lower-level primitives, not duplicate logic.
| Check | Description | Anti-Pattern |
|---|---|---|
| C.1 | Higher abstractions compose lower ones | Convenience function re-implements core logic |
| C.2 | Common patterns extracted to reusable utilities | Same validation logic in 5 different functions |
| C.3 | Trait/interface hierarchies are coherent | Trait with 20 methods when 3 would compose |
| C.4 | Extension points via composition, not inheritance | Deep inheritance hierarchies |
| Remediation Patterns: |
+ OtherTrait) over monolithic traitsEach concern should have exactly one canonical path through the API.
| Check | Description | Anti-Pattern |
|---|---|---|
| D.1 | No redundant methods with overlapping functionality | get(), fetch(), retrieve() all doing the same thing |
| D.2 | Clear canonical path for common operations | 5 ways to create an instance, none obviously "correct" |
| D.3 | Deprecated paths actively marked and documented | Old API coexisting with new, neither marked deprecated |
| D.4 | No "stringly typed" APIs where enums would work | method: "GET" instead of Method::Get |
| Remediation Patterns: |
#[deprecated] or equivalent with migration guidanceNames should be precise, consistent, and minimize mental overhead.
| Check | Description | Anti-Pattern |
|---|---|---|
| E.1 | Consistent naming conventions throughout | getUserById vs fetch_user vs user.get |
| E.2 | Names reflect precise semantics | process() instead of validateAndTransformInput() |
| E.3 | No abbreviations without project-wide glossary | crnt_req_hdlr instead of current_request_handler |
| E.4 | Boolean methods/fields use predicate naming | valid instead of is_valid or has_permission |
| Remediation Patterns: |
Errors should be informative, typed, and recoverable where possible.
| Check | Description | Anti-Pattern |
|---|---|---|
| F.1 | Error types are domain-specific, not stringly-typed | Error::Generic(String) for everything |
| F.2 | Errors contain sufficient context for debugging | "failed" vs "failed to parse config at line 42: expected integer" |
| F.3 | Recoverable errors distinct from fatal panics | Panicking on user input validation failure |
| F.4 | Error variants map to distinct recovery paths | Single error type with no way to discriminate cause |
| Remediation Patterns: |
thiserror/anyhow (Rust), errors.Is/As (Go), custom error classes (TS/Python)For each component, produce:
## Audit: `module::Component`
### Summary
Brief description of the component's purpose and surface.
### Findings
| Dimension | Rating | Notes |
|:----------|:-------|:------|
| A. Minimal Surface | PASS | — |
| B. Type Safety | WARN | Uses `String` for user_id; newtype recommended |
| C. Composability | PASS | — |
| D. Monosemicity | FAIL | Redundant `create` and `new` methods |
| E. Naming | PASS | — |
| F. Error Handling | WARN | Generic error type; consider domain errors |
### Recommended Changes
1. **[D.1]** Consolidate `create` and `new` into single `new` constructor
2. **[B.1]** Introduce `UserId(String)` newtype
3. **[F.1]** Define `ComponentError` enum with specific variants
### Open Questions for User
1. Is backwards compatibility required for the `create` method?
2. Should `UserId` validation happen at construction time?
After completing component audits, assess systemic patterns.
Rate the overall API coherence:
| Criterion | Score (1-5) | Notes |
|---|---|---|
| Minimal Surface | ||
| Type Safety | ||
| Composability | ||
| Monosemicity | ||
| Naming Coherence | ||
| Error Handling | ||
| Overall |
Synthesize findings into prioritized action items.
## Remediation Plan: [Project Name]
### P0 — Critical
1. [ ] [Module] Brief description — Links to finding
### P1 — High
1. [ ] [Module] Brief description — Links to finding
### P2 — Medium
1. [ ] [Module] Brief description — Links to finding
### P3 — Low
1. [ ] [Module] Brief description — Links to finding
///)#[must_use] on Result-returning functions#[non_exhaustive] on enums for future-proofingunwrap()/expect() in library code pathsSend/Sync bounds on public typespub(crate) for internal-only itemsError and support Is/Asany in public API@dataclass or pydantic for structured dataEnum for finite sets of values__all__ defined in __init__.pyDict[str, Any] in public signatures┌─────────────────────────────────────────────────────────────────┐
│ Phase 0: Scope Definition │
│ → User approves scope │
├─────────────────────────────────────────────────────────────────┤
│ Phase 1: Surface Discovery │
│ → Full codebase ingestion │
│ → Surface map generated │
│ → User confirms completeness │
├─────────────────────────────────────────────────────────────────┤
│ Phase 2: Iterative Component Audit │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ For each component: │ │
│ │ → Analyze against 6 dimensions │ │
│ │ → Present findings │ │
│ │ → Await user acknowledgment │ │
│ │ → Proceed to next component │ │
│ └──────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Phase 3: Cross-Cutting Analysis │
│ → Consistency audit │
│ → Layering audit │
│ → Coherence scoring │
├─────────────────────────────────────────────────────────────────┤
│ Phase 4: Remediation Plan │
│ → Prioritized action items │
│ → User approves before implementation │
└─────────────────────────────────────────────────────────────────┘
This protocol enforces iterative human engagement to maintain agent coherence. Never skip checkpoints. If context becomes unclear or findings accumulate beyond what can be tracked, pause and summarize progress before continuing. The goal is not merely to identify issues, but to cultivate a shared understanding of API quality between the auditor and the user.
npx claudepluginhub nrdxp/predicate --plugin predicateEvaluates public API surface design, consistency in naming/parameters/returns/errors, documentation completeness, and alignment with language exemplars like pandas/requests/tokio. Use before releases.
Reviews public interfaces for API design quality including naming, method signatures, parameters, type safety, and REST endpoints. Use when evaluating usability and readability of class APIs or endpoints.
Designs and reviews public APIs, exported function signatures, module boundaries, types/interfaces, and code contracts to make correct usage easy and incorrect usage hard.