Audit health of contract tests across services — staleness, sync gaps, uncommitted pacts, missing tests.
Scanned 9/12/2026
Install to Claude Code
npx -y skills add aibot88/sec_skill_store --skill contract-check --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Contract Check?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/aibot88-contract-check)More formats (shields.io, HTML) on the badges page.
---
name: contract-check
description: "Audit health of contract tests across services — staleness, sync gaps, uncommitted pacts, missing tests."
allowed-tools: "Read,Grep,Glob,Bash(./scripts/contract-check:*),Bash(./scripts/mgit:*),Bash(ls:*),Bash(chmod:*),Skill,AskUserQuestion"
version: "1.0.0"
author: "flurdy"
---
# Contract Check — Pact Health Auditor
Audits the health of consumer-driven contract tests across all letterbox services. Surfaces staleness, sync gaps, uncommitted pacts, missing tests, documentation drift, and disabled tests.
This is a **read-only audit** — it does not run tests or modify files. Use `/contract-test` to execute contract test workflows.
## When to Use
- Starting a work session to check contract health
- After modifying connectors or API endpoints
- Before a release to verify all contracts are synced and committed
- Periodically to catch drift and gaps
## Usage
```
/contract-check # Full audit (all checks)
/contract-check status # Summary dashboard only
/contract-check stale # Consumer output newer than provider copy
/contract-check uncommitted # Pact files not committed in service repos
/contract-check sync-gaps # Pairs missing from sync-pacts.sh
/contract-check missing # Connectors with no consumer tests
/contract-check docs # Documentation drift in pact-workflow.md
/contract-check disabled # Contract tests excluded from default builds
/contract-check <service> # All checks scoped to one service
```
## Setup
On first run, ensure the script symlink exists:
```bash
ln -sfn "$SKILLS_DIR/contract-check/scripts/contract-check.sh" ./scripts/contract-check
chmod +x ./scripts/contract-check
```
Where `$SKILLS_DIR` resolves to `${CLAUDE_HOME:-$HOME/.claude}/skills`.
## Instructions
### Step 0: Ensure Setup
Check that `./scripts/contract-check` exists and is executable. If not, create the symlink as shown in Setup above.
### Step 1: Parse Subcommand
Parse the user's argument:
- `(none)` or `full` → Run all checks (mechanical + semantic)
- `status` → Run all checks but only show the summary table
- `stale` → Run `./scripts/contract-check stale`
- `uncommitted` → Run `./scripts/contract-check uncommitted`
- `sync-gaps` → Run `./scripts/contract-check sync-gaps`
- `missing` → Run semantic Missing Consumer Tests check (Step 3)
- `docs` → Run semantic Documentation Drift check (Step 4)
- `disabled` → Run semantic Disabled Tests check (Step 5)
- `<service>` → Run all checks scoped to that service
- `matrix` → Run `./scripts/contract-check matrix`
### Step 1b: Normalize Before Checking (recommended)
Before running the `uncommitted` check (or `all`/`full`), suggest running `make normalize-pacts` first. Pact libraries regenerate random UUIDs, dates, and strings on every test run, causing noisy git diffs that aren't real contract changes. The normalizer replaces these with deterministic placeholders.
**Note:** `normalize-pacts.py` currently only normalizes values referenced by `generators.body` metadata. UUIDs in `providerStates.params`, `request.path`, `request.headers`, and `response.headers` are NOT yet normalized — those may still show as noise in uncommitted diffs. Flag these as noise in the report when the diff is UUID-only.
### Step 2: Mechanical Checks (via script)
Run the appropriate script subcommand:
```bash
./scripts/contract-check all # or stale, uncommitted, sync-gaps, matrix
```
Parse the script output. Each line starts with a status keyword:
- `OK` — no action needed
- `STALE` — consumer pact is newer than provider copy
- `DIFFERS` — same age but content differs
- `MISSING_PROVIDER` — provider does not have this pact file
- `UNCOMMITTED` — pact file has uncommitted changes
- `COVERED` — pair is in sync-pacts.sh
- `NOT_SYNCED` — pair has consumer pact but is missing from sync-pacts.sh
- `STALE_SYNC` — pair is in sync-pacts.sh but consumer has no pact file (not built)
- `CLEAN` — no issues found
- `NO_DATA` — no consumer pact files found
- `SUMMARY` — counts for the check
### Step 3: Missing Consumer Tests (LLM-driven)
For each consumer service, compare connectors against consumer test files:
1. **Find connectors** — Glob for connector files:
- Play services (admin, hosted, registration, profile, web): `<service>/app/connectors/*Connector.scala`
- Scala 3 services (digest, patrol, reconciler, dispatch, etc.): `<service>/src/main/scala/**/connectors/*Connector.scala`
2. **Find consumer tests** — Glob for consumer test files:
- Play services: `<service>/test/contract/*Consumer*.scala`
- Scala 3 services: `<service>/src/test/scala/**/contract/*Consumer*.scala`
3. **Extract provider name** from connector filename: strip `Connector` suffix, lowercase. E.g., `AccountConnector.scala` → `account`.
4. **Exclude external connectors** that are not internal services:
- Braintree, Stripe, SES, Spam, Web, Api (base class), ConnectorConfiguration, ConnectorModule
5. **Cross-reference**: For each connector, check if a matching consumer test exists. Report connectors with no consumer test.
Output format:
```
### Missing Consumer Tests
| Service | Connector | Provider | Has Consumer Test |
|---------|-----------|----------|-------------------|
| reconciler | EventConnector | event | NO |
| reconciler | MessageQueueConnector | messagequeue | NO |
```
### Step 4: Documentation Drift (LLM-driven)
1. Read `docs/pact-workflow.md`
2. Run `./scripts/contract-check matrix` to get actual relationships
3. Compare the documented consumer→provider relationships against the matrix
4. Flag:
- Consumers listed in docs but not in matrix (or vice versa)
- Providers listed in docs but not in matrix (or vice versa)
- Stale TODOs or notes that have been resolved
- Missing entries for newer services (digest, patrol, reconciler, profile)
### Step 5: Disabled Tests (LLM-driven)
1. Search for contract test exclusions in build.sbt files:
- Pattern: `-l tags.ContractTest` or `--exclude-tags=ContractTest`
- Check both active and commented-out exclusions
2. For services with active exclusions, check if the Makefile `test-contract` target overrides it (e.g., with `set Test/testOptions := Nil`)
3. Report:
- Services where `sbt test` silently skips contract tests
- Whether the Makefile target correctly overrides the exclusion
- Services lacking a `test-contract` Makefile target entirely
### Step 6: Format Report
Combine all findings into a health report:
```markdown
# Contract Health Report
## Summary
| Check | Status | Details |
|------------------|--------|--------------------------------|
| Staleness | PASS/WARN/FAIL | X stale / Y total |
| Uncommitted | PASS/WARN | X files across Y services |
| Sync coverage | PASS/FAIL | X/Y pairs in sync-pacts.sh |
| Missing tests | PASS/WARN | X connectors without tests |
| Documentation | PASS/FAIL | X items out of date |
| Disabled tests | PASS/INFO | X services exclude by default |
## Staleness
[details from script output]
## Uncommitted Pact Files
[details from script output]
## Sync Coverage Gaps
[details from script output]
## Missing Consumer Tests
[details from LLM analysis]
## Documentation Drift
[details from LLM analysis]
## Disabled Tests
[details from LLM analysis]
## Recommended Actions (priority order)
1. [most impactful fix first]
2. ...
```
Status thresholds:
- **PASS**: No issues found
- **INFO**: Informational, no action required
- **WARN**: Issues found but not blocking
- **FAIL**: Significant gaps that should be addressed
### Step 7: Offer Remediation
After presenting the report, ask the user:
> Would you like me to create beads for the issues found? I can triage them as P3 tasks with appropriate service labels.
If the user agrees, use `/triage` to create beads for actionable findings. Group related findings into single beads where appropriate (e.g., "Add digest, patrol, reconciler to sync-pacts.sh" as one bead rather than three).
## Scoped Checks (single service)
When invoked with a service name (`/contract-check dispatch`):
1. Run mechanical checks and filter output to only show lines involving that service (as consumer or provider)
2. Run semantic checks scoped to that service only
3. Present a focused report for just that service
## Error Handling
- **Script not found**: Create the symlink (see Setup)
- **mgit not found**: Error — must be run from within the letterbox project root
- **No consumer pact files**: Report that consumer tests haven't been run; suggest `make test-contract` in consumer services
- **Service directory missing**: Skip gracefully, note in output
## Known Provider Pact Directories
Most providers use `test/resources/pacts/`. Exceptions:
- **event**: `src/test/resources/pacts/`
- **membership**: `src/test/resources/pacts/`
## Known External Connectors (exclude from missing-test checks)
Braintree, Stripe, SES, SpamConfiguration, WebConfiguration, ApiConnector (abstract base), ConnectorConfiguration, ConnectorModule
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!