ma-agents 3.9.0 → 3.11.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 (84) hide show
  1. package/README.md +9 -0
  2. package/bin/cli.js +36 -36
  3. package/lib/bmad-cache/cache-manifest.json +8 -8
  4. package/lib/bmad-cache/cis/_git_preserved/index +0 -0
  5. package/lib/bmad-cache/cis/_git_preserved/logs/HEAD +1 -1
  6. package/lib/bmad-cache/cis/_git_preserved/logs/refs/heads/main +1 -1
  7. package/lib/bmad-cache/cis/_git_preserved/logs/refs/remotes/origin/HEAD +1 -1
  8. package/lib/bmad-cache/cis/_git_preserved/objects/pack/pack-42ffc048f54e58ce94c6331bc6be97ebbb7936f2.idx +0 -0
  9. package/lib/bmad-cache/cis/_git_preserved/objects/pack/{pack-cad8ff313ea5db860ddcc7780f03917dcba1da8d.pack → pack-42ffc048f54e58ce94c6331bc6be97ebbb7936f2.pack} +0 -0
  10. package/lib/bmad-cache/cis/_git_preserved/objects/pack/pack-42ffc048f54e58ce94c6331bc6be97ebbb7936f2.rev +0 -0
  11. package/lib/bmad-cache/cis/_git_preserved/packed-refs +1 -1
  12. package/lib/bmad-cache/cis/_git_preserved/refs/heads/main +1 -1
  13. package/lib/bmad-cache/cis/_git_preserved/shallow +1 -1
  14. package/lib/bmad-cache/cis/src/module-help.csv +5 -5
  15. package/lib/bmad-cache/gds/_git_preserved/index +0 -0
  16. package/lib/bmad-cache/gds/_git_preserved/logs/HEAD +1 -1
  17. package/lib/bmad-cache/gds/_git_preserved/logs/refs/heads/main +1 -1
  18. package/lib/bmad-cache/gds/_git_preserved/logs/refs/remotes/origin/HEAD +1 -1
  19. package/lib/bmad-cache/gds/_git_preserved/objects/pack/pack-9427a146a90c00bb542cba038874bf9671ba4dc0.idx +0 -0
  20. package/lib/bmad-cache/gds/_git_preserved/objects/pack/{pack-c1322f7c8531a89dc4f3f34c4955d194f286c1e6.pack → pack-9427a146a90c00bb542cba038874bf9671ba4dc0.pack} +0 -0
  21. package/lib/bmad-cache/gds/_git_preserved/objects/pack/pack-9427a146a90c00bb542cba038874bf9671ba4dc0.rev +0 -0
  22. package/lib/bmad-cache/gds/_git_preserved/packed-refs +1 -1
  23. package/lib/bmad-cache/gds/_git_preserved/refs/heads/main +1 -1
  24. package/lib/bmad-cache/gds/_git_preserved/shallow +1 -1
  25. package/lib/bmad-cache/gds/src/module-help.csv +34 -34
  26. package/lib/bmad-cache/tea/.claude-plugin/marketplace.json +1 -1
  27. package/lib/bmad-cache/tea/.github/workflows/publish.yaml +168 -0
  28. package/lib/bmad-cache/tea/README.md +67 -57
  29. package/lib/bmad-cache/tea/_git_preserved/index +0 -0
  30. package/lib/bmad-cache/tea/_git_preserved/objects/pack/pack-f0df537f2649464ff6c5aee241165eb9c8664227.idx +0 -0
  31. package/lib/bmad-cache/tea/_git_preserved/objects/pack/{pack-9b16db8eb5022c18cef1f0a27d63b6e0f4bc2b2a.pack → pack-f0df537f2649464ff6c5aee241165eb9c8664227.pack} +0 -0
  32. package/lib/bmad-cache/tea/_git_preserved/objects/pack/pack-f0df537f2649464ff6c5aee241165eb9c8664227.rev +0 -0
  33. package/lib/bmad-cache/tea/_git_preserved/packed-refs +1 -1
  34. package/lib/bmad-cache/tea/_git_preserved/refs/heads/main +1 -1
  35. package/lib/bmad-cache/tea/_git_preserved/shallow +1 -1
  36. package/lib/bmad-cache/tea/package-lock.json +2 -2
  37. package/lib/bmad-cache/tea/package.json +5 -6
  38. package/lib/bmad-cache/tea/src/agents/bmad-tea/resources/knowledge/contract-testing.md +2 -3
  39. package/lib/bmad-cache/tea/src/agents/bmad-tea/resources/knowledge/pact-consumer-framework-setup.md +42 -95
  40. package/lib/bmad-cache/tea/src/agents/bmad-tea/resources/knowledge/pactjs-utils-consumer-helpers.md +5 -6
  41. package/lib/bmad-cache/tea/src/agents/bmad-tea/resources/knowledge/pactjs-utils-provider-verifier.md +1 -1
  42. package/lib/bmad-cache/tea/src/agents/bmad-tea/resources/tea-index.csv +1 -1
  43. package/lib/bmad-cache/tea/src/module-help.csv +9 -9
  44. package/lib/bmad-extension/.claude-plugin/marketplace.json.template +2 -2
  45. package/lib/bmad-extension/skills/add-sprint/SKILL.md +1 -1
  46. package/lib/bmad-extension/skills/add-to-sprint/SKILL.md +1 -1
  47. package/lib/bmad-extension/skills/bmad-dev-story/workflow.md +1 -1
  48. package/lib/bmad-extension/skills/bmad-sprint-planning/workflow.md +1 -1
  49. package/lib/bmad-extension/skills/bmad-sprint-status/workflow.md +1 -1
  50. package/lib/bmad-extension/skills/cleanup-done/SKILL.md +1 -1
  51. package/lib/bmad-extension/skills/close-sprint/SKILL.md +1 -1
  52. package/lib/bmad-extension/skills/generate-backlog/SKILL.md +1 -1
  53. package/lib/bmad-extension/skills/modify-sprint/SKILL.md +1 -1
  54. package/lib/bmad-extension/skills/module.yaml +1 -1
  55. package/lib/bmad-extension/skills/prioritize-backlog/SKILL.md +1 -1
  56. package/lib/bmad-extension/skills/remove-from-sprint/SKILL.md +1 -1
  57. package/lib/bmad-extension/skills/sprint-status-view/SKILL.md +1 -1
  58. package/lib/bmad-extension/workflows/add-sprint/workflow.md +39 -0
  59. package/lib/bmad-extension/workflows/add-to-sprint/workflow.md +39 -0
  60. package/lib/bmad-extension/workflows/modify-sprint/workflow.md +39 -0
  61. package/lib/bmad-extension/workflows/sprint-status-view/workflow.md +39 -0
  62. package/lib/bmad-extension-plugin/.claude-plugin/marketplace.json +2 -2
  63. package/lib/bmad-extension-plugin/skills/add-sprint/SKILL.md +1 -1
  64. package/lib/bmad-extension-plugin/skills/add-to-sprint/SKILL.md +1 -1
  65. package/lib/bmad-extension-plugin/skills/bmad-dev-story/workflow.md +1 -1
  66. package/lib/bmad-extension-plugin/skills/bmad-sprint-planning/workflow.md +1 -1
  67. package/lib/bmad-extension-plugin/skills/bmad-sprint-status/workflow.md +1 -1
  68. package/lib/bmad-extension-plugin/skills/cleanup-done/SKILL.md +1 -1
  69. package/lib/bmad-extension-plugin/skills/close-sprint/SKILL.md +1 -1
  70. package/lib/bmad-extension-plugin/skills/generate-backlog/SKILL.md +1 -1
  71. package/lib/bmad-extension-plugin/skills/modify-sprint/SKILL.md +1 -1
  72. package/lib/bmad-extension-plugin/skills/module.yaml +1 -1
  73. package/lib/bmad-extension-plugin/skills/prioritize-backlog/SKILL.md +1 -1
  74. package/lib/bmad-extension-plugin/skills/remove-from-sprint/SKILL.md +1 -1
  75. package/lib/bmad-extension-plugin/skills/sprint-status-view/SKILL.md +1 -1
  76. package/lib/bmad.js +73 -1
  77. package/package.json +4 -4
  78. package/lib/bmad-cache/cis/_git_preserved/objects/pack/pack-cad8ff313ea5db860ddcc7780f03917dcba1da8d.idx +0 -0
  79. package/lib/bmad-cache/cis/_git_preserved/objects/pack/pack-cad8ff313ea5db860ddcc7780f03917dcba1da8d.rev +0 -0
  80. package/lib/bmad-cache/gds/_git_preserved/objects/pack/pack-c1322f7c8531a89dc4f3f34c4955d194f286c1e6.idx +0 -0
  81. package/lib/bmad-cache/gds/_git_preserved/objects/pack/pack-c1322f7c8531a89dc4f3f34c4955d194f286c1e6.rev +0 -0
  82. package/lib/bmad-cache/tea/.github/workflows/manual-release.yaml +0 -216
  83. package/lib/bmad-cache/tea/_git_preserved/objects/pack/pack-9b16db8eb5022c18cef1f0a27d63b6e0f4bc2b2a.idx +0 -0
  84. package/lib/bmad-cache/tea/_git_preserved/objects/pack/pack-9b16db8eb5022c18cef1f0a27d63b6e0f4bc2b2a.rev +0 -0
@@ -216,17 +216,28 @@ See `CONTRIBUTING.md` for guidelines.
216
216
 
217
217
  ## Publishing TEA to NPM
218
218
 
219
- TEA uses an automated release workflow that handles versioning, metadata sync, tagging, NPM publishing, and GitHub releases.
219
+ TEA uses an automated publish workflow modeled after the main `BMAD-METHOD` repo. It supports:
220
+
221
+ - `next` prereleases published automatically from `main`
222
+ - manual stable releases on the `latest` dist-tag
223
+ - trusted npm publishing (no `NPM_TOKEN` secret)
224
+ - metadata sync for `package.json`, `package-lock.json`, and `.claude-plugin/marketplace.json`
220
225
 
221
226
  ### Prerequisites (One-Time Setup)
222
227
 
223
- 1. **NPM Token Configuration:**
224
- - Generate NPM automation token: [npmjs.com/settings/tokens](https://www.npmjs.com/settings/your-username/tokens)
225
- - Add to GitHub Secrets: `Settings` `Secrets and variables` `Actions` → `New repository secret`
226
- - Name: `NPM_TOKEN`
227
- - Value: [your token]
228
+ 1. **npm Trusted Publishing:**
229
+ - In npm package settings for `bmad-method-test-architecture-enterprise`, configure Trusted Publishers for this GitHub repository
230
+ - Allow publishes from the `bmad-code-org/bmad-method-test-architecture-enterprise` repo and the `.github/workflows/publish.yaml` workflow
231
+ - GitHub Actions must be able to request an OIDC token (`id-token: write`), which the workflow already does
232
+
233
+ 2. **GitHub App Secrets for Stable Releases:**
234
+ - Add `RELEASE_APP_ID`
235
+ - Add `RELEASE_APP_PRIVATE_KEY`
236
+ - Install the corresponding GitHub App on this repository with contents write access
237
+ - If `main` is protected, ensure the app is allowed to push the release commit and tag
238
+ - These are used only for manual stable releases so the workflow can push the version bump commit and tag back to `main`
228
239
 
229
- 2. **Verify Package Configuration:**
240
+ 3. **Verify Package Configuration:**
230
241
  ```bash
231
242
  # Check package.json settings
232
243
  cat package.json | grep -A 3 "publishConfig"
@@ -242,71 +253,61 @@ TEA uses an automated release workflow that handles versioning, metadata sync, t
242
253
 
243
254
  #### Option 1: Using npm Scripts (Recommended)
244
255
 
245
- From your local terminal after merging to the release branch you want to publish, typically `main`:
256
+ From your local terminal after merging to `main`:
246
257
 
247
258
  ```bash
248
- # Beta release (first release or testing)
249
- npm run release:beta
250
-
251
- # Alpha release (early testing)
252
- npm run release:alpha
259
+ # Publish the next prerelease from current main
260
+ npm run release:next
253
261
 
254
- # Patch release (bug fixes)
262
+ # Publish a stable patch release
255
263
  npm run release:patch
256
264
 
257
- # Minor release (new features, backwards compatible)
265
+ # Publish a stable minor release
258
266
  npm run release:minor
259
267
 
260
- # Major release (breaking changes)
268
+ # Publish a stable major release
261
269
  npm run release:major
262
270
  ```
263
271
 
264
272
  #### Option 2: Manual Workflow Trigger
265
273
 
266
274
  1. Go to **Actions** tab in GitHub
267
- 2. Click **"Manual Release"** workflow
275
+ 2. Click **"Publish"** workflow
268
276
  3. Click **"Run workflow"**
269
277
  4. Choose the branch to release, typically `main`
270
- 5. Select version bump type (alpha, beta, patch, minor, major)
271
- 6. Click **"Run workflow"**
278
+ 5. Select channel:
279
+ - `next` for a prerelease publish
280
+ - `latest` for a stable release
281
+ 6. If using `latest`, choose the bump type (`patch`, `minor`, `major`)
282
+ 7. Click **"Run workflow"**
272
283
 
273
284
  ### What Happens Automatically
274
285
 
275
286
  The workflow performs these steps:
276
287
 
277
288
  1. ✅ **Validation**: Runs the full `npm test` suite, including schema checks, install tests, knowledge checks, linting, markdown linting, formatting, and release metadata validation
278
- 2. ✅ **Version Bump**: Updates `package.json`, `package-lock.json`, and `.claude-plugin/marketplace.json`
279
- - `beta`: 1.12.3 1.12.4-beta.0
280
- - `alpha`: 1.12.3 1.12.4-alpha.0
281
- - `patch`: 1.12.3 1.12.4
282
- - `minor`: 1.12.3 1.13.0
283
- - `major`: 1.12.3 2.0.0
284
- 3. **Commit**: Creates version bump commit
285
- 4. ✅ **Tag**: Creates git tag (e.g., v1.12.4-beta.0)
286
- 5. ✅ **Publish**: Publishes to NPM registry
287
- - Alpha → dist-tag `alpha` (`npm install bmad-method-test-architecture-enterprise@alpha`)
288
- - Beta → dist-tag `beta` (`npm install bmad-method-test-architecture-enterprise@beta`)
289
- - Stable dist-tag `latest` (`npm install bmad-method-test-architecture-enterprise`)
290
- 6. **Push**: Pushes the version bump commit back to the selected branch when permitted, and always pushes the tag
291
- 7. **GitHub Release**: Creates release with auto-generated notes
292
- 8. **Summary**: Displays installation instructions and distribution links
293
-
294
- ### Version Bump Strategy
295
-
296
- **For TEA Module:**
297
-
298
- - **Beta (`1.12.x-beta.x`)**: Release candidate testing on the next patch line
299
- - **Alpha (`1.12.x-alpha.x`)**: Early development and experimental validation
300
- - **Patch (`1.12.x`)**: Bug fixes, no breaking changes
301
- - **Minor (`1.x.0`)**: New features, backwards compatible
302
- - **Major (`x.0.0`)**: Breaking changes
289
+ 2. ✅ **Version Bump**:
290
+ - `next`: derives the next prerelease version and publishes it with dist-tag `next`
291
+ - `latest`: bumps the stable version (`patch`, `minor`, or `major`)
292
+ 3. **Metadata Sync**: Updates `.claude-plugin/marketplace.json` to match the package version before publishing
293
+ 4. **Publish**: Publishes to npm with provenance enabled
294
+ - `next``npm publish --tag next --provenance`
295
+ - `latest` `npm publish --tag latest --provenance`
296
+ 5. ✅ **Stable Release Finalization**: For `latest`, creates a version bump commit, tags it, pushes it to `main`, and creates a GitHub Release
297
+
298
+ ### Channel Strategy
299
+
300
+ - **`next`**: prerelease channel for the newest merged changes
301
+ - **`latest`**: stable channel for intentional releases
302
+ - **`patch`**: bug fixes, no breaking changes
303
+ - **`minor`**: new features, backwards compatible
304
+ - **`major`**: breaking changes
303
305
 
304
306
  **Recommended Release Path:**
305
307
 
306
- 1. `1.12.3` `1.12.4-beta.0` (first beta)
307
- 2. Test beta with early adopters
308
- 3. `1.12.4-beta.0` `1.12.4-beta.1` (fixes)
309
- 4. When stable: `1.12.4-beta.1` → `1.12.4`
308
+ 1. Merge releasable work to `main`
309
+ 2. Let `next` publish for early validation
310
+ 3. When ready, cut a stable `latest` release via `patch`, `minor`, or `major`
310
311
 
311
312
  ### Verify Publication
312
313
 
@@ -314,6 +315,7 @@ The workflow performs these steps:
314
315
 
315
316
  ```bash
316
317
  npm view bmad-method-test-architecture-enterprise
318
+ npm view bmad-method-test-architecture-enterprise dist-tags
317
319
  ```
318
320
 
319
321
  **Install TEA:**
@@ -337,24 +339,31 @@ If you need to unpublish a version:
337
339
 
338
340
  ```bash
339
341
  # Unpublish specific version (within 72 hours)
340
- npm unpublish bmad-method-test-architecture-enterprise@1.12.4-beta.0
342
+ npm unpublish bmad-method-test-architecture-enterprise@1.13.2-next.0
341
343
 
342
344
  # Deprecate version (preferred for older releases)
343
- npm deprecate bmad-method-test-architecture-enterprise@1.12.4-beta.0 "Use version X.Y.Z instead"
345
+ npm deprecate bmad-method-test-architecture-enterprise@1.13.2-next.0 "Use version X.Y.Z instead"
344
346
  ```
345
347
 
346
348
  ### Troubleshooting
347
349
 
348
- **"NPM_TOKEN not found":**
350
+ **Trusted publishing failed:**
349
351
 
350
- - Verify secret is set: GitHub repo Settings Secrets and variables → Actions
351
- - Secret name must be exactly: `NPM_TOKEN`
352
+ - Verify npm Trusted Publishing is configured for this repository and workflow
353
+ - Verify the workflow has `id-token: write`
354
+ - Confirm the publish is running from the canonical repository, not a fork
352
355
 
353
356
  **"Package already exists":**
354
357
 
355
358
  - Check if package name is already taken on NPM
356
359
  - Update `name` in `package.json` if needed
357
360
 
361
+ **"Version push failed":**
362
+
363
+ - Verify `RELEASE_APP_ID` and `RELEASE_APP_PRIVATE_KEY` are configured
364
+ - Verify the GitHub App is installed on this repository with contents write access
365
+ - If branch protection is enabled on `main`, verify the app is allowed to push the release commit and tag
366
+
358
367
  **"Tests failed":**
359
368
 
360
369
  - Fix failing tests before release
@@ -362,9 +371,9 @@ npm deprecate bmad-method-test-architecture-enterprise@1.12.4-beta.0 "Use versio
362
371
 
363
372
  **"Git push failed (protected branch)":**
364
373
 
365
- - This is expected for a protected release branch
366
- - The tag and version bump are still created
367
- - You may need to manually merge the version bump commit
374
+ - This is not expected once the release GitHub App is configured correctly
375
+ - Verify branch protection allows the app to push the release commit and tag
376
+ - If needed, create the GitHub Release manually after resolving the app permissions
368
377
 
369
378
  ### Release Checklist
370
379
 
@@ -375,7 +384,8 @@ Before releasing:
375
384
  - [ ] CHANGELOG.md updated
376
385
  - [ ] No uncommitted changes
377
386
  - [ ] On `main` branch
378
- - [ ] NPM token configured in GitHub Secrets
387
+ - [ ] npm Trusted Publishing configured
388
+ - [ ] `RELEASE_APP_ID` and `RELEASE_APP_PRIVATE_KEY` configured
379
389
  - [ ] Package name available on NPM
380
390
 
381
391
  After releasing:
@@ -1,2 +1,2 @@
1
1
  # pack-refs with: peeled fully-peeled sorted
2
- 3016dd0390208ef4c9b68841252f6db07fb8805e refs/remotes/origin/main
2
+ b0ee2a5128d0f0b9f32c30a0017722a846518349 refs/remotes/origin/main
@@ -1 +1 @@
1
- 3016dd0390208ef4c9b68841252f6db07fb8805e
1
+ b0ee2a5128d0f0b9f32c30a0017722a846518349
@@ -1 +1 @@
1
- 3016dd0390208ef4c9b68841252f6db07fb8805e
1
+ b0ee2a5128d0f0b9f32c30a0017722a846518349
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "bmad-method-test-architecture-enterprise",
3
- "version": "1.13.1",
3
+ "version": "1.15.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "bmad-method-test-architecture-enterprise",
9
- "version": "1.13.1",
9
+ "version": "1.15.1",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@clack/prompts": "^0.11.0",
@@ -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.13.1",
4
+ "version": "1.15.1",
5
5
  "description": "Master Test Architect for quality strategy, test automation, and release gates",
6
6
  "keywords": [
7
7
  "bmad",
@@ -33,11 +33,10 @@
33
33
  "lint:fix": "eslint . --fix",
34
34
  "lint:md": "markdownlint-cli2 '**/*.md'",
35
35
  "prepare": "command -v husky >/dev/null 2>&1 && husky || exit 0",
36
- "release:alpha": "gh workflow run manual-release.yaml -f version_bump=alpha",
37
- "release:beta": "gh workflow run manual-release.yaml -f version_bump=beta",
38
- "release:major": "gh workflow run manual-release.yaml -f version_bump=major",
39
- "release:minor": "gh workflow run manual-release.yaml -f version_bump=minor",
40
- "release:patch": "gh workflow run manual-release.yaml -f version_bump=patch",
36
+ "release:major": "gh workflow run publish.yaml -f channel=latest -f bump=major",
37
+ "release:minor": "gh workflow run publish.yaml -f channel=latest -f bump=minor",
38
+ "release:next": "gh workflow run publish.yaml -f channel=next",
39
+ "release:patch": "gh workflow run publish.yaml -f channel=latest -f bump=patch",
41
40
  "test": "npm run test:schemas && npm run test:install && npm run test:knowledge && npm run test:release-metadata && npm run test:tea-workflow-descriptions && npm run validate:schemas && npm run lint && npm run lint:md && npm run format:check",
42
41
  "test:coverage": "c8 npm test",
43
42
  "test:install": "node test/test-installation-components.js",
@@ -167,8 +167,7 @@ describe('User API Contract', () => {
167
167
  ```json
168
168
  {
169
169
  "scripts": {
170
- "test:pact:consumer": "./scripts/check-pact-determinism.sh 'npm run test:pact:consumer:run' 3 ./pacts",
171
- "test:pact:consumer:run": "vitest run --config vitest.config.pact.ts",
170
+ "test:pact:consumer": "vitest run --config vitest.config.pact.ts",
172
171
  "publish:pact": ". ./scripts/env-setup.sh && ./scripts/publish-pact.sh"
173
172
  }
174
173
  }
@@ -1056,7 +1055,7 @@ Four rules that together prevent both (a) non-deterministic pact generation fail
1056
1055
  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.
1057
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 on two repos (`pactjs-utils`, `seon-mcp-server`). 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.
1058
1057
  3. **One `addInteraction()` per `it()` block** — see `pactjs-utils-consumer-helpers.md` Example 6.
1059
- 4. **Determinism gate** runs the consumer suite N times and fails on byte-different pact JSON before publish see `pact-consumer-framework-setup.md` Example 10 (`scripts/check-pact-determinism.sh`).
1058
+ 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.
1060
1059
 
1061
1060
  Provider suites require the same `pool: 'forks'` + `singleFork: true` combination — see `pactjs-utils-provider-verifier.md` Example 7.
1062
1061
 
@@ -76,9 +76,9 @@ export default defineConfig({
76
76
 
77
77
  **Key Points**:
78
78
 
79
- - **`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`. See Example 10 for the determinism gate that enforces byte-stability across re-runs.
79
+ - **`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
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 on two repos (`pactjs-utils`, `seon-mcp-server`). Vitest v2+ defaults to `forks`, but set the pool explicitly so the contract does not drift with Vitest version bumps.
81
- - **Single-file consumer suites** (one `.pacttest.ts` per consumer+provider pair) have not been observed to flake under default threads pool, because FFI state is not shared across files when there is only one file. Adding `pool: 'forks'` is still recommended it future-proofs you the moment a second file is added but a suite passing today with only `fileParallelism: false` is not broken.
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
82
  - **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
83
  - Do NOT add `setupFiles`, `coverage`, or other settings from the unit test config
84
84
  - Keep it minimal — Pact tests run in Node environment with extended timeout
@@ -96,8 +96,7 @@ export default defineConfig({
96
96
  ```json
97
97
  {
98
98
  "scripts": {
99
- "test:pact:consumer": "./scripts/check-pact-determinism.sh 'npm run test:pact:consumer:run' 3 ./pacts",
100
- "test:pact:consumer:run": "vitest run --config vitest.config.pact.ts",
99
+ "test:pact:consumer": "vitest run --config vitest.config.pact.ts",
101
100
  "publish:pact": ". ./scripts/env-setup.sh && ./scripts/publish-pact.sh",
102
101
  "can:i:deploy:consumer": ". ./scripts/env-setup.sh && PACTICIPANT=<service-name> ./scripts/can-i-deploy.sh",
103
102
  "record:consumer:deployment": ". ./scripts/env-setup.sh && PACTICIPANT=<service-name> ./scripts/record-deployment.sh"
@@ -109,8 +108,6 @@ Replace `<service-name>` with the consumer's pacticipant name (e.g., `my-fronten
109
108
 
110
109
  **Key Points**:
111
110
 
112
- - **`test:pact:consumer` IS the determinism gate** — it runs the inner command 3× and fails if pact output is not byte-stable. This is the command CI and developers run before pushing. See Example 10 for the `check-pact-determinism.sh` script itself.
113
- - **`test:pact:consumer:run` is the fast inner command** for TDD loops (a single pass of the suite, no gate). Developers can iterate with this; CI always goes through the outer gated script.
114
111
  - Use colon-separated naming: `test:pact:consumer`, NOT `test:contract` or `test:contract:consumer`
115
112
  - Broker scripts source `env-setup.sh` inline in package.json (`. ./scripts/env-setup.sh && ...`)
116
113
  - `PACTICIPANT` is set per-script invocation, not globally
@@ -139,7 +136,7 @@ export GITHUB_SHA="${GITHUB_SHA:-$(git rev-parse --short HEAD)}"
139
136
  export GITHUB_BRANCH="${GITHUB_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}"
140
137
  ```
141
138
 
142
- #### `scripts/publish-pact.sh` — Publish Pacts to Broker (with defense-in-depth normalization)
139
+ #### `scripts/publish-pact.sh` — Publish Pacts to Broker
143
140
 
144
141
  ```bash
145
142
  #!/bin/bash
@@ -147,9 +144,8 @@ export GITHUB_BRANCH="${GITHUB_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}"
147
144
  #
148
145
  # Before publish, normalize each pact JSON: sort interactions by (description, provider state name,
149
146
  # method, path) and sort object keys via `jq -S`. This gives byte-stable output to the broker even
150
- # if the PactV4 generator produces ordering drift between runs. Paired with scripts/check-pact-determinism.sh
151
- # as defense-in-depth the gate catches drift pre-publish; normalization ensures "Cannot change pact
152
- # content" from PactFlow never fires on ordering-only changes that slip past the gate.
147
+ # if the PactV4 generator produces ordering drift between runs. Ensures "Cannot change pact content"
148
+ # from PactFlow never fires on ordering-only changes.
153
149
  #
154
150
  # Requires: PACT_BROKER_BASE_URL, PACT_BROKER_TOKEN, GITHUB_SHA, GITHUB_BRANCH, jq
155
151
  # -e: exit on error -u: error on undefined vars -o pipefail: fail if any pipe segment fails
@@ -229,7 +225,7 @@ fi
229
225
  - Use `PACTICIPANT` env var (required via `${PACTICIPANT:?...}`), not hardcoded service names
230
226
  - `can-i-deploy` includes `--retry-while-unknown=10 --retry-interval=30` (waits for provider verification)
231
227
  - `record-deployment` has branch guard (only records on main/master)
232
- - **`publish-pact.sh` normalizes interactions with `jq -S` + `sort_by(...)` before publishing** — defense-in-depth alongside the determinism gate (Example 10). The gate catches drift; normalization ensures byte-stable payload to the broker regardless of generator quirks. Keep both; they protect against different failure modes.
228
+ - **`publish-pact.sh` normalizes interactions with `jq -S` + `sort_by(...)` before publishing** — ensures byte-stable payload to the broker regardless of generator ordering quirks.
233
229
  - 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
234
230
 
235
231
  ---
@@ -276,8 +272,8 @@ jobs:
276
272
  - name: Install dependencies
277
273
  run: npm ci
278
274
 
279
- # (1) Generate pact files — runs the determinism gate (3 runs + byte-stable check via jq)
280
- - name: Consumer pact tests (determinism gate)
275
+ # (1) Generate pact files
276
+ - name: Run consumer contract tests
281
277
  run: npm run test:pact:consumer
282
278
 
283
279
  # (2) Publish pacts to broker (publish-pact.sh also normalizes interaction order as defense-in-depth)
@@ -301,8 +297,7 @@ jobs:
301
297
 
302
298
  **Key Points**:
303
299
 
304
- - **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. The determinism gate, publish, can-i-deploy, and record-deployment are all the same commands a developer runs locally.
305
- - **The determinism gate is its own visible step, not a side-effect of publish.** A failing gate must be debuggable from the CI log without re-running. Do not fold it into a `prepublish:pact` hook — folding hides the failure inside a publish log and makes attribution harder.
300
+ - **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.
306
301
  - **Workflow-level `env` block** for broker secrets and git vars — not per-step
307
302
  - **`detect-breaking-change` step** runs before install to set `PACT_BREAKING_CHANGE` env var
308
303
  - **Step numbering skips (3)** — step 3 is the webhook-triggered provider verification (happens externally)
@@ -625,89 +620,42 @@ pact-logs/
625
620
 
626
621
  ---
627
622
 
628
- ### Example 10: Determinism Gate Script (Primary Defense)
623
+ ### Example 10: Test File Organization One File Per Consumer+Provider Pair
629
624
 
630
- **Context**: Even with `fileParallelism: false` (Example 2) and one-interaction-per-`it()` (see `pactjs-utils-consumer-helpers.md`), the PactV4 Rust FFI layer can occasionally produce byte-different pact JSON between runs — interaction ordering drift, nested matcher serialization quirks, or `Date` / random-value matchers that weren't locked down. This causes PactFlow to reject re-publishes of the same consumer SHA with `Cannot change pact content for already published pact`. The determinism gate runs the consumer suite N times locally and in CI, hashes the normalized pact files, and fails fast if drift is detected — before any publish is attempted.
625
+ **Context**: Avoiding Pact Rust FFI handle collisions when structuring consumer test files.
631
626
 
632
- **Implementation**:
633
-
634
- #### `scripts/check-pact-determinism.sh`
635
-
636
- ```bash
637
- #!/bin/bash
638
- # Run a pact consumer command N times and fail if the generated pact files are not byte-stable.
639
- # Primary defense against PactV4 non-deterministic output.
640
- #
641
- # Usage: ./scripts/check-pact-determinism.sh "<cmd>" [runs] [pact-dir]
642
- # Example: ./scripts/check-pact-determinism.sh 'npm run test:pact:consumer:run' 3 ./pacts
643
- #
644
- # Requires: jq installed on the runner (ubuntu-latest has it; macOS users need `brew install jq`).
645
- set -euo pipefail
646
-
647
- CMD="${1:?usage: ./scripts/check-pact-determinism.sh \"<cmd>\" [runs] [pact-dir]}"
648
- RUNS="${PACT_DETERMINISM_RUNS:-${2:-3}}"
649
- PACT_DIR="${3:-./pacts}"
627
+ **Rule**: Every consumer+provider pair maps to exactly one `.pacttest.ts` file. Never split interactions for the same pair across multiple files.
650
628
 
651
- TMP_DIR="$(mktemp -d)"
652
- trap 'rm -rf "$TMP_DIR"' EXIT
629
+ **Root cause**: The Pact Rust FFI maintains one handle per consumer+provider pair per process. With `singleFork: true` (all files run sequentially in one forked process), two files for the same pair access the same FFI handle back-to-back. The second file's `new PactV4({ consumer, provider })` call re-enters the handle still holding stale interaction state from the first file. The first test in the second file starts the mock server in this corrupted state — "request was expected but not received" results, sporadic and Linux-CI-only (execution order differs between environments).
653
630
 
654
- hash_pact_file() {
655
- # Sort interactions by (description, first provider state name, method, path), sort keys with -S.
656
- # The sorted output is what we hash — so ordering-only drift does NOT count as non-determinism here.
657
- # (The gate catches deeper drift; ordering drift is handled by publish-pact.sh normalization.)
658
- jq -S '.interactions |= sort_by(.description, (.providerStates[0].name // ""), .request.method, .request.path)' "$1" \
659
- | shasum -a 256 | awk '{print $1}'
660
- }
631
+ **Evidence**: In `pactjs-utils`, `movies-read.pacttest.ts` and `movies-write.pacttest.ts` both used `consumer: 'SampleAppConsumer', provider: 'SampleMoviesAPI'`. The vitest config and CI workflow were correct throughout. The fix was merging the two files into `movies.pacttest.ts`. The config was not changed.
661
632
 
662
- for run in $(seq 1 "$RUNS"); do
663
- echo "→ determinism run $run/$RUNS"
664
- rm -f "$PACT_DIR"/*.json 2>/dev/null || true
665
- eval "$CMD" >"$TMP_DIR/run-$run.log" 2>&1 || {
666
- echo "❌ run $run failed dumping log:"
667
- cat "$TMP_DIR/run-$run.log"
668
- exit 1
669
- }
670
- : > "$TMP_DIR/run-$run.hashes"
671
- for f in "$PACT_DIR"/*.json; do
672
- [ -f "$f" ] || continue
673
- printf '%s %s\n' "$(hash_pact_file "$f")" "$(basename "$f")" >> "$TMP_DIR/run-$run.hashes"
674
- done
675
- sort -o "$TMP_DIR/run-$run.hashes" "$TMP_DIR/run-$run.hashes"
676
- done
677
-
678
- # Compare every subsequent run against run 1.
679
- FAIL=0
680
- for run in $(seq 2 "$RUNS"); do
681
- if ! diff -q "$TMP_DIR/run-1.hashes" "$TMP_DIR/run-$run.hashes" >/dev/null; then
682
- FAIL=1
683
- echo ""
684
- echo "❌ Pact output differs between run 1 and run $run:"
685
- diff "$TMP_DIR/run-1.hashes" "$TMP_DIR/run-$run.hashes" || true
686
- fi
687
- done
688
-
689
- if [ "$FAIL" -ne 0 ]; then
690
- echo ""
691
- echo "Pact output is non-deterministic across $RUNS runs. Likely causes:"
692
- echo " • multiple .addInteraction() chained in a single it() block (PactV4 FFI drops one non-deterministically)"
693
- echo " • fileParallelism: true in vitest.config.pact.ts (workers race on shared pact JSON)"
694
- echo " • missing pool: 'forks' + singleFork: true (threads pool shares FFI state across files on Linux CI)"
695
- echo " • Date / random matchers that don't lock a stable example value"
696
- echo " • provider state params mutating between runs (e.g. Date.now())"
697
- exit 1
698
- fi
699
-
700
- echo "✅ Pact output is byte-stable across $RUNS runs."
633
+ ```typescript
634
+ // WRONG same consumer+provider pair split across two files
635
+ // movies-read.pacttest.ts
636
+ const pact = new PactV4({ consumer: 'SampleAppConsumer', provider: 'SampleMoviesAPI', ... })
637
+ describe('Read Operations', () => { /* 4 tests: GET /movies, GET /movies/:id */ })
638
+
639
+ // movies-write.pacttest.ts ← second PactV4 for the SAME pair = FFI handle collision
640
+ const pact = new PactV4({ consumer: 'SampleAppConsumer', provider: 'SampleMoviesAPI', ... })
641
+ describe('Write Operations', () => { /* 5 tests: POST, PUT, DELETE */ })
642
+
643
+ // RIGHT one file per consumer+provider pair, describe blocks for organization
644
+ // movies.pacttest.ts
645
+ const pact = new PactV4({ consumer: 'SampleAppConsumer', provider: 'SampleMoviesAPI', ... })
646
+ describe('Movies API', () => {
647
+ describe('Read Operations', () => { /* 4 tests */ })
648
+ describe('Write Operations', () => { /* 5 tests */ })
649
+ })
701
650
  ```
702
651
 
703
652
  **Key Points**:
704
653
 
705
- - **Wire this script into `test:pact:consumer`** (see Example 3). The outer script IS the gate; the inner `test:pact:consumer:run` is the single-pass command for TDD loops.
706
- - **Default 3 runs** is the sweet spot 2 runs miss intermittent drops, >3 slows CI without catching more. Override with an env var or the positional arg if you're actively debugging a flake.
707
- - **Treat gate failures as a P0 bug, not a "retry until green" condition.** Find the source of non-determinism (chained `addInteraction`, unsorted interactions, Date-dependent matchers). Do not raise `RUNS` to 10 to mask the symptom.
708
- - **Requires `jq`** installed by default on `ubuntu-latest`. For macOS local dev, document `brew install jq` in the project README.
709
- - **In CI, make this its own visible step** (see Example 5 step (1) naming). Do not fold into a `prepublish:pact` hook that hides the failure inside a publish log.
710
- - **Defense-in-depth with `publish-pact.sh` normalization** (Example 4): the gate catches pre-publish drift; the publish-time `jq` sort ensures any ordering-only drift that slipped past the gate still produces a byte-stable payload to PactFlow.
654
+ - **File = contract**: A `.pacttest.ts` file represents one consumer+provider contract. One contract = one file.
655
+ - **Describe blocks, not files**: Organize by operation type (`Read Operations`, `Write Operations`), resource, or feature always within one file per pair.
656
+ - **Different pairs = different files**: `ServiceA / BackendAPI` and `ServiceA / AuthAPI` are two contracts and correctly use two separate files. This rule only forbids splitting ONE pair.
657
+ - **`singleFork: true` is not a fix for this**: It ensures correct pact JSON write semantics across files, but when two files share a pair it actually guarantees the FFI collision (both land in the same process). Without it you'd get file-write races instead. Neither is safe. The fix is one file per pair.
658
+ - **Naming convention**: `{domain}.pacttest.ts` when one domain maps to one pair. `{consumer-kebab}-{provider-kebab}.pacttest.ts` when the filename must be self-describing about which pair it covers.
711
659
 
712
660
  ---
713
661
 
@@ -715,12 +663,11 @@ echo "✅ Pact output is byte-stable across $RUNS runs."
715
663
 
716
664
  Before presenting the consumer CDC framework to the user, verify:
717
665
 
718
- - [ ] `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` eliminates the Linux-CI "request was expected but not received" flake observed once a second `.pacttest.ts` is added — see Example 2 Key Points for evidence, mechanism qualifier, and single-file exception)
666
+ - [ ] `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)
719
668
  - [ ] `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
720
- - [ ] `package.json` splits `test:pact:consumer` (gated determinism runner) and `test:pact:consumer:run` (inner single-pass command)
721
- - [ ] `scripts/check-pact-determinism.sh` is present, hashes via `jq -S` + `sort_by`, defaults to 3 runs, and is the body of the `test:pact:consumer` script
722
- - [ ] `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 (defense-in-depth alongside the gate)
723
- - [ ] Script names match pactjs-utils (`test:pact:consumer`, `test:pact:consumer:run`, `publish:pact`, `can:i:deploy:consumer`, `record:consumer:deployment`)
669
+ - [ ] `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
+ - [ ] Script names match pactjs-utils (`test:pact:consumer`, `publish:pact`, `can:i:deploy:consumer`, `record:consumer:deployment`)
724
671
  - [ ] Scripts source `env-setup.sh` inline in package.json
725
672
  - [ ] Shell scripts use `pact-broker` not `npx pact-broker`
726
673
  - [ ] Shell scripts use `PACTICIPANT` env var pattern
@@ -730,7 +677,7 @@ Before presenting the consumer CDC framework to the user, verify:
730
677
  - [ ] CI workflow named `contract-test-consumer.yml`
731
678
  - [ ] CI has workflow-level env block (not per-step)
732
679
  - [ ] CI has `detect-breaking-change` step before install
733
- - [ ] CI step (1) is the determinism gate (calls `npm run test:pact:consumer`) — its own visible step, not folded into publish
680
+ - [ ] CI step (1) generates pact files (calls `npm run test:pact:consumer`) — its own visible step, not folded into publish
734
681
  - [ ] CI steps are 1:1 with developer commands — every CI step calls `npm run <same-name>` a dev would run locally (no direct `vitest` or `pact-broker` invocation)
735
682
  - [ ] CI step numbering skips (3) — webhook-triggered provider verification
736
683
  - [ ] CI can-i-deploy has `PACT_BREAKING_CHANGE != 'true'` condition
@@ -260,10 +260,9 @@ it.each([
260
260
 
261
261
  **Key Points**:
262
262
 
263
- - **This rule stacks with two other MANDATORY vitest settings**: `fileParallelism: false` AND `pool: 'forks'` with `poolOptions.forks.singleFork: true`. All three are required and address different failure modes `fileParallelism: false` prevents parallel workers from racing on the shared pact JSON; `pool: 'forks'` + `singleFork: true` prevents the Pact Rust FFI from leaking state across files (manifests as "request was expected but not received" flakes on Linux CI only); one-interaction-per-`it()` prevents the FFI from dropping interactions within a single test body.
264
- - Symptom of violating this rule: the pact file is byte-different between otherwise-identical runs; `scripts/check-pact-determinism.sh` flags drift; PactFlow rejects a republish with `Cannot change pact content`.
263
+ - **This rule stacks with two MANDATORY vitest settings and one file-organization rule. All four address different failure modes; none substitutes for the others**: (1) `fileParallelism: false` prevents parallel workers racing on the shared pact JSON file; (2) `pool: 'forks'` with `singleFork: true` required for pact JSON write safety across multiple files; (3) **one `.pacttest.ts` per consumer+provider pair** — `singleFork: true` keeps all files in one process, so two files for the same pair produce an FFI handle collision ("request was expected but not received" on Linux CI, sporadic); (4) one-interaction-per-`it()` (this rule) — prevents the FFI from dropping interactions within a single test body. See `pact-consumer-framework-setup.md` Example 10 for the file-organization ✅/❌ pattern.
264
+ - Symptom of violating this rule: the pact file is byte-different between otherwise-identical runs; PactFlow rejects a republish with `Cannot change pact content`.
265
265
  - The rule applies to both HTTP consumer pacts (`PactV4`) and message consumer pacts (`MessageConsumerPact`).
266
- - See `pact-consumer-framework-setup.md` Example 10 for the determinism gate that automatically catches violations of this rule.
267
266
 
268
267
  ## Key Points
269
268
 
@@ -278,13 +277,13 @@ it.each([
278
277
  - **Body shorthand**: `setJsonBody` keeps body-only responses concise and readable
279
278
  - **Matchers check type, not value**: `string('My movie')` means "any string", `integer(1)` means "any integer". The example values are arbitrary — the provider can return different values and verification still passes as long as the type matches. Use matchers only in `.willRespondWith()` (responses), never in `.withRequest()` (requests) — Postel's Law applies.
280
279
  - **Reuse test values across files**: Interactions are uniquely identified by `uponReceiving` + `.given()`, not by placeholder values. Two test files can both use `testId: 100` without conflicting. On the provider side, shared values simplify state handlers — idempotent handlers (check if exists, create if not) only need to ensure one record exists. Use different values only when testing different states of the same entity type (e.g., `movieExists(100)` for happy paths vs. `movieNotFound(999)` for error paths).
281
- - **One `addInteraction()` per `it()` block (MANDATORY for PactV4)**: Multiple interactions inside one `it()` cause the Rust FFI to non-deterministically drop interactions. Use one `it()` per interaction or `it.each(...)` for parameterized cases. See Example 6 and the determinism gate in `pact-consumer-framework-setup.md` Example 10.
280
+ - **One `addInteraction()` per `it()` block (MANDATORY for PactV4)**: Multiple interactions inside one `it()` cause the Rust FFI to non-deterministically drop interactions. Use one `it()` per interaction or `it.each(...)` for parameterized cases. See Example 6.
282
281
 
283
282
  ## Related Fragments
284
283
 
285
284
  - `pactjs-utils-overview.md` — installation, decision tree, design philosophy
286
285
  - `pactjs-utils-provider-verifier.md` — provider-side state handler implementation; same `pool: 'forks'` + `singleFork: true` rule as consumer
287
- - `pact-consumer-framework-setup.md` — Vitest `fileParallelism: false` + `pool: 'forks'` + `singleFork: true` config, determinism gate (Example 10), and CI wiring
286
+ - `pact-consumer-framework-setup.md` — Vitest `fileParallelism: false` + `pool: 'forks'` + `singleFork: true` config and CI wiring
288
287
  - `contract-testing.md` — foundational patterns with raw Pact.js
289
288
 
290
289
  ## Anti-Patterns
@@ -375,6 +374,6 @@ it('returns empty list', async () => {
375
374
  });
376
375
  ```
377
376
 
378
- See Example 6 above for the full rationale and the determinism gate that enforces this rule.
377
+ See Example 6 above for the full rationale.
379
378
 
380
379
  _Source: @seontechnologies/pactjs-utils consumer-helpers module, pactjs-utils sample-app consumer tests_
@@ -296,7 +296,7 @@ export default defineConfig({
296
296
  - `pactjs-utils-overview.md` — installation, decision tree, design philosophy
297
297
  - `pactjs-utils-consumer-helpers.md` — consumer-side state parameter creation, **one-interaction-per-`it()` rule**
298
298
  - `pactjs-utils-request-filter.md` — auth injection for provider verification
299
- - `pact-consumer-framework-setup.md` — consumer-side framework setup, Vitest `fileParallelism: false`, determinism gate
299
+ - `pact-consumer-framework-setup.md` — consumer-side framework setup, Vitest `fileParallelism: false`, CI wiring
300
300
  - `pact-broker-webhooks.md` — PactFlow → GitHub webhook auth/staleness for webhook-triggered provider verification (`contract_requiring_verification_published`)
301
301
  - `contract-testing.md` — foundational patterns with raw Pact.js
302
302
 
@@ -38,7 +38,7 @@ pactjs-utils-consumer-helpers,Pact.js Utils Consumer Helpers,"createProviderStat
38
38
  pactjs-utils-provider-verifier,Pact.js Utils Provider Verifier,"buildVerifierOptions, buildMessageVerifierOptions; vitest pool:forks + singleFork for FFI safety (same rule applies to consumer and provider)","pactjs-utils,provider,consumer,contract-testing,pact,api,backend,ci,vitest,ffi",specialized,knowledge/pactjs-utils-provider-verifier.md
39
39
  pactjs-utils-request-filter,Pact.js Utils Request Filter,"createRequestFilter, noOpRequestFilter for auth injection","pactjs-utils,auth,contract-testing,pact",specialized,knowledge/pactjs-utils-request-filter.md
40
40
  pact-mcp,Pact MCP Server,"SmartBear MCP for PactFlow: generate tests, review, can-i-deploy, provider states","pact,mcp,pactflow,contract-testing,broker",specialized,knowledge/pact-mcp.md
41
- pact-consumer-framework-setup,Pact Consumer CDC Framework Setup,"Directory structure, vitest config with fileParallelism:false + pool:forks + singleFork:true (FFI safety), determinism gate (check-pact-determinism.sh), jq-normalized publishing, 1:1 local/CI parity, PactV4 patterns","pactjs-utils,consumer,contract-testing,pact,ci,framework,setup,vitest,shell-scripts,determinism,jq,pactv4,ffi",specialized,knowledge/pact-consumer-framework-setup.md
41
+ pact-consumer-framework-setup,Pact Consumer CDC Framework Setup,"Directory structure, vitest config with fileParallelism:false + pool:forks + singleFork:true (FFI safety), one-file-per-consumer+provider-pair rule (FFI handle collision prevention), jq-normalized publishing, 1:1 local/CI parity, PactV4 patterns","pactjs-utils,consumer,contract-testing,pact,ci,framework,setup,vitest,shell-scripts,jq,pactv4,ffi,file-organization,one-file-per-pair",specialized,knowledge/pact-consumer-framework-setup.md
42
42
  pact-broker-webhooks,Pact Broker Webhooks,"PactFlow → GitHub repository_dispatch auth via dedicated machine user + classic PAT (repo scope, no expiration) + PactFlow secret; staleness monitoring and PAT rotation runbook","pact,pactflow,broker,webhooks,github,auth,pat,ci,operations,security",specialized,knowledge/pact-broker-webhooks.md
43
43
  adr-quality-readiness-checklist,ADR Quality Readiness Checklist,"8-category 29-criteria framework for ADR testability and NFR assessment","nfr,testability,adr,quality,assessment,checklist",extended,knowledge/adr-quality-readiness-checklist.md
44
44
  playwright-cli,Playwright CLI,"Token-efficient CLI for AI coding agents: element refs, sessions, snapshots, trace analysis, debug=cli autonomous investigation","cli,browser,agent,automation,snapshot,trace,debug",core,knowledge/playwright-cli.md