bmad-method-test-architecture-enterprise 1.23.4 → 1.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +19 -0
  3. package/docs/how-to/customization/integrate-pactjs-utils.md +47 -0
  4. package/docs/reference/knowledge-base.md +5 -5
  5. package/package.json +1 -1
  6. package/src/agents/bmad-tea/resources/knowledge/contract-testing.md +48 -3
  7. package/src/agents/bmad-tea/resources/knowledge/pact-broker-webhooks.md +44 -5
  8. package/src/agents/bmad-tea/resources/knowledge/pact-consumer-framework-setup.md +98 -21
  9. package/src/agents/bmad-tea/resources/knowledge/pactjs-utils-mandate.md +28 -13
  10. package/src/agents/bmad-tea/resources/knowledge/pactjs-utils-overview.md +17 -3
  11. package/src/agents/bmad-tea/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
  12. package/src/agents/bmad-tea/resources/tea-index.csv +5 -5
  13. package/src/workflows/testarch/bmad-testarch-atdd/resources/knowledge/contract-testing.md +48 -3
  14. package/src/workflows/testarch/bmad-testarch-atdd/resources/knowledge/pact-broker-webhooks.md +44 -5
  15. package/src/workflows/testarch/bmad-testarch-atdd/resources/knowledge/pact-consumer-framework-setup.md +98 -21
  16. package/src/workflows/testarch/bmad-testarch-atdd/resources/knowledge/pactjs-utils-mandate.md +28 -13
  17. package/src/workflows/testarch/bmad-testarch-atdd/resources/knowledge/pactjs-utils-overview.md +17 -3
  18. package/src/workflows/testarch/bmad-testarch-atdd/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
  19. package/src/workflows/testarch/bmad-testarch-atdd/resources/tea-index.csv +5 -5
  20. package/src/workflows/testarch/bmad-testarch-automate/resources/knowledge/contract-testing.md +48 -3
  21. package/src/workflows/testarch/bmad-testarch-automate/resources/knowledge/pact-broker-webhooks.md +44 -5
  22. package/src/workflows/testarch/bmad-testarch-automate/resources/knowledge/pact-consumer-framework-setup.md +98 -21
  23. package/src/workflows/testarch/bmad-testarch-automate/resources/knowledge/pactjs-utils-mandate.md +28 -13
  24. package/src/workflows/testarch/bmad-testarch-automate/resources/knowledge/pactjs-utils-overview.md +17 -3
  25. package/src/workflows/testarch/bmad-testarch-automate/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
  26. package/src/workflows/testarch/bmad-testarch-automate/resources/tea-index.csv +5 -5
  27. package/src/workflows/testarch/bmad-testarch-automate/steps-c/step-03a-subagent-api.md +1 -1
  28. package/src/workflows/testarch/bmad-testarch-ci/resources/knowledge/contract-testing.md +48 -3
  29. package/src/workflows/testarch/bmad-testarch-ci/resources/knowledge/pact-broker-webhooks.md +44 -5
  30. package/src/workflows/testarch/bmad-testarch-ci/resources/knowledge/pact-consumer-framework-setup.md +98 -21
  31. package/src/workflows/testarch/bmad-testarch-ci/resources/knowledge/pactjs-utils-mandate.md +28 -13
  32. package/src/workflows/testarch/bmad-testarch-ci/resources/knowledge/pactjs-utils-overview.md +17 -3
  33. package/src/workflows/testarch/bmad-testarch-ci/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
  34. package/src/workflows/testarch/bmad-testarch-ci/resources/tea-index.csv +5 -5
  35. package/src/workflows/testarch/bmad-testarch-ci/steps-c/step-02-generate-pipeline.md +10 -7
  36. package/src/workflows/testarch/bmad-testarch-ci/steps-c/step-03-configure-quality-gates.md +1 -1
  37. package/src/workflows/testarch/bmad-testarch-framework/resources/knowledge/contract-testing.md +48 -3
  38. package/src/workflows/testarch/bmad-testarch-framework/resources/knowledge/pact-broker-webhooks.md +44 -5
  39. package/src/workflows/testarch/bmad-testarch-framework/resources/knowledge/pact-consumer-framework-setup.md +98 -21
  40. package/src/workflows/testarch/bmad-testarch-framework/resources/knowledge/pactjs-utils-mandate.md +28 -13
  41. package/src/workflows/testarch/bmad-testarch-framework/resources/knowledge/pactjs-utils-overview.md +17 -3
  42. package/src/workflows/testarch/bmad-testarch-framework/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
  43. package/src/workflows/testarch/bmad-testarch-framework/resources/tea-index.csv +5 -5
  44. package/src/workflows/testarch/bmad-testarch-framework/steps-c/step-03-scaffold-framework.md +6 -5
  45. package/src/workflows/testarch/bmad-testarch-nfr/resources/knowledge/contract-testing.md +48 -3
  46. package/src/workflows/testarch/bmad-testarch-nfr/resources/knowledge/pact-broker-webhooks.md +44 -5
  47. package/src/workflows/testarch/bmad-testarch-nfr/resources/knowledge/pact-consumer-framework-setup.md +98 -21
  48. package/src/workflows/testarch/bmad-testarch-nfr/resources/knowledge/pactjs-utils-mandate.md +28 -13
  49. package/src/workflows/testarch/bmad-testarch-nfr/resources/knowledge/pactjs-utils-overview.md +17 -3
  50. package/src/workflows/testarch/bmad-testarch-nfr/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
  51. package/src/workflows/testarch/bmad-testarch-nfr/resources/tea-index.csv +5 -5
  52. package/src/workflows/testarch/bmad-testarch-test-design/resources/knowledge/contract-testing.md +48 -3
  53. package/src/workflows/testarch/bmad-testarch-test-design/resources/knowledge/pact-broker-webhooks.md +44 -5
  54. package/src/workflows/testarch/bmad-testarch-test-design/resources/knowledge/pact-consumer-framework-setup.md +98 -21
  55. package/src/workflows/testarch/bmad-testarch-test-design/resources/knowledge/pactjs-utils-mandate.md +28 -13
  56. package/src/workflows/testarch/bmad-testarch-test-design/resources/knowledge/pactjs-utils-overview.md +17 -3
  57. package/src/workflows/testarch/bmad-testarch-test-design/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
  58. package/src/workflows/testarch/bmad-testarch-test-design/resources/tea-index.csv +5 -5
  59. package/src/workflows/testarch/bmad-testarch-test-review/resources/knowledge/contract-testing.md +48 -3
  60. package/src/workflows/testarch/bmad-testarch-test-review/resources/knowledge/pact-broker-webhooks.md +44 -5
  61. package/src/workflows/testarch/bmad-testarch-test-review/resources/knowledge/pact-consumer-framework-setup.md +98 -21
  62. package/src/workflows/testarch/bmad-testarch-test-review/resources/knowledge/pactjs-utils-mandate.md +28 -13
  63. package/src/workflows/testarch/bmad-testarch-test-review/resources/knowledge/pactjs-utils-overview.md +17 -3
  64. package/src/workflows/testarch/bmad-testarch-test-review/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
  65. package/src/workflows/testarch/bmad-testarch-test-review/resources/tea-index.csv +5 -5
  66. package/src/workflows/testarch/bmad-testarch-test-review/steps-c/criteria-registry.md +12 -12
  67. package/src/workflows/testarch/bmad-testarch-test-review/steps-c/step-03c-subagent-maintainability.md +1 -0
  68. package/src/workflows/testarch/bmad-testarch-trace/resources/knowledge/contract-testing.md +48 -3
  69. package/src/workflows/testarch/bmad-testarch-trace/resources/knowledge/pact-broker-webhooks.md +44 -5
  70. package/src/workflows/testarch/bmad-testarch-trace/resources/knowledge/pact-consumer-framework-setup.md +98 -21
  71. package/src/workflows/testarch/bmad-testarch-trace/resources/knowledge/pactjs-utils-mandate.md +28 -13
  72. package/src/workflows/testarch/bmad-testarch-trace/resources/knowledge/pactjs-utils-overview.md +17 -3
  73. package/src/workflows/testarch/bmad-testarch-trace/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
  74. package/src/workflows/testarch/bmad-testarch-trace/resources/tea-index.csv +5 -5
  75. package/tools/build-docs.js +4 -0
@@ -31,7 +31,7 @@
31
31
  "name": "bmad-method-test-architecture-enterprise",
32
32
  "source": "./",
33
33
  "description": "Master Test Architect module for quality strategy, test automation, CI/CD quality gates, and structured testing education. Part of the BMad Method ecosystem.",
34
- "version": "1.23.4",
34
+ "version": "1.24.0",
35
35
  "author": {
36
36
  "name": "Murat K Ozcan (TEA Creator) & Brian (BMad) Madison"
37
37
  },
package/CHANGELOG.md CHANGED
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.24.0] - 2026-09-01
11
+
12
+ ### Changed
13
+
14
+ - Pact guidance now covers the short-lived branch coordination shipped in
15
+ `@seontechnologies/pactjs-utils` 1.2.0: scoped `consumerBranch` selectors for
16
+ HTTP and message provider verification, PR-only consumer/provider branch
17
+ detection, exact PactFlow provider-revision checkout, and an additive
18
+ `can-i-deploy` branch check that preserves the environment-wide safety gate.
19
+ It also adopts `isBreakingChangeTolerantBranch` with the explicit-consumer
20
+ no-pacts guard that prevents typo-driven false greens. The Pact mandate,
21
+ framework scaffolding, knowledge selection metadata, and public integration
22
+ documentation use the same rules.
23
+
24
+ ### Fixed
25
+
26
+ - Documentation builds clear Astro's content cache before reading the symlinked
27
+ `docs/` tree, preventing duplicate content IDs on repeated builds.
28
+
10
29
  ## [1.23.4] - 2026-08-31
11
30
 
12
31
  ### Fixed
@@ -134,6 +134,53 @@ await new Verifier(
134
134
 
135
135
  State handler names and their `params` must match the consumer's `createProviderState` exactly. That pairing is the contract's own contract.
136
136
 
137
+ ## Coordinate Different PR Branch Names
138
+
139
+ `matchingBranch: true` covers teams that use the same branch name in consumer
140
+ and provider repositories. Pact.js Utils 1.2.0 also covers short-lived branches
141
+ with different names.
142
+
143
+ On the provider side, `buildVerifierOptions` and
144
+ `buildMessageVerifierOptions` accept `consumerBranch`, defaulting to
145
+ `PACT_CONSUMER_BRANCH`:
146
+
147
+ ```typescript
148
+ buildVerifierOptions({
149
+ provider: 'SampleMoviesAPI',
150
+ port: '3001',
151
+ includeMainAndDeployed: true,
152
+ consumer: 'SampleAppConsumer',
153
+ consumerBranch: process.env.PACT_CONSUMER_BRANCH,
154
+ });
155
+ ```
156
+
157
+ The explicit selector is scoped to `consumer`; the builders throw when a
158
+ consumer branch is supplied without one. It stays alongside the matching,
159
+ main, and deployed selectors.
160
+
161
+ On the consumer side, copy the package's `detect-provider-branch` composite
162
+ action and add `Pact provider branch: <name>` to the PR template. During the PR,
163
+ `can-i-deploy.sh`:
164
+
165
+ 1. checks the target environment while ignoring only that named provider;
166
+ 2. checks the same consumer version against the provider branch tip.
167
+
168
+ Both calls are required. A branch check proves less than an environment check,
169
+ so the override is read on pull requests only and disappears on push to main.
170
+
171
+ The provider has a mirror `detect-consumer-branch` action for manual
172
+ coordination. Keep that PR flow separate from PactFlow's
173
+ `contract_requiring_verification_published` webhook. The webhook identifies an
174
+ exact provider version that needs a result. Check out its
175
+ `providerVersionNumber`, verify that commit belongs to
176
+ `providerVersionBranch`, and publish against those values.
177
+
178
+ Provider suites with an explicit breaking-change tolerance policy should use
179
+ `isBreakingChangeTolerantBranch`. It recognizes only `main`, `master`, and
180
+ `release/**`. Check and reject a missing hand-entered `PACT_CONSUMER_BRANCH`
181
+ before applying that tolerance, so a typo cannot turn an unexecuted
182
+ cross-branch verification green.
183
+
137
184
  ## Which Workflows Change
138
185
 
139
186
  | Workflow | What the flag changes |
@@ -139,16 +139,16 @@ Contract testing fundamentals plus Pact.js Utils, Pact MCP, and broker operation
139
139
 
140
140
  | Fragment | Tier | Description | Key Topics |
141
141
  | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
142
- | [contract-testing](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/contract-testing.md) | specialized | Raw Pact patterns, publishing, verification, resilience, PactV4 four-rule determinism and FFI safety block | Contract testing, Pact fundamentals |
143
- | [pactjs-utils-overview](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pactjs-utils-overview.md) | specialized | Installation, flow decision tree, utility map | pactjs-utils, CDCT/BDCT, integration strategy |
142
+ | [contract-testing](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/contract-testing.md) | specialized | Raw Pact patterns, publishing, verification, short-lived branch coordination, resilience, and FFI safety | Contract testing, Pact fundamentals |
143
+ | [pactjs-utils-overview](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pactjs-utils-overview.md) | specialized | Installation, flow decision tree, utility map, and branch coordination | pactjs-utils, CDCT/BDCT, integration strategy |
144
144
  | [pactjs-utils-mandate](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pactjs-utils-mandate.md) | core | The binding rule when `tea_use_pactjs_utils` is true: substitution table, the relevance gate before scaffolding, banned patterns, pre-emit self-check, broker degradation | Standards, contract testing, generation, review |
145
145
  | [pactjs-utils-zod-to-pact](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pactjs-utils-zod-to-pact.md) | specialized | `zodToPactMatchers` for consumer-curated schemas, example precedence, Pact V3 matcher mapping, anti-patterns | pactjs-utils, zod, consumer schemas, matchers |
146
146
  | [pactjs-utils-consumer-helpers](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pactjs-utils-consumer-helpers.md) | specialized | Provider-state helpers: `createProviderState`, `toJsonMap`; request/response callback helpers: `setJsonBody`, `setJsonContent` | pactjs-utils, consumer testing, provider state |
147
- | [pactjs-utils-provider-verifier](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pactjs-utils-provider-verifier.md) | specialized | `buildVerifierOptions`, `buildMessageVerifierOptions`, broker selectors, tagging | pactjs-utils, provider verification, CI |
147
+ | [pactjs-utils-provider-verifier](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pactjs-utils-provider-verifier.md) | specialized | Verifier builders, scoped `consumerBranch`, provider revision metadata, branch classification | pactjs-utils, provider verification, CI |
148
148
  | [pactjs-utils-request-filter](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pactjs-utils-request-filter.md) | specialized | `createRequestFilter`, `noOpRequestFilter` auth/header patterns | pactjs-utils, request filter, auth injection |
149
149
  | [pact-mcp](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pact-mcp.md) | specialized | SmartBear MCP tools for provider states, review, can-i-deploy, matrix | pact-mcp, broker interaction, pactflow |
150
- | [pact-consumer-framework-setup](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pact-consumer-framework-setup.md) | specialized | Consumer CDC framework scaffolding: directory layout, scripts, CI workflow, and PactV4 test patterns | pactjs-utils, consumer CDC, framework setup |
151
- | [pact-broker-webhooks](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pact-broker-webhooks.md) | specialized | PactFlow GitHub `repository_dispatch` auth via a dedicated machine user and classic PAT, staleness monitoring, PAT rotation runbook | pact broker, webhooks, CI operations, security |
150
+ | [pact-consumer-framework-setup](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pact-consumer-framework-setup.md) | specialized | Consumer CDC scaffolding with deterministic tests, PR-only provider branch detection, and additive branch-aware `can-i-deploy` | pactjs-utils, consumer CDC, framework setup |
151
+ | [pact-broker-webhooks](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pact-broker-webhooks.md) | specialized | PactFlow dispatch auth, exact provider target checkout, staleness monitoring, and PAT rotation | pact broker, webhooks, CI operations, security |
152
152
  | [pact-consumer-di](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pact-consumer-di.md) | extended | Dependency-injection pattern for Pact consumer tests using real client code | pact, consumer, DI, contract accuracy |
153
153
 
154
154
  **Used in:** `framework`, `test-design`, `atdd`, `automate`, `test-review`, `ci` (conditioned by `tea_use_pactjs_utils` and `tea_pact_mcp`)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "bmad-method-test-architecture-enterprise",
4
- "version": "1.23.4",
4
+ "version": "1.24.0",
5
5
  "description": "Master Test Architect for quality strategy, test automation, and release gates",
6
6
  "keywords": [
7
7
  "bmad",
@@ -458,6 +458,43 @@ jobs:
458
458
 
459
459
  ---
460
460
 
461
+ ### Coordinating Different Short-Lived Branch Names
462
+
463
+ `matchingBranch: true` works only when consumer and provider branches share a
464
+ name. Release trains often break that assumption: a consumer feature branch may
465
+ need a provider at `release/week-32`.
466
+
467
+ Treat this as two separate checks:
468
+
469
+ 1. **Provider selects the consumer branch.** Pass a scoped `consumer` plus
470
+ `consumerBranch` to `buildVerifierOptions` or
471
+ `buildMessageVerifierOptions`. `PACT_CONSUMER_BRANCH` is the default input.
472
+ The explicit `{ consumer, branch }` selector is additive to matching, main,
473
+ and deployed selectors.
474
+ 2. **Consumer selects the provider branch.** On PRs only, parse `Pact provider
475
+ branch: <name>` into `PACT_PROVIDER_BRANCH`. Keep the environment-wide
476
+ `can-i-deploy` call, use `--ignore <provider>` for that one in-flight
477
+ pacticipant, then run a second `can-i-deploy` with the provider's
478
+ `--branch`. Both calls must fail hard.
479
+
480
+ The second check is weaker than `--to-environment`: it proves compatibility
481
+ with a branch tip, not with deployed software. Its safety comes from narrow
482
+ scope and short lifetime. Never read the override from the merged PR on push,
483
+ and never replace the environment check globally.
484
+
485
+ Keep manual PR branch selection separate from PactFlow's
486
+ `contract_requiring_verification_published` webhook. That event names the exact
487
+ provider version missing a verification result. Check out its
488
+ `providerVersionNumber`, confirm that commit belongs to
489
+ `providerVersionBranch`, and publish with the same version and branch. Stop the
490
+ job when the target cannot be checked out. A substitute revision produces no
491
+ evidence for the Broker's requested target.
492
+
493
+ See `pactjs-utils-provider-verifier.md`,
494
+ `pact-consumer-framework-setup.md`, and `pact-broker-webhooks.md`.
495
+
496
+ ---
497
+
461
498
  ### Example 4: Resilience Coverage (Testing Fallback Behavior)
462
499
 
463
500
  **Context**: Capture timeout, retry, and error handling behavior explicitly in contracts.
@@ -1014,6 +1051,8 @@ Before implementing contract testing, verify:
1014
1051
  - [ ] **Provider verification**: Runs on PR, verifies all consumer pacts
1015
1052
  - [ ] **State handlers**: Provider implements all given() states
1016
1053
  - [ ] **can-i-deploy**: Blocks deployment if contracts incompatible
1054
+ - [ ] **Short-lived branch overrides**: PR-only, scoped to one pacticipant, and
1055
+ additive to the environment gate
1017
1056
  - [ ] **Webhooks configured**: Consumer changes trigger provider verification
1018
1057
  - [ ] **Retention policy**: Old pacts archived (keep 30 days, all production tags)
1019
1058
  - [ ] **Resilience tested**: Timeouts, retries, error codes in contracts
@@ -1039,25 +1078,31 @@ When `tea_use_pactjs_utils` is enabled, the following utilities replace manual b
1039
1078
  | Repeated builder callbacks for query/header/body | `setJsonContent({ query, headers, body })` | Reusable callback for `.withRequest(...)` and `.willRespondWith(...)` |
1040
1079
  | Inline body lambda `(builder) => builder.jsonBody(body)` | `setJsonBody(body)` | Body-only shorthand for cleaner response builders |
1041
1080
  | 30+ lines of `VerifierOptions` assembly | `buildVerifierOptions({ provider, port, includeMainAndDeployed, stateHandlers })` | One-call setup, env-aware, flow auto-detection |
1042
- | Manual broker URL + selector logic from env vars | `handlePactBrokerUrlAndSelectors({ ..., options })` | Mutates options in-place with broker URL and selectors |
1081
+ | Manual broker URL + selector logic from env vars | `handlePactBrokerUrlAndSelectors({ ..., consumerBranch, options })` | Handles standard selectors and a scoped named consumer branch |
1043
1082
  | DIY Express middleware for auth injection | `createRequestFilter({ tokenGenerator })` | Bearer prefix contract prevents double-prefix bugs |
1044
1083
  | Manual CI branch/tag extraction | `getProviderVersionTags()` | CI-aware (GitHub Actions, GitLab CI, etc.) |
1084
+ | Repeated main/master/release branch classification | `isBreakingChangeTolerantBranch(branch)` | One exact boundary for an explicit breaking-change tolerance policy |
1045
1085
  | Message verifier config assembly | `buildMessageVerifierOptions({ provider, messageProviders })` | Same one-call pattern for Kafka/async contracts |
1046
1086
  | Inline no-op filter `(req, res, next) => next()` | `noOpRequestFilter` | Pre-built pass-through for no-auth providers |
1047
1087
  | Hand-written matcher helper duplicating a Zod/TS type | `zodToPactMatchers(ConsumerMovieSchema, example)` | Single source of truth for response shape; consumer-curated scope keeps contracts lean and consumer-driven |
1048
1088
 
1049
1089
  See the `pactjs-utils-*.md` knowledge fragments for complete examples and anti-patterns (`pactjs-utils-zod-to-pact.md` covers the consumer-curated schema pattern).
1050
1090
 
1091
+ For differently named in-flight branches, use pactjs-utils 1.2.0 or newer.
1092
+ That release adds `consumerBranch` to both verifier builders and ships the
1093
+ PR-only provider-branch detection, additive `can-i-deploy` templates, and
1094
+ `isBreakingChangeTolerantBranch`.
1095
+
1051
1096
  ### PactV4 Determinism & FFI Safety (Mandatory)
1052
1097
 
1053
1098
  Four rules that together prevent both (a) non-deterministic pact generation failures that cause `Cannot change pact content for already published pact` errors at PactFlow publish, and (b) "request was expected but not received" flakes observed on Linux CI once a consumer+provider pair has more than one `.pacttest.ts` file:
1054
1099
 
1055
1100
  1. **Consumer Vitest `fileParallelism: false`** in `vitest.config.pact.ts` — prevents parallel workers from racing on the shared pact JSON. See `pact-consumer-framework-setup.md` Example 2.
1056
- 2. **Consumer Vitest `pool: 'forks'` + `poolOptions.forks.singleFork: true`** in `vitest.config.pact.ts` — same config as the provider side (`pactjs-utils-provider-verifier.md` Example 7). Best current understanding: the `@pact-foundation/pact` napi-rs binding is not robust across Vitest worker threads sharing a process; serialization alone (via `fileParallelism: false`) is insufficient on the default threads pool in Vitest v1. Forks + `singleFork: true` runs every pact file in one subprocess with a coherent FFI handle and eliminated a reproducible Linux-CI flake across multiple repos. Single-file consumer suites have not been observed to flake; this rule is still recommended as a future-proof. See `pact-consumer-framework-setup.md` Example 2.
1101
+ 2. **Consumer Vitest `pool: 'forks'` + `poolOptions.forks.singleFork: true`** in `vitest.config.pact.ts` — same config as the provider side (`pactjs-utils-provider-verifier.md` Example 8). Best current understanding: the `@pact-foundation/pact` napi-rs binding is not robust across Vitest worker threads sharing a process; serialization alone (via `fileParallelism: false`) is insufficient on the default threads pool in Vitest v1. Forks + `singleFork: true` runs every pact file in one subprocess with a coherent FFI handle and eliminated a reproducible Linux-CI flake across multiple repos. Single-file consumer suites have not been observed to flake; this rule is still recommended as a future-proof. See `pact-consumer-framework-setup.md` Example 2.
1057
1102
  3. **One `addInteraction()` per `it()` block** — see `pactjs-utils-consumer-helpers.md` Example 6.
1058
1103
  4. **`publish-pact.sh` jq normalization** sorts interactions before publish — ensures byte-stable payload to PactFlow regardless of generator ordering quirks. See `pact-consumer-framework-setup.md` Example 4.
1059
1104
 
1060
- Provider suites require the same `pool: 'forks'` + `singleFork: true` combination — see `pactjs-utils-provider-verifier.md` Example 7.
1105
+ Provider suites require the same `pool: 'forks'` + `singleFork: true` combination — see `pactjs-utils-provider-verifier.md` Example 8.
1061
1106
 
1062
1107
  ### Webhook Auth & Staleness
1063
1108
 
@@ -9,6 +9,10 @@ Configure PactFlow webhooks to trigger provider verification in GitHub Actions v
9
9
  ### Why webhooks matter
10
10
 
11
11
  - PactFlow's `contract_requiring_verification_published` webhook is the mechanism that notifies a provider repo (via `repository_dispatch`) that a consumer has published a contract needing verification.
12
+ - The webhook carries `${pactbroker.providerVersionNumber}` and
13
+ `${pactbroker.providerVersionBranch}` for the provider version missing a
14
+ result. The provider workflow checks out that exact registered revision
15
+ before publishing verification.
12
16
  - Without a working webhook, `can-i-deploy` in the consumer CI **times out** (900s) and eventually fails with `There is no verified pact between <consumer-version> and the version of <provider> currently in <env>` — even though nothing is wrong in either codebase.
13
17
  - Webhook failures are **silent by default**: PactFlow keeps emitting requests, GitHub keeps returning `401 Unauthorized`, but nothing alerts the team until a PR is blocked.
14
18
 
@@ -85,13 +89,38 @@ jobs:
85
89
  PACT_BROKER_TOKEN: ${{ secrets.PACT_BROKER_TOKEN }}
86
90
  # Pulled from webhook client_payload when triggered by PactFlow:
87
91
  PACT_PAYLOAD_URL: ${{ github.event.client_payload.pact_url }}
88
- GITHUB_SHA: ${{ github.event.client_payload.sha || github.sha }}
89
- GITHUB_BRANCH: ${{ github.event.client_payload.branch || github.head_ref || github.ref_name }}
92
+ PACT_PROVIDER_VERSION: ${{ github.event.client_payload.sha }}
93
+ PACT_PROVIDER_BRANCH: ${{ github.event.client_payload.branch }}
90
94
  steps:
91
95
  - uses: actions/checkout@v4
92
96
  with:
93
- # Check out the provider version known to the broker — this is the provider SHA PactFlow wants verified.
94
- ref: ${{ github.event.client_payload.sha || github.sha }}
97
+ fetch-depth: 0
98
+ - name: Select provider revision
99
+ id: provider-revision
100
+ run: |
101
+ if [ -z "$PACT_PROVIDER_BRANCH" ] || [ -z "$PACT_PROVIDER_VERSION" ]; then
102
+ echo "Webhook payload is missing the provider branch or version."
103
+ exit 1
104
+ fi
105
+
106
+ git fetch origin -- "$PACT_PROVIDER_BRANCH"
107
+
108
+ if ! git rev-parse --verify --quiet "$PACT_PROVIDER_VERSION^{commit}" >/dev/null; then
109
+ echo "Provider version $PACT_PROVIDER_VERSION is unavailable."
110
+ exit 1
111
+ fi
112
+
113
+ if ! git merge-base --is-ancestor "$PACT_PROVIDER_VERSION" FETCH_HEAD; then
114
+ echo "Provider version $PACT_PROVIDER_VERSION is not on $PACT_PROVIDER_BRANCH."
115
+ exit 1
116
+ fi
117
+
118
+ git checkout --detach "$PACT_PROVIDER_VERSION"
119
+ SELECTED_VERSION="$(git rev-parse HEAD)"
120
+
121
+ echo "PACT_PROVIDER_VERSION=$SELECTED_VERSION" >> "$GITHUB_ENV"
122
+ echo "PACT_PROVIDER_BRANCH=$PACT_PROVIDER_BRANCH" >> "$GITHUB_ENV"
123
+ echo "GITHUB_BRANCH=$PACT_PROVIDER_BRANCH" >> "$GITHUB_ENV"
95
124
  - uses: actions/setup-node@v4
96
125
  with:
97
126
  node-version: 20
@@ -107,7 +136,14 @@ jobs:
107
136
 
108
137
  - `repository_dispatch` is the event type emitted by GitHub when the webhook's REST call hits `/repos/<org>/<repo>/dispatches`.
109
138
  - The `types` filter must match the webhook's `event_type` (`contract_requiring_verification_published` here).
110
- - Checking out the provider version known to the broker (`providerVersionNumber`) ensures verification runs against the exact provider commit PactFlow registered — not whatever is on main.
139
+ - Check out the exact `providerVersionNumber` on `providerVersionBranch`.
140
+ PactFlow emits this event for each main, deployed, or released provider
141
+ version missing a result.
142
+ - Verify that the commit exists and belongs to the registered branch before
143
+ detaching to it. A force-pushed or deleted revision fails the job and leaves
144
+ the requested result unknown.
145
+ - After checkout, overwrite `PACT_PROVIDER_VERSION` with `git rev-parse HEAD`
146
+ and publish the result against `PACT_PROVIDER_BRANCH`.
111
147
  - `PACT_PAYLOAD_URL` makes `buildVerifierOptions` verify only the triggering pact (see `pactjs-utils-provider-verifier.md` Example 1).
112
148
 
113
149
  ### Example 3: Secret Rotation Runbook
@@ -180,6 +216,9 @@ jobs:
180
216
  - **Classic PAT, `repo` scope, no expiration** — required for `repository_dispatch`. The "no expiration" trade-off is accepted in exchange for machine-user ownership + PactFlow-secret storage + staleness monitoring.
181
217
  - **Store the PAT as a PactFlow secret** at `/settings/secrets`, reference it from the webhook via `${user.<secret-name>}`. Never inline the token.
182
218
  - **Monitor for silence** — at minimum, a daily scheduled CI job that asserts a recent verification timestamp exists for each critical consumer/provider pair.
219
+ - **Provider targets need broker history** — publish provider verification
220
+ results with branch metadata. Record deployments and releases so PactFlow can
221
+ identify every provider version that this event must verify.
183
222
  - **Rotation is a runbook, not an emergency** — document it (see Example 3), keep it in the repo, and do a practice rotation once a year so it stays fresh.
184
223
  - **Symptom to remember**: "consumer `can-i-deploy` timeout after 900s with `There is no verified pact...`" + "provider's `contract-test-provider` workflow has no recent runs" = expired/revoked PAT. Start with Example 3.
185
224
 
@@ -35,8 +35,10 @@ scripts/
35
35
 
36
36
  .github/
37
37
  ├── actions/
38
- └── detect-breaking-change/
39
- └── action.yml # PR checkbox-driven breaking change detection
38
+ ├── detect-breaking-change/
39
+ └── action.yml # PR checkbox-driven breaking change detection
40
+ │ └── detect-provider-branch/
41
+ │ └── action.yml # PR-only provider branch coordination
40
42
  └── workflows/
41
43
  └── contract-test-consumer.yml # Consumer CDC CI workflow
42
44
  ```
@@ -77,8 +79,8 @@ export default defineConfig({
77
79
  **Key Points**:
78
80
 
79
81
  - **`fileParallelism: false` is required** — primary defense against non-deterministic pact generation. Without it, parallel workers race on the shared pact JSON file and corrupt interactions. Symptom: local runs pass, CI randomly fails with `Cannot change pact content for already published pact`. The `publish-pact.sh` `jq` sort (Example 4) provides byte-stability at publish time.
80
- - **`pool: 'forks'` + `singleFork: true` is required for multi-file consumer suites** — same config the provider side uses (`pactjs-utils-provider-verifier.md` Example 7). Best current understanding: the `@pact-foundation/pact` napi-rs binding is not robust across Vitest worker threads sharing a process; with the default threads pool (Vitest v1) and multiple `.pacttest.ts` files on the same consumer+provider pair, we observed reproducible "request was expected but not received" flakes on Linux CI only. `singleFork: true` serializes every pact file into one forked subprocess and eliminated the flake across multiple repos. Vitest v2+ defaults to `forks`, but set the pool explicitly so the contract does not drift with Vitest version bumps.
81
- - **One `.pacttest.ts` per consumer+provider pair is the canonical pattern** — not just an observation. Two files for the same pair in one process (which `singleFork: true` guarantees) cause an FFI handle collision: the second file's `new PactV4(...)` call re-enters the FFI handle still holding stale state from the first file → "request was expected but not received" sporadically on Linux CI. The fix is structural — merge the files, not the config. `pool: 'forks'` is still required for pact JSON write safety but does NOT prevent same-pair file splits from colliding. Multiple files for **different** pairs (different consumer or provider name) are correct and safe. See Example 10 for the ✅/❌ pattern.
82
+ - **`pool: 'forks'` + `singleFork: true` is required for multi-file consumer suites** — same config the provider side uses (`pactjs-utils-provider-verifier.md` Example 8). Best current understanding: the `@pact-foundation/pact` napi-rs binding is not robust across Vitest worker threads sharing a process; with the default threads pool (Vitest v1) and multiple `.pacttest.ts` files on the same consumer+provider pair, we observed reproducible "request was expected but not received" flakes on Linux CI only. `singleFork: true` serializes every pact file into one forked subprocess and eliminated the flake across multiple repos. Vitest v2+ defaults to `forks`, but set the pool explicitly so the contract does not drift with Vitest version bumps.
83
+ - **One `.pacttest.ts` per consumer+provider pair is the canonical pattern** — not just an observation. Two files for the same pair in one process (which `singleFork: true` guarantees) cause an FFI handle collision: the second file's `new PactV4(...)` call re-enters the FFI handle still holding stale state from the first file → "request was expected but not received" sporadically on Linux CI. The fix is structural — merge the files, not the config. `pool: 'forks'` is still required for pact JSON write safety but does NOT prevent same-pair file splits from colliding. Multiple files for **different** pairs (different consumer or provider name) are correct and safe. See Example 11 for the ✅/❌ pattern.
82
84
  - **Interacting settings**: leave `isolate` at its default (`true`). Do NOT set `sequence.concurrent: true`, `maxConcurrency > 1`, or `maxWorkers > 1` in this config — they defeat the serialization this rule relies on. `hookTimeout` may be raised if mock-server startup is slow, but keep `testTimeout` ≥ `hookTimeout`.
83
85
  - Do NOT add `setupFiles`, `coverage`, or other settings from the unit test config
84
86
  - Keep it minimal — Pact tests run in Node environment with extended timeout
@@ -98,13 +100,16 @@ export default defineConfig({
98
100
  "scripts": {
99
101
  "test:pact:consumer": "vitest run --config vitest.config.pact.ts",
100
102
  "publish:pact": ". ./scripts/env-setup.sh && ./scripts/publish-pact.sh",
101
- "can:i:deploy:consumer": ". ./scripts/env-setup.sh && PACTICIPANT=<service-name> ./scripts/can-i-deploy.sh",
103
+ "can:i:deploy:consumer": ". ./scripts/env-setup.sh && PACTICIPANT=<consumer-name> PROVIDER_PACTICIPANT=<provider-name> ./scripts/can-i-deploy.sh",
102
104
  "record:consumer:deployment": ". ./scripts/env-setup.sh && PACTICIPANT=<service-name> ./scripts/record-deployment.sh"
103
105
  }
104
106
  }
105
107
  ```
106
108
 
107
- Replace `<service-name>` with the consumer's pacticipant name (e.g., `my-frontend-app`).
109
+ Replace `<consumer-name>` and `<provider-name>` with the Pact Broker
110
+ pacticipant names. `PROVIDER_PACTICIPANT` scopes the optional short-lived
111
+ provider branch override; the plain environment gate still works when no
112
+ override is set.
108
113
 
109
114
  **Key Points**:
110
115
 
@@ -185,12 +190,33 @@ set -euo pipefail
185
190
  PACTICIPANT="${PACTICIPANT:?PACTICIPANT env var is required}"
186
191
  ENVIRONMENT="${ENVIRONMENT:-dev}"
187
192
 
188
- pact-broker can-i-deploy \
189
- --pacticipant "$PACTICIPANT" \
190
- --version="$GITHUB_SHA" \
191
- --to-environment "$ENVIRONMENT" \
192
- --retry-while-unknown=10 \
193
- --retry-interval=30
193
+ # PR-only override set by detect-provider-branch. Preserve the environment-wide
194
+ # gate for every other dependency, and check the in-flight provider branch
195
+ # separately. Both calls fail hard under set -e.
196
+ if [ -n "${PACT_PROVIDER_BRANCH:-}" ] && [ -n "${PROVIDER_PACTICIPANT:-}" ]; then
197
+ pact-broker can-i-deploy \
198
+ --pacticipant "$PACTICIPANT" \
199
+ --version="$GITHUB_SHA" \
200
+ --to-environment "$ENVIRONMENT" \
201
+ --ignore "$PROVIDER_PACTICIPANT" \
202
+ --retry-while-unknown=10 \
203
+ --retry-interval=30
204
+
205
+ pact-broker can-i-deploy \
206
+ --pacticipant "$PACTICIPANT" \
207
+ --version="$GITHUB_SHA" \
208
+ --pacticipant "$PROVIDER_PACTICIPANT" \
209
+ --branch="$PACT_PROVIDER_BRANCH" \
210
+ --retry-while-unknown=10 \
211
+ --retry-interval=30
212
+ else
213
+ pact-broker can-i-deploy \
214
+ --pacticipant "$PACTICIPANT" \
215
+ --version="$GITHUB_SHA" \
216
+ --to-environment "$ENVIRONMENT" \
217
+ --retry-while-unknown=10 \
218
+ --retry-interval=30
219
+ fi
194
220
  ```
195
221
 
196
222
  #### `scripts/record-deployment.sh` — Record Deployment
@@ -224,6 +250,9 @@ fi
224
250
  - Use `pact-broker` directly, NOT `npx pact-broker`
225
251
  - Use `PACTICIPANT` env var (required via `${PACTICIPANT:?...}`), not hardcoded service names
226
252
  - `can-i-deploy` includes `--retry-while-unknown=10 --retry-interval=30` (waits for provider verification)
253
+ - A PR-only `PACT_PROVIDER_BRANCH` override is additive: the environment check
254
+ ignores only `PROVIDER_PACTICIPANT`, then a second check targets that
255
+ pacticipant's branch. `--branch` never silently replaces `--to-environment`.
227
256
  - `record-deployment` has branch guard (only records on main/master)
228
257
  - **`publish-pact.sh` normalizes interactions with `jq -S` + `sort_by(...)` before publishing** — ensures byte-stable payload to the broker regardless of generator ordering quirks.
229
258
  - Do NOT invent custom env vars like `PACT_CONSUMER_VERSION` or `PACT_BREAKING_CHANGE` in scripts — those are handled by `env-setup.sh` and the CI detect-breaking-change action respectively
@@ -269,6 +298,9 @@ jobs:
269
298
  - name: Detect Pact breaking change
270
299
  uses: ./.github/actions/detect-breaking-change
271
300
 
301
+ - name: Detect Pact provider branch
302
+ uses: ./.github/actions/detect-provider-branch
303
+
272
304
  - name: Install dependencies
273
305
  run: npm ci
274
306
 
@@ -284,10 +316,15 @@ jobs:
284
316
  # the provider's contract-test-provider.yml workflow.
285
317
  # can-i-deploy retries while waiting for provider verification.
286
318
 
287
- # (4) Check deployment safety (main only — on PRs, local verification is the gate)
288
- - name: Can I deploy consumer? (main only)
289
- if: github.ref == 'refs/heads/main' && env.PACT_BREAKING_CHANGE != 'true'
319
+ # (4) Check deployment safety.
320
+ # NOTE: First-time bootstrap: if no verified contract exists on the broker yet,
321
+ # gate this to main only: if: github.ref == 'refs/heads/main' && env.PACT_BREAKING_CHANGE != 'true'
322
+ # Once the first contract is published and verified on main, remove the main-only condition.
323
+ - name: Can I deploy consumer?
324
+ if: env.PACT_BREAKING_CHANGE != 'true'
290
325
  run: npm run can:i:deploy:consumer
326
+ env:
327
+ PACT_PROVIDER_BRANCH: ${{ env.PACT_PROVIDER_BRANCH }}
291
328
 
292
329
  # (5) Record deployment (main only)
293
330
  - name: Record consumer deployment (main only)
@@ -300,8 +337,12 @@ jobs:
300
337
  - **1:1 local/CI parity is a hard rule**: every CI step is `npm run <same-name-a-dev-uses>`. Never let CI invoke `vitest` or `pact-broker` directly — that divergence is how "works on my machine" slips in. Consumer tests, publish, can-i-deploy, and record-deployment are all the same commands a developer runs locally.
301
338
  - **Workflow-level `env` block** for broker secrets and git vars — not per-step
302
339
  - **`detect-breaking-change` step** runs before install to set `PACT_BREAKING_CHANGE` env var
340
+ - **`detect-provider-branch` step** runs on PR events before install and exports
341
+ a short-lived `PACT_PROVIDER_BRANCH` hint from the PR description
303
342
  - **Step numbering skips (3)** — step 3 is the webhook-triggered provider verification (happens externally)
304
- - **can-i-deploy condition**: `github.ref == 'refs/heads/main' && env.PACT_BREAKING_CHANGE != 'true'`
343
+ - **can-i-deploy condition**: `env.PACT_BREAKING_CHANGE != 'true'` after the
344
+ first main contract has been published and verified. During one-time broker
345
+ bootstrap, gate it to main until that verification exists.
305
346
  - **Comment on (4)**: "on PRs, local verification is the gate"
306
347
  - **No upload-artifact step** — the broker is the source of truth for pact files
307
348
  - **`dependabot[bot]` skip** on the job (contract tests don't run for dependency updates)
@@ -377,7 +418,38 @@ runs:
377
418
 
378
419
  ---
379
420
 
380
- ### Example 7: Consumer Test Using PactV4 Builder
421
+ ### Example 7: Detect Provider Branch Composite Action
422
+
423
+ **Context**: A consumer PR needs verification against a provider branch that
424
+ has not merged or deployed yet. Add `Pact provider branch: <name>` to the PR
425
+ template and parse it only for `pull_request` events.
426
+
427
+ ```yaml
428
+ name: 'Detect Pact Provider Branch'
429
+ description: 'Exports a PR-only provider branch override'
430
+
431
+ runs:
432
+ using: 'composite'
433
+ steps:
434
+ - name: Set PACT_PROVIDER_BRANCH from PR description
435
+ if: github.event_name == 'pull_request'
436
+ uses: actions/github-script@v7
437
+ with:
438
+ script: |
439
+ const body = context.payload.pull_request.body || '';
440
+ const match = body.match(
441
+ /^[^\S\r\n]*Pact provider branch:[^\S\r\n]*(\S+)[^\S\r\n]*$/im
442
+ );
443
+ core.exportVariable('PACT_PROVIDER_BRANCH', match?.[1] || '');
444
+ ```
445
+
446
+ Do not read this field from the merged PR on push. It is a coordination hint for
447
+ one open PR, not durable deployment metadata. After merge, the normal
448
+ `--to-environment` gate must be authoritative again.
449
+
450
+ ---
451
+
452
+ ### Example 8: Consumer Test Using PactV4 Builder
381
453
 
382
454
  **Context**: Consumer pact test using PactV4 `addInteraction()` builder pattern. The test MUST call **real consumer code** (your actual API client/service functions) against the mock server — not raw `fetch()`. Using `fetch()` directly defeats the purpose of CDC testing because it doesn't verify your actual consumer code works with the contract.
383
455
 
@@ -503,7 +575,7 @@ describe('Movies API Consumer Contract', () => {
503
575
 
504
576
  ---
505
577
 
506
- ### Example 8: Support Files
578
+ ### Example 9: Support Files
507
579
 
508
580
  #### Pact Config Factory
509
581
 
@@ -608,7 +680,7 @@ export const setJsonBody = (body: unknown) => setJsonContent({ body });
608
680
 
609
681
  ---
610
682
 
611
- ### Example 9: .gitignore Entries
683
+ ### Example 10: .gitignore Entries
612
684
 
613
685
  **Context**: Pact-specific entries to add to `.gitignore`.
614
686
 
@@ -620,7 +692,7 @@ pact-logs/
620
692
 
621
693
  ---
622
694
 
623
- ### Example 10: Test File Organization — One File Per Consumer+Provider Pair
695
+ ### Example 11: Test File Organization — One File Per Consumer+Provider Pair
624
696
 
625
697
  **Context**: Avoiding Pact Rust FFI handle collisions when structuring consumer test files.
626
698
 
@@ -664,7 +736,7 @@ describe('Movies API', () => {
664
736
  Before presenting the consumer CDC framework to the user, verify:
665
737
 
666
738
  - [ ] `vitest.config.pact.ts` is minimal **and sets `fileParallelism: false` AND `pool: 'forks'` with `poolOptions.forks.singleFork: true`** (`fileParallelism: false` prevents shared pact JSON corruption from parallel workers; forks + `singleFork: true` is required for pact JSON write safety across files — see Example 2 Key Points for mechanism and evidence)
667
- - [ ] Each consumer+provider pair is covered by exactly ONE `.pacttest.ts` file — never split interactions for the same pair across multiple files (two `PactV4` instances for the same pair in one process cause FFI handle collision → "request was expected but not received" on Linux CI; `singleFork: true` does NOT prevent this — it ensures both files share one process, which guarantees the collision; see Example 10)
739
+ - [ ] Each consumer+provider pair is covered by exactly ONE `.pacttest.ts` file — never split interactions for the same pair across multiple files (two `PactV4` instances for the same pair in one process cause FFI handle collision → "request was expected but not received" on Linux CI; `singleFork: true` does NOT prevent this — it ensures both files share one process, which guarantees the collision; see Example 11)
668
740
  - [ ] `vitest.config.pact.ts` does NOT set `sequence.concurrent: true`, `maxConcurrency > 1`, `maxWorkers > 1`, or `isolate: false` — all four defeat the serialization the rule relies on
669
741
  - [ ] `scripts/publish-pact.sh` normalizes interactions with `jq -S '.interactions |= sort_by(.description, (.providerStates[0].name // ""), .request.method, .request.path)'` before the `pact-broker publish` call (ensures byte-stable payload to PactFlow regardless of generator ordering)
670
742
  - [ ] Script names match pactjs-utils (`test:pact:consumer`, `publish:pact`, `can:i:deploy:consumer`, `record:consumer:deployment`)
@@ -672,6 +744,9 @@ Before presenting the consumer CDC framework to the user, verify:
672
744
  - [ ] Shell scripts use `pact-broker` not `npx pact-broker`
673
745
  - [ ] Shell scripts use `PACTICIPANT` env var pattern
674
746
  - [ ] `can-i-deploy.sh` has `--retry-while-unknown=10 --retry-interval=30`
747
+ - [ ] `can-i-deploy.sh` keeps the environment-wide check and, only when both
748
+ `PACT_PROVIDER_BRANCH` and `PROVIDER_PACTICIPANT` exist, ignores that one
749
+ provider there and checks its branch in a second failing command
675
750
  - [ ] `record-deployment.sh` has branch guard
676
751
  - [ ] `env-setup.sh` uses `set -eu`; broker scripts use `set -euo pipefail` — each with explanatory comment
677
752
  - [ ] CI workflow named `contract-test-consumer.yml`
@@ -683,6 +758,8 @@ Before presenting the consumer CDC framework to the user, verify:
683
758
  - [ ] CI can-i-deploy has `PACT_BREAKING_CHANGE != 'true'` condition
684
759
  - [ ] CI has NO upload-artifact step
685
760
  - [ ] `.github/actions/detect-breaking-change/action.yml` exists
761
+ - [ ] `.github/actions/detect-provider-branch/action.yml` reads `Pact provider
762
+ branch: <name>` on PR events only
686
763
  - [ ] Consumer tests use `.pacttest.ts` extension
687
764
  - [ ] Consumer tests use PactV4 `addInteraction()` builder
688
765
  - [ ] `uponReceiving()` names follow `"a request to <action> <resource> [<condition>]"` pattern and are unique within the consumer-provider pair
@@ -58,19 +58,20 @@ These are correctness rules from the per-utility fragments, not style preference
58
58
 
59
59
  ## Substitution Table
60
60
 
61
- | Need | Raw Pact — do not emit | pactjs-utils — emit this | Level | Fragment |
62
- | -------------------------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ----------- | ----------------------------------- |
63
- | Provider state on an interaction | `.given('name', { id: 1 } as JsonMap)`, hand-cast params | `.given(...createProviderState({ name, params }))` | REQUIRED | `pactjs-utils-consumer-helpers.md` |
64
- | Coercing params to Pact's `JsonMap` | Manual casts, `String(date)`, `null` handling per call site | `toJsonMap(value)` | REQUIRED | `pactjs-utils-consumer-helpers.md` |
65
- | PactV4 request/response builder callbacks | Repeated inline `(b) => { b.query(...); b.headers(...); b.jsonBody(...) }` lambdas | `setJsonContent({ query?, headers?, body? })`, or `setJsonBody(body)` for body-only | REQUIRED | `pactjs-utils-consumer-helpers.md` |
66
- | HTTP provider verification options | A hand-assembled 30-line `VerifierOptions` object | `buildVerifierOptions({ provider, port, includeMainAndDeployed, stateHandlers })` | REQUIRED | `pactjs-utils-provider-verifier.md` |
67
- | Message/Kafka provider verification options | A second hand-assembled options object | `buildMessageVerifierOptions({ ... })` | REQUIRED | `pactjs-utils-provider-verifier.md` |
68
- | Broker URL and consumer version selectors | Hand-written env-var branching for local vs remote vs breaking-change flows | `handlePactBrokerUrlAndSelectors(...)` (or let `buildVerifierOptions` read the env) | REQUIRED | `pactjs-utils-provider-verifier.md` |
69
- | Provider version tags in CI | Hand-written branch/tag extraction per CI platform | `getProviderVersionTags()` | REQUIRED | `pactjs-utils-provider-verifier.md` |
70
- | Auth injection during provider verification | A bespoke Express middleware, with its recurring `Bearer Bearer` bug | `createRequestFilter({ tokenGenerator })` | REQUIRED | `pactjs-utils-request-filter.md` |
71
- | A provider that needs no auth injection | Omitting `requestFilter`, or an empty inline function | `noOpRequestFilter` | REQUIRED | `pactjs-utils-request-filter.md` |
72
- | Response matchers where a Zod schema exists | Hand-written `MatchersV3` trees duplicating the schema | `zodToPactMatchers(schema, examples?)` | RECOMMENDED | `pactjs-utils-zod-to-pact.md` |
73
- | Exercising the consumer inside `executeTest` | Raw `fetch(`${mockServer.url}/...`)` | Inject `mockServer.url` as `baseUrl` and call the real client | RECOMMENDED | `pact-consumer-di.md` |
61
+ | Need | Raw Pact — do not emit | pactjs-utils — emit this | Level | Fragment |
62
+ | -------------------------------------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- | ----------- | ----------------------------------- |
63
+ | Provider state on an interaction | `.given('name', { id: 1 } as JsonMap)`, hand-cast params | `.given(...createProviderState({ name, params }))` | REQUIRED | `pactjs-utils-consumer-helpers.md` |
64
+ | Coercing params to Pact's `JsonMap` | Manual casts, `String(date)`, `null` handling per call site | `toJsonMap(value)` | REQUIRED | `pactjs-utils-consumer-helpers.md` |
65
+ | PactV4 request/response builder callbacks | Repeated inline `(b) => { b.query(...); b.headers(...); b.jsonBody(...) }` lambdas | `setJsonContent({ query?, headers?, body? })`, or `setJsonBody(body)` for body-only | REQUIRED | `pactjs-utils-consumer-helpers.md` |
66
+ | HTTP provider verification options | A hand-assembled 30-line `VerifierOptions` object | `buildVerifierOptions({ provider, port, includeMainAndDeployed, stateHandlers })` | REQUIRED | `pactjs-utils-provider-verifier.md` |
67
+ | Message/Kafka provider verification options | A second hand-assembled options object | `buildMessageVerifierOptions({ ... })` | REQUIRED | `pactjs-utils-provider-verifier.md` |
68
+ | Broker URL and consumer version selectors | Hand-written env branching for local, remote, breaking-change, or named-branch flows | `handlePactBrokerUrlAndSelectors(...)` (or let `buildVerifierOptions` read the env) | REQUIRED | `pactjs-utils-provider-verifier.md` |
69
+ | Provider version tags in CI | Hand-written branch/tag extraction per CI platform | `getProviderVersionTags()` | REQUIRED | `pactjs-utils-provider-verifier.md` |
70
+ | Breaking-change tolerant branch check | Repeated `main` / `master` / `release/` string logic | `isBreakingChangeTolerantBranch(branch)` | REQUIRED | `pactjs-utils-provider-verifier.md` |
71
+ | Auth injection during provider verification | A bespoke Express middleware, with its recurring `Bearer Bearer` bug | `createRequestFilter({ tokenGenerator })` | REQUIRED | `pactjs-utils-request-filter.md` |
72
+ | A provider that needs no auth injection | Omitting `requestFilter`, or an empty inline function | `noOpRequestFilter` | REQUIRED | `pactjs-utils-request-filter.md` |
73
+ | Response matchers where a Zod schema exists | Hand-written `MatchersV3` trees duplicating the schema | `zodToPactMatchers(schema, examples?)` | RECOMMENDED | `pactjs-utils-zod-to-pact.md` |
74
+ | Exercising the consumer inside `executeTest` | Raw `fetch(`${mockServer.url}/...`)` | Inject `mockServer.url` as `baseUrl` and call the real client | RECOMMENDED | `pact-consumer-di.md` |
74
75
 
75
76
  `zodToPactMatchers` is RECOMMENDED because it needs a Zod schema to exist. Where the project has one, derive the matchers from it rather than maintaining a parallel matcher tree. Where it does not, write matchers from provider scrutiny and say so.
76
77
 
@@ -82,6 +83,11 @@ When this mandate is active, these are defects in generated or reviewed code:
82
83
 
83
84
  - `.given('state name', someObject as JsonMap)` — a hand-cast provider state where `createProviderState` applies.
84
85
  - A literal `VerifierOptions` object passed to `new Verifier(...)`, where `buildVerifierOptions` applies.
86
+ - Hand-built `{ branch: process.env.PACT_CONSUMER_BRANCH }` selectors where the
87
+ builder's scoped `consumer` + `consumerBranch` inputs apply.
88
+ - Hand-written `branch === 'main' || branch === 'master' ||
89
+ branch.startsWith('release/')` checks where
90
+ `isBreakingChangeTolerantBranch` applies.
85
91
  - A bespoke `requestFilter` middleware that prefixes a bearer token by hand.
86
92
  - Repeated inline PactV4 builder lambdas that `setJsonContent` or `setJsonBody` would replace.
87
93
  - Raw `fetch` inside `executeTest` in a project whose consumer client is importable, with no note saying why.
@@ -159,6 +165,8 @@ await new Verifier(
159
165
  provider: 'SampleMoviesAPI',
160
166
  port: '3001',
161
167
  includeMainAndDeployed: process.env.PACT_BREAKING_CHANGE !== 'true',
168
+ consumer: 'movie-web',
169
+ consumerBranch: process.env.PACT_CONSUMER_BRANCH,
162
170
  stateHandlers,
163
171
  requestFilter: createRequestFilter({ tokenGenerator: () => process.env.TEST_AUTH_TOKEN ?? 'test-token' }),
164
172
  }),
@@ -179,6 +187,13 @@ Any `yes` is a blocker.
179
187
  6. Does `executeTest` call raw `fetch` while the consumer client is importable, with no stated reason?
180
188
  7. Is any response matcher derived from consumer-side types rather than provider source, OpenAPI, or broker data?
181
189
  8. Is any interaction missing its `// Provider endpoint:` comment?
190
+ 9. Does provider verification hand-build an explicit consumer branch selector,
191
+ or set `consumerBranch` without a scoped `consumer`?
192
+ 10. Does a breaking-change catch hand-roll tolerant branch classification
193
+ instead of `isBreakingChangeTolerantBranch`, or check breaking-change
194
+ tolerance before rejecting a "no pacts found" result that occurred with
195
+ an explicit `PACT_CONSUMER_BRANCH` set (letting a typo masquerade as a
196
+ tolerated breaking change)?
182
197
 
183
198
  Fix, or record a deviation. Do not emit unresolved.
184
199