Skip to content

fix: release-gate exports check, fixture-load guard, and E2E doc lifecycle#300

Closed
jpr5 wants to merge 3 commits into
mainfrom
fix/e2e-doc-and-guards
Closed

fix: release-gate exports check, fixture-load guard, and E2E doc lifecycle#300
jpr5 wants to merge 3 commits into
mainfrom
fix/e2e-doc-and-guards

Conversation

@jpr5

@jpr5 jpr5 commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Why

Three pre-existing issues surfaced while reviewing #299 (the toolResultContains gate). None block that PR; they're worth fixing on their own. Prioritized from the review backlog as the items with real teeth (a publish-safety gap, a fixture-load footgun, and a broken canonical test example).

What

1. Release gate now validates exports + formatting (ci:)
release ran build && test && lint && publish — it skipped test:exports (publint + attw) and format:check, so a broken export map could publish (this class of failure recurred, see CHANGELOG #263). Now: format:check && build && test && lint && test:exports && publish (format up front, exports after build since it needs dist/).

2. A malformed fixture no longer aborts the whole file (fix:)
recordedTimings.interChunkDelaysMs was .filter-ed with no Array.isArray guard (its siblings ttftMs/totalDurationMs are guarded). Since loadFixtureFile maps entries with no try/catch, a single fixture with a non-array value threw a TypeError that took down every fixture in the file — silently, via the loader's warn-and-return-empty wrapper. Now coerced to [].

Red-green on the real loader:

  • RED: TypeError: Cannot read properties of null (reading 'filter') — sibling fixtures fail to load.
  • GREEN: malformed entry sanitized, all fixtures load.

3. Fixed the broken E2E lifecycle in the write-fixtures skill (docs:)
The canonical setup examples loaded fixtures once, then called full reset() in afterEach — which clears the fixture pool, so every test after the first 404s. Switched to the narrow resetMatchCounts() (and suite.llm.resetMatchCounts() for the suite — MockSuite has no resetMatchCounts), and reworded the two prose spots that steered readers to reset() between tests.

A full sweep of every doc surface confirmed the anti-pattern was confined to skills/write-fixtures/SKILL.md — the docs/*.html pages are correct (docs/test-plugins and docs/control-api are the right reference and already warn about it), and pytest fixtures are function-scoped so they're immune.

Test

format:check, lint, tsc --noEmit, test:exports (all 🟢), build, and the full suite (4446 tests) all pass locally.

jpr5 added 3 commits July 15, 2026 15:51
The release script published without verifying formatting or the package
export map, so unformatted code or a broken export map could reach npm
(recurring failure, see CHANGELOG #263). Add format:check before build
(cheap, fail fast) and test:exports after build (needs the built dist),
with npm publish last.
Untrusted fixture JSON may set interChunkDelaysMs to null/missing/non-array.
The sanitizer called .filter with no Array.isArray guard, throwing a TypeError
inside entryToFixture that aborted the entire fixture file's load and silently
dropped every fixture in it. Coerce non-array values to [] like sibling guards.
The E2E setup examples loaded fixtures once then called full reset()
in afterEach, which wipes the in-memory fixture pool and 404s every
test after the first. Switch between-test cleanup to resetMatchCounts()
(suite.llm.resetMatchCounts() for MockSuite) and correct the prose so
it leads with resetMatchCounts() and warns reset() also clears fixtures.
@pkg-pr-new

pkg-pr-new Bot commented Jul 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@copilotkit/aimock@300

commit: 87dcf79

@jpr5

jpr5 commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Folded these three fixes into #299 so they ship in the single 1.37.0 release — avoids a second version bump and the npm latest merge-order hazard. All three commits (release-gate, loader guard, E2E-doc lifecycle) are now on #299 with a CHANGELOG entry for the loader-guard fix.

@jpr5 jpr5 closed this Jul 16, 2026
@jpr5 jpr5 deleted the fix/e2e-doc-and-guards branch July 16, 2026 00:20
jpr5 added a commit that referenced this pull request Jul 16, 2026
…299)

## Why

The showcase mastra interrupt demos use a native suspend tool where pick
and cancel resume with the SAME tool_call_id; the requests differ only
inside the tool-result message content (`{"chosen_time": ...}` vs
`{"cancelled": true}`). Every existing JSON-expressible gate is
identical between the two requests, so first-match-wins replays the pick
confirmation after the user cancelled. `predicate` cannot help: the JSON
fixture loader has no way to express it.

## What

New `match.toolResultContains: string` gate: passes when the request's
LAST message is a `role: "tool"` result whose text content contains the
substring. Mirrors the existing `toolCallId` last-message rule so the
two stay consistent.

- `src/router.ts`: gate placed with the other shape predicates, right
after `toolCallId`
- `src/types.ts`: `FixtureMatch` + `FixtureFileEntry.match`
- `src/fixture-loader.ts`: `entryToFixture` carry-through; validation
(string, non-empty — empty substring would silently act as a catch-all);
userMessage dedup key; catch-all discriminator set
- `packages/aimock-pytest`: `toolResultContains` added to
`_MATCH_LEVEL_OPT_KEYS` (0.5.0)
- docs: fixtures + multi-turn pages, README, write-fixtures skill
- tests: approve/cancel discrimination sharing one toolCallId; substring
present/absent; no-tool-message request never matches; stale tool result
followed by a new user turn never matches; array-of-parts content; null
content; loader validation + dedup + carry-through

## Version

1.37.0 (changelog entry added with this PR's number in a follow-up
commit on this branch).

---

### Maintainer note — additional fixes folded in during review

Beyond the `toolResultContains` gate, this branch now also carries (all
under the single 1.37.0 release):

- **fix:** completed the duplicate-userMessage dedup key so
legitimately-distinct fixtures no longer emit spurious shadow warnings
(kind-aware matcher serialization; `predicate` keyed by identity).
- **fix:** guarded `recordedTimings.interChunkDelaysMs` against
non-array values — a malformed entry no longer throws and silently drops
every fixture in its file.
- **fix:** bumped the `aimock-pytest` default server pin to 1.37.0 so
the auto-download path actually supports the new kwarg the client
forwards.
- **ci:** the `release` script now runs `format:check` + `test:exports`
before publish.
- **docs:** corrected the match-field / dedup / `toolCallId` semantics
docs, and fixed the write-fixtures E2E lifecycle to use
`resetMatchCounts()` instead of a full `reset()` between tests.

(These consolidate PR #300, now closed, into this one release. CHANGELOG
updated accordingly.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant