| name | contract-test |
| description | Run consumer-driven contract tests (pact-lite, no broker). Guides through generating, syncing, and verifying contracts between services. Supports both single-service and multi-service project-wide runs. |
| model-tier | premium |
| model | opus |
| effort | high |
| version | 2.0.0 |
| author | flurdy |
Contract Test (Pact-Lite)
Consumer-driven contract testing without a broker. Contracts are JSON files exchanged directly between consumer and provider services via the filesystem.
When to Use
- After modifying a connector, API client, or endpoint interface (request/response models, URLs, headers)
- When adding a new REST endpoint that other services will consume
- To verify contracts haven't broken after upstream/downstream changes
- When a consumer or provider test is failing and you need to re-sync contracts
- After bulk changes that touch multiple services — run project-wide to verify all contracts
Usage
/contract-test # Auto-detect: run contract tests for current service
/contract-test consumer # Run consumer tests to generate contract files
/contract-test sync # Copy generated contracts to provider services
/contract-test provider # Run provider verification against current contracts
/contract-test full # Single-service: consumer + sync + provider for current service
/contract-test full all # Multi-service: ALL consumers → sync → normalize → ALL providers
/contract-test full <svc> <svc> # Multi-service: named consumers → sync → normalize → affected providers
/contract-test status # Show contract test coverage and staleness
Concepts
Consumer-Driven Contracts (Pact-Lite)
This is a lightweight version of the Pact workflow that works without a broker:
- Consumer services define expected interactions with providers in test code
- Consumer tests generate contract files (JSON) describing those expectations
- Contract files are copied from consumer to provider (no broker — just filesystem)
- Provider services verify they satisfy all consumer contracts
Roles
- Consumer: A service that calls another service's API. Generates
.json contract files.
- Provider: A service that exposes an API. Verifies contracts from its consumers.
- A service can be both consumer and provider for different dependencies.
Instructions
Step 0: Detect Project Context
Before running any command, detect the project context:
-
Identify the scope — is this a single-service or multi-service run?
full all or full <svc1> <svc2> → multi-service (see Multi-Service Workflow below)
- Everything else → single-service, identify the current service from the working directory or ask the user
-
Detect build tool and language:
| Indicator | Language | Build Tool | Test Command |
|---|
build.sbt | Scala | sbt | sbt test or sbt "testOnly -- -n ContractTest" |
package.json | JS/TS | npm/yarn/pnpm | npm test -- --grep contract |
go.mod | Go | go | go test ./... -run Contract |
pom.xml | Java | maven | mvn test -Dtest=*Contract* |
build.gradle* | Java/Kotlin | gradle | gradle test --tests '*Contract*' |
Cargo.toml | Rust | cargo | cargo test contract |
pyproject.toml / setup.py | Python | pytest | pytest -k contract |
-
Check for Makefile aliases — prefer these over raw commands:
grep -E '(test-contract|contract-test|pact)' Makefile 2>/dev/null
Common Makefile targets (use these if available):
make test-contract — run contract tests (consumer or provider)
make test-contract-only — run only contract tests
make pact-publish — copy generated contracts to provider services
make sync-pacts — sync all contracts across services (root Makefile)
make normalize-pacts — normalize generated UUIDs/dates to reduce noise
-
Determine service role:
- Look for consumer test files:
*Consumer*.{scala,java,ts,js,go,py,rs}, *Pact*.{...}
- Look for provider verification files:
*Verify*Pact*, *Provider*Verify*
- Check for contract output directory:
target/pacts/, pacts/, contracts/
- Check for contract input directory:
test/resources/pacts/, src/test/resources/pacts/, contracts/
Single-Service Workflow (consumer / sync / provider / full)
Step 1: Generate Contracts (Consumer Side)
Run consumer contract tests to generate contract JSON files:
make test-contract
After running, verify contracts were generated:
find . -name "*.json" -path "*/pacts/*" -newer . -mmin -5 2>/dev/null
ls target/pacts/ 2>/dev/null || ls pacts/ 2>/dev/null || ls contracts/ 2>/dev/null
Step 2: Sync Contracts to Providers
Copy generated contract files from consumer to provider services:
make pact-publish
make sync-pacts
If no Makefile target exists, copy manually:
cp target/pacts/*-account-provider.json ../account/test/resources/pacts/
Important: The contract filename typically follows the pattern:
<consumer-name>-consumer-<provider-name>-provider.json
Step 3: Normalize (if available)
If the project has a normalize step, run it after sync to reduce noisy diffs:
make normalize-pacts
This replaces generated UUIDs and timestamps with deterministic placeholders so git diffs only show meaningful contract changes.
Step 4: Verify Contracts (Provider Side)
Navigate to each affected provider service and run verification:
cd ../<provider-service>
make test-contract
Step 5: Report Results
After running, report:
- Which contracts were generated/synced/verified
- Any failures with clear indication of which consumer-provider pair failed
- Suggestions for fixing broken contracts
Multi-Service Workflow (full all / full )
For multi-service projects, the full subcommand with all or named services runs the complete project-wide workflow. The ordering is critical: all consumer generation must complete before syncing, and syncing must complete before provider verification.
Phase 1: Discover consumer and provider services
Identify which services are consumers and which are providers:
- Check project documentation (e.g.
docs/pact-workflow.md) for the definitive list
- Or scan for consumer test files and provider verification files across all services
- If the user specified service names, those are the consumer services to run; providers are determined by which providers those consumers talk to
Phase 2: Run ALL consumer tests (generate pacts)
Run make test-contract in every consumer service. All consumers must succeed before proceeding to sync.
Execute consumers sequentially (each may start an sbt process):
cd <consumer1> && make test-contract
cd <consumer2> && make test-contract
If any consumer fails, stop and report the failure. Do not proceed to sync with partial pacts — that would overwrite good provider pacts with stale ones.
Track results as you go:
| Consumer | Status | Pacts Generated |
|-------------|--------|-----------------|
| admin | PASS | 7 |
| hosted | PASS | 7 |
| dispatch | FAIL | - |
Phase 3: Sync ALL pacts at once
After all consumers pass, sync everything in one operation:
make sync-pacts
This is more reliable than per-service make pact-publish because the root sync script covers all known consumer→provider relationships in one pass.
Phase 4: Normalize pacts (if available)
make normalize-pacts
Phase 5: Run ALL provider verifications
Run make test-contract in every affected provider service:
cd <provider1> && make test-contract
cd <provider2> && make test-contract
If full all was specified, run all providers. If specific consumers were named, only run providers that those consumers talk to (determined from the sync script or documentation).
Track results:
| Provider | Status | Consumers Verified |
|-------------|--------|--------------------|
| account | PASS | admin, digest, patrol |
| messagequeue| PASS | admin, digest, dispatch, hosted, patrol, reconciler |
| membership | FAIL | admin, digest, hosted, patrol, reconciler |
Phase 6: Report summary
Report:
- Total consumers tested, passed, failed
- Total providers verified, passed, failed
- Any specific consumer→provider pair failures
- Suggestions for fixing broken contracts
- Remind about committing pact file changes if any were updated during sync
Subcommand Details
consumer
Run consumer contract tests for the current service. Generates contract files but does not sync or verify.
sync
Copy existing contract files from consumer output directories to provider input directories. Does not run any tests. Prefer root-level make sync-pacts over per-service make pact-publish when available.
provider
Run provider verification tests for the current service. Assumes contracts are already synced.
full (no args)
Single-service workflow: consumer -> sync -> normalize -> provider for the current service and its affected providers. See Single-Service Workflow above.
full all
Multi-service workflow: run ALL consumers -> sync ALL pacts -> normalize -> verify ALL providers. Use this after broad changes or to validate the entire contract test suite. See Multi-Service Workflow above.
full <svc1> <svc2> ...
Multi-service workflow for named consumer services only. Runs the named consumers -> sync -> normalize -> verifies only the providers those consumers talk to. Useful when you know which services changed.
status
Show an overview of contract test health:
- List all consumer-provider relationships detected
- Show last-modified timestamps of contract files
- Flag any contracts where consumer output is newer than provider copy (stale)
- Flag any providers missing contracts
Error Handling
Consumer test fails
- The contract definition in test code doesn't match expectations
- Fix the consumer test, then re-run
consumer
Provider verification fails
- The provider's actual API doesn't match the consumer's contract
- Either: (a) update the provider API to match, or (b) update the consumer contract if the change is intentional
- After fixing, re-run the full workflow
Stale contracts
- Consumer output is newer than provider copy
- Run
sync to update provider copies, then verify
Noisy diffs after sync
- Pact libraries often regenerate random UUIDs, timestamps, or metadata in contract JSON even when the actual contract hasn't changed
- Run
make normalize-pacts (if available) after sync to replace generated values with deterministic placeholders
- If no normalizer exists, inspect the diff — if only generated IDs changed, the sync can be skipped or the noise committed as-is
Multi-service run: consumer failure
- If any consumer fails during
full all, stop immediately — do not sync or verify
- Fix the failing consumer test, then restart the
full run
- Syncing with partial/stale pacts risks overwriting good provider copies and causing cascading false failures
Missing contracts
- A consumer-provider relationship exists in code but no contract file found
- Create consumer contract tests first, then run
full
Adapting to a New Project
This skill works with any project that follows the consumer-driven contract pattern. To adopt it in a new project:
-
Add Makefile targets for consistency:
test-contract:
@echo "Running contract tests..."
pact-publish:
@echo "Publishing contracts to providers..."
-
Organize contract files:
- Consumer output:
target/pacts/ or contracts/output/
- Provider input:
test/resources/pacts/ or contracts/input/
-
Name contract files consistently:
<consumer>-consumer-<provider>-provider.json
-
Tag contract tests so they can be run independently of unit tests
Rules
- NEVER skip contract tests when modifying connectors or API interfaces
- ALWAYS sync contracts after generating — stale provider copies cause false failures
- ALWAYS verify on the provider side after syncing — sync alone doesn't prove compatibility
- ALWAYS normalize after syncing (if available) to keep diffs clean
- In multi-service runs: ALL consumers must pass before syncing — never sync partial results
- In multi-service runs: prefer root-level
make sync-pacts over per-service make pact-publish
- Connectors are service boundary code — treat any change as a potential breaking contract change
- If a Makefile target exists, prefer it over raw commands
- Report clearly which consumer-provider pairs were tested and their pass/fail status