Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions agents/api-contract-guardian/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
## API Contract Guardian

Detect and gate breaking API changes across OpenAPI and GraphQL in monorepos or multi-repo setups. Ideal for PR checks, release validation, and cross-service compatibility.

### What it does
- Scans OpenAPI 3.x (YAML/JSON) and GraphQL SDL files
- Diffs current branch against `main` (configurable)
- Flags breaking vs. non-breaking changes with severity
- Supports allowlist for intentional breakages with IDs
- Produces a machine-readable JSON report and CI-friendly pass/fail

### Why it stands out
- Multi-spec support (OpenAPI + GraphQL)
- Monorepo-aware service grouping
- Clear migration recommendations
- Zero-code setup via TOML agent config

### Inputs (arguments)
- `openapi_paths`: comma-separated globs for OpenAPI files. Example: `**/openapi.{yml,yaml,json}`
- `graphql_paths`: comma-separated globs for GraphQL SDL files. Example: `services/**/{schema.graphql,schema.gql}`
- `target_branch`: branch to compare against (default: `main`)
- `allowlist_file`: JSON/YAML of approved breaking changes
- `fail_on`: minimum severity to fail (`minor|moderate|major|critical`, default: `major`)

### Output
Structured JSON including `summary`, `findings[]`, `approved`, and `requires_changes` per the `agent.toml` `output_schema`.

### Install Qodo Command
```bash
npm install -g @qodo/command
qodo --version
```

### Run locally (this repo)
```bash
cd /Users/muhammadumairshahid/agents
qodo api_contract_guardian \
--agent-file=agents/api-contract-guardian/agent.toml \
--set openapi_paths="**/openapi.{yml,yaml,json}" \
--set graphql_paths="**/*.{graphql,gql}" \
--set target_branch=main \
--set fail_on=major
```

### Run against another repository/workspace
Option A: clone the target repo and run from its root using a remote or local agent file.
```bash
git clone https://github.com/your-org/your-service.git
cd your-service
qodo api_contract_guardian \
--agent-file=/Users/muhammadumairshahid/agents/agents/api-contract-guardian/agent.toml \
--set openapi_paths="**/openapi.{yml,yaml,json}" \
--set graphql_paths="**/*.{graphql,gql}" \
--set target_branch=main
```

Option B: run from anywhere with a remote `--agent-file` URL (after you publish a raw link).

### Allowlist format (example)
```yaml
rules:
- id: OAPI-REMOVE-USER-AGE
file: services/user/openapi.yaml
path: /users GET
category: response.property.removal
expires: 2026-01-01
justification: Field removed; deprecation period communicated.
```

### CI: GitHub Actions (example)
```yaml
name: API Contract Guardian
on:
pull_request:
branches: [ main ]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm i -g @qodo/command
- name: Run API Contract Guardian
run: |
qodo api_contract_guardian \
--agent-file=agents/api-contract-guardian/agent.toml \
--set openapi_paths="**/openapi.{yml,yaml,json}" \
--set graphql_paths="**/*.{graphql,gql}" \
--set target_branch=${{ github.base_ref || 'main' }} \
--ci
```

### Local smoke test samples
Use the samples in `examples/samples/` to sanity-check behavior:
```bash
cd /Users/muhammadumairshahid/agents/agents/api-contract-guardian/examples/samples
git checkout -b test
# introduce a breaking change in the sample and run from repo root
cd /Users/muhammadumairshahid/agents
qodo api_contract_guardian --agent-file=agents/api-contract-guardian/agent.toml --ci
```

### Notes
- The agent verifies function/tool calls and structured output; business logic is kept declarative in the prompt to align with contest guidance.
- For monorepos, add service name hints by placing schemas under `services/<service>/`.


102 changes: 102 additions & 0 deletions agents/api-contract-guardian/agent.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
version = "1.0"

[commands.api_contract_guardian]
description = "Enforce API contract consistency across branches and services using OpenAPI/GraphQL schemas; fail PRs on breaking changes."

instructions = """
You are an API contract guardian. Your job is to detect, explain, and gate breaking API changes across services and repositories.

Scope:
- Compare current branch API schemas (OpenAPI 3.x JSON/YAML and GraphQL SDL) against the target branch (default: main) or a baseline file.
- Report all breaking changes with severity, location, and human‑readable guidance.
- Suggest migration notes and safe upgrade paths.
- Optionally allow documented exceptions via an allowlist file.

Inputs:
- openapi_paths: comma‑separated globs for OpenAPI files (e.g., "**/openapi.{yml,yaml,json}")
- graphql_paths: comma‑separated globs for GraphQL SDL files (e.g., "**/*.graphql,**/*.gql")
- target_branch: git ref to compare schemas against (default: main)
- allowlist_file: path to a YAML/JSON file listing approved breaking changes
- fail_on: minimum severity to fail the run (minor|moderate|major|critical)

Policy (breaking examples):
- Removing operations/paths/types/fields
- Narrowing request/response shapes (required adds, enum removal, type narrowing)
- Status code removals; content type removals
- Response schema changes that are backward incompatible
- GraphQL: field/type removals, non‑null tightening, enum value removals, input arg additions without defaults

Process:
1. Discover schema files from provided paths; if none provided, use sensible defaults.
2. Checkout and read baseline schemas from target_branch.
3. Diff schemas using specialized rules per spec.
4. Summarize breaking vs non‑breaking deltas; map to severity.
5. Apply allowlist exceptions to downgrade or ignore specific findings.
6. Output a structured JSON report and a concise summary. Set approved=false if any finding >= fail_on.

Edge cases:
- Missing baseline: treat as initial introduction; mark as non‑breaking unless configured otherwise.
- Non‑parseable files: report with severity=critical and continue others.
- Monorepo: group results per workspace/service directory.

Return actionable recommendations for each breaking change.
"""

arguments = [
{ name = "openapi_paths", type = "string", required = false, description = "Comma-separated globs for OpenAPI files" },
{ name = "graphql_paths", type = "string", required = false, description = "Comma-separated globs for GraphQL SDL files" },
{ name = "target_branch", type = "string", required = false, default = "main", description = "Branch to compare against" },
{ name = "allowlist_file", type = "string", required = false, description = "Path to allowed exceptions file (yaml/json)" },
{ name = "fail_on", type = "string", required = false, default = "major", description = "Minimum severity to fail: minor|moderate|major|critical" }
]

tools = ["git", "filesystem"]
execution_strategy = "act"

output_schema = """
{
"type": "object",
"properties": {
"summary": {
"type": "object",
"description": "Summary statistics for files and services compared, and severities encountered",
"properties": {
"services": {"type": "number"},
"files_compared": {"type": "number"},
"breaking": {"type": "number"},
"non_breaking": {"type": "number"},
"severity_max": {"type": "string", "enum": ["none","minor","moderate","major","critical"]}
},
"required": ["files_compared","breaking","non_breaking","severity_max"]
},
"findings": {
"type": "array",
"description": "List of detected API contract changes with severity and recommendations",
"items": {
"type": "object",
"properties": {
"type": {"type": "string", "enum": ["openapi","graphql"]},
"service": {"type": "string"},
"file": {"type": "string"},
"path": {"type": "string", "description": "Schema path, e.g., /users GET or GraphQL Type.Field"},
"category": {"type": "string"},
"severity": {"type": "string", "enum": ["minor","moderate","major","critical"]},
"title": {"type": "string"},
"description": {"type": "string"},
"recommendation": {"type": "string"},
"allowed_by": {"type": "string", "description": "Allowlist rule id if matched"}
},
"required": ["type","file","category","severity","title","description"]
}
},
"approved": {"type": "boolean", "description": "True if the change set passes the configured severity policy"},
"requires_changes": {"type": "boolean", "description": "True if authors must address reported breaking changes"}
},
"required": ["summary","findings","approved","requires_changes"]
}
"""

exit_expression = "approved"



24 changes: 24 additions & 0 deletions agents/api-contract-guardian/examples/ci/github-actions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: API Contract Guardian
on:
pull_request:
branches: [ main ]
jobs:
contract:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm i -g @qodo/command
- name: Run API Contract Guardian
run: |
qodo api_contract_guardian \
--agent-file=agents/api-contract-guardian/agent.toml \
--set openapi_paths="**/openapi.{yml,yaml,json}" \
--set graphql_paths="**/*.{graphql,gql}" \
--set target_branch=${{ github.base_ref || 'main' }} \
--ci

23 changes: 23 additions & 0 deletions agents/api-contract-guardian/examples/samples/openapi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
openapi: 3.0.3
info:
title: Sample Users API
version: 1.0.0
paths:
/users:
get:
summary: List users
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
type: string
name:
type: string

9 changes: 9 additions & 0 deletions agents/api-contract-guardian/examples/samples/schema.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
type User {
id: ID!
name: String!
}

type Query {
users: [User!]!
}