---
date: "2026-03-04T22:27:37.933286+00:00"
git_commit: 91e566efea0cbf53ba06a29b63317b7435609bd8
branch: master
topic: "Automatic OpenAPI Validation Pipelines for Backpressure Hooks"
tags: [research, openapi, validation, hooks, backpressure, linting]
status: complete
---

# Research: Automatic OpenAPI Validation Pipelines

## Research Question

What automatic validation pipelines exist for OpenAPI specs that can be integrated into the current Claude Code backpressure hook setup, running after the OpenAPI spec has been modified?

## Summary

The project already has a PostToolUse hook system that runs backend compile checks and frontend lint/type-checks after Edit/Write operations. Adding OpenAPI spec validation requires a new hook script that triggers specifically when `api.yaml` is modified. Several CLI tools support OpenAPI 3.1.0 validation — **Redocly CLI** is the strongest fit given the existing Node.js toolchain, MIT license, active maintenance, and zero-config baseline.

## Current Backpressure Setup

### Hook Architecture (`.claude/settings.json`)

The project uses Claude Code hooks for automated quality gates:

| Hook Event | Trigger | Scripts |
|---|---|---|
| `PostToolUse` | `Edit\|Write` tool calls | `backend-compile-check.sh`, `frontend-check.sh` |
| `Stop` | Agent attempts to stop | `run-tests.sh` |

### How Hooks Work

Each hook script:
1. Reads JSON from stdin containing `tool_input.file_path`
2. Pattern-matches the file path to decide if it should run
3. Executes validation (compile, lint, type-check, test)
4. Returns JSON with either success message or failure details
5. On failure: outputs `hookSpecificOutput` with error context (PostToolUse) or `{"decision":"block"}` (Stop)

### Existing Pattern for File Matching

```bash
# backend-compile-check.sh — matches Java files
case "$FILE_PATH" in
    */backend/src/*.java|backend/src/*.java) ;;
    *) exit 0 ;;
esac

# frontend-check.sh — matches TS/Vue files
case "$FILE_PATH" in
    */frontend/src/*.ts|*/frontend/src/*.vue|frontend/src/*.ts|frontend/src/*.vue) ;;
    *) exit 0 ;;
esac
```

An OpenAPI validation hook would use the same pattern:
```bash
case "$FILE_PATH" in
    */openapi/api.yaml|*/openapi/*.yaml) ;;
    *) exit 0 ;;
esac
```

### Existing OpenAPI Tooling in the Project

- **Backend:** `openapi-generator-maven-plugin` v7.20.0 generates Spring interfaces from `api.yaml` (`pom.xml:149-178`)
- **Frontend:** `openapi-typescript` v7.13.0 generates TypeScript types; `openapi-fetch` v0.17.0 provides type-safe client
- **No validation/linting tools** currently installed — no Redocly, Spectral, or other linter config exists

## Tool Evaluation

### Redocly CLI (`@redocly/cli`)

| Attribute | Value |
|---|---|
| OpenAPI 3.1 | Full support |
| Install | `npm install -g @redocly/cli` or `npx @redocly/cli@latest` |
| CLI | `redocly lint api.yaml` |
| License | MIT |
| Maintenance | Very active — latest v2.20.3 (2026-03-03), daily/weekly releases |
| GitHub | ~1.4k stars (Redocly ecosystem: 24k+ combined) |

**Checks:** Structural validity against OAS schema, configurable linting rules (naming, descriptions, operation IDs, security), style/consistency enforcement. Built-in rulesets: `minimal`, `recommended`, `recommended-strict`. Zero-config baseline works immediately. Custom rules via `redocly.yaml`.

**Fit for this project:** Node.js already in the toolchain (frontend). `npx` form requires no permanent install. MIT license compatible with GPL-3.0. The `@redocly/openapi-core` package is already present as a transitive dependency of `openapi-typescript` in `node_modules`.

### Spectral (`@stoplight/spectral-cli`)

| Attribute | Value |
|---|---|
| OpenAPI 3.1 | Full support (since v6.x) |
| Install | `npm install -g @stoplight/spectral-cli` |
| CLI | `spectral lint api.yaml` |
| License | Apache 2.0 |
| Maintenance | Active — latest v6.15.0 (2025-04-22), slower cadence |
| GitHub | ~3k stars |

**Checks:** Schema compliance, missing descriptions/tags/operationIds, contact/license metadata. Highly extensible custom rulesets via YAML/JS. Configurable severity levels.

**Fit for this project:** Well-established industry standard. Apache 2.0 compatible with GPL. Less actively maintained than Redocly (10 months since last release). Heavier custom ruleset system may be over-engineered for current needs.

### Vacuum (`daveshanley/vacuum`)

| Attribute | Value |
|---|---|
| OpenAPI 3.1 | Full support (via libopenapi) |
| Install | `brew install daveshanley/vacuum/vacuum` or Go binary |
| CLI | `vacuum lint api.yaml` |
| License | MIT |
| Maintenance | Active — latest release 2025-12-22 |
| GitHub | ~1k stars |

**Checks:** Structural validation, Spectral-compatible rulesets, OWASP security checks, naming conventions, descriptions/examples/tags. Single Go binary — no runtime dependencies.

**Fit for this project:** Zero-dependency binary is appealing for CI. However, adds a non-Node.js tool dependency when the project already has Node.js. Spectral ruleset compatibility is a plus for portability.

### oasdiff (`oasdiff/oasdiff`)

| Attribute | Value |
|---|---|
| OpenAPI 3.1 | Beta |
| Install | `brew install oasdiff` or Go binary |
| CLI | `oasdiff breaking base.yaml revision.yaml` |
| License | Apache 2.0 |
| Maintenance | Active — latest v1.11.10 (2026-02-05) |
| GitHub | ~1.1k stars |

**Checks:** 300+ breaking change detection rules (paths, parameters, schemas, security, headers, enums). Requires two spec versions to compare — not a standalone validator.

**Fit for this project:** Different category — detects breaking changes between spec versions, not structural validity. Useful as a CI-only check comparing `HEAD~1` vs `HEAD`. OAS 3.1 support is still beta.

### Not Recommended

- **swagger-cli:** Abandoned, no OAS 3.1 support
- **IBM OpenAPI Validator:** Active but opinionated IBM-specific rules add configuration overhead for no benefit

## Tool Comparison Matrix

| Tool | OAS 3.1 | License | Last Release | Stars | Runtime | Category |
|---|---|---|---|---|---|---|
| **Redocly CLI** | Full | MIT | 2026-03-03 | ~1.4k | Node.js | Lint + validate |
| **Spectral** | Full | Apache 2.0 | 2025-04-22 | ~3k | Node.js | Lint |
| **Vacuum** | Full | MIT | 2025-12-22 | ~1k | Go binary | Lint + validate |
| **oasdiff** | Beta | Apache 2.0 | 2026-02-05 | ~1.1k | Go binary | Breaking changes |

## Integration Pattern

### Hook Script Structure

An OpenAPI validation hook would follow the existing pattern in `.claude/hooks/`:

```bash
#!/usr/bin/env bash
set -euo pipefail

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_input',{}).get('file_path',''))" 2>/dev/null || echo "")

# Only run for OpenAPI spec files
case "$FILE_PATH" in
    */openapi/*.yaml|*/openapi/*.yml) ;;
    *) exit 0 ;;
esac

cd "$CLAUDE_PROJECT_DIR/backend"

# Run validation
if OUTPUT=$(npx @redocly/cli@latest lint src/main/resources/openapi/api.yaml --format=stylish 2>&1); then
    echo '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"✓ OpenAPI spec validation passed."}}'
else
    ESCAPED=$(echo "$OUTPUT" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))")
    echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":$ESCAPED}}"
fi
```

### Registration in `.claude/settings.json`

The hook would be added to the existing `PostToolUse` array alongside the compile and lint hooks:

```json
{
  "type": "command",
  "command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/openapi-validate.sh\"",
  "timeout": 120
}
```

### Configuration (Optional)

A `redocly.yaml` in the project root or `backend/` directory can customize rules:

```yaml
extends:
  - recommended

rules:
  operation-operationId: error
  tag-description: warn
  no-ambiguous-paths: error
```

## Code References

- `.claude/settings.json:1-32` — Hook configuration (PostToolUse + Stop events)
- `.claude/hooks/backend-compile-check.sh` — Java file detection pattern + compile check
- `.claude/hooks/frontend-check.sh` — TS/Vue file detection pattern + type-check + lint
- `.claude/hooks/run-tests.sh` — Stop hook with test execution and block/approve logic
- `backend/pom.xml:149-178` — openapi-generator-maven-plugin configuration
- `backend/src/main/resources/openapi/api.yaml` — The OpenAPI 3.1.0 spec to validate

## Open Questions

- Should the validation use a pinned version (`npx @redocly/cli@1.x.x`) or latest? Pinned is more reproducible; latest gets rule updates automatically.
- Should a `redocly.yaml` config be added immediately with the `recommended` ruleset, or start with zero-config (structural validation only) and add rules incrementally?
- Is breaking change detection (oasdiff) desirable as a separate CI check, or is structural validation sufficient for now?
