| name | api-audit |
| description | Protocol for auditing API surface coherence and type safety.
Trigger when:
- Evaluating API designs, interface type safety, or design elegance.
- Prompt contains: /api-audit, API surface, API coherence, type safety.
|
API Coherence Audit Protocol
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.
Phase 0: Scope Definition
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).
Checkpoint: Present scope to user for approval before proceeding.
Phase 1: Surface Discovery (Full Codebase Ingestion)
Objective: Build a complete mental model of the public API surface before any analysis.
1.1 Enumerate Public Surface
For each entry point, catalog:
1.2 Generate Surface Map
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:
- Is this complete? Are there missing entry points?
- Are there items here that should NOT be public?
Phase 2: Iterative Component Audit
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.
Audit Dimensions
For each component, evaluate against all dimensions. Rate each: [PASS | WARN | FAIL]
Dimension A: Minimal Surface (Encapsulation)
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: | | |
- Use visibility modifiers (
pub(crate), internal, private)
- Builder pattern for complex construction
- Facade pattern to simplify overly complex subsystems
Dimension B: Type Safety & Error Unrepresentability
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: | | |
- Rust: Proper
#[non_exhaustive] usage; no unwrap in library code; correct Send/Sync bounds
- Go: Proper error wrapping; unexported fields for invariants; meaningful zero values
- TypeScript: Strict mode; no
any; discriminated unions over string literals
- Python: Type hints on public API; @dataclass for structured data; no Dict[str, Any] leakage
Remediation Patterns:
- Parse, don't validate (deserialize to concrete types)
- Newtype pattern for domain primitives
- Typestate pattern for state machines
#[must_use] on Result-returning functions
Dimension C: Composability & Self-Reuse
Higher-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: | | |
- Extract shared logic to internal helpers, call from public API
- Use decorator/wrapper types for cross-cutting concerns
- Prefer trait composition (
+ OtherTrait) over monolithic traits
Dimension D: Monosemicity (One Path Per Concern)
Each 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: | | |
- Consolidate redundant methods; deprecate legacy paths
- Document the "happy path" prominently
- Use
#[deprecated] or equivalent with migration guidance
Dimension E: Naming & Cognitive Load
Names 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: | | |
- Establish and document naming conventions in CONTRIBUTING.md
- Rename for precision; use refactoring tools for safe migration
Dimension F: Error Handling Coherence
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: | | |
- Define enum error types per module/subsystem
- Use
thiserror/anyhow (Rust), errors.Is/As (Go), custom error classes (TS/Python)
- Include structured context (file paths, line numbers, input values)
Iteration Template
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?
Checkpoint: Present findings for this component. Await acknowledgment before proceeding.
Phase 3: Cross-Cutting Analysis
After completing component audits, assess systemic patterns.
3.1 Consistency Audit
3.2 Layering Audit
3.3 Coherence Score
Rate the overall API coherence:
| Criterion | Score (1-5) | Notes |
|---|
| Minimal Surface | | |
| Type Safety | | |
| Composability | | |
| Monosemicity | | |
| Naming Coherence | | |
| Error Handling | | |
| Overall | | |
Phase 4: Remediation Plan
Synthesize findings into prioritized action items.
Priority Levels
- P0 (Critical): Type safety gaps enabling invalid states; unhandled error conditions
- P1 (High): Encapsulation violations; significant duplication
- P2 (Medium): Naming inconsistencies; documentation gaps
- P3 (Low): Style preferences; minor redundancies
Remediation Template
## 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
Checkpoint: Present remediation plan for user approval before any code changes.
Appendix: Language-Specific Checklists
Rust
Go
TypeScript
Python
Workflow Execution Summary
┌─────────────────────────────────────────────────────────────────┐
│ 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 │
└─────────────────────────────────────────────────────────────────┘
Final Directive
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.