Skip to content

Add pre-commit hook and CLAUDE.md - #3

Merged
jpr5 merged 1 commit into
mainfrom
add-pre-commit-hook
Mar 3, 2026
Merged

Add pre-commit hook and CLAUDE.md#3
jpr5 merged 1 commit into
mainfrom
add-pre-commit-hook

Conversation

@jpr5

@jpr5 jpr5 commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add husky + lint-staged pre-commit hook that runs prettier and eslint on staged files automatically
  • Add CLAUDE.md documenting check workflow and project conventions
  • Prevents CI failures from formatting/lint issues by catching them before commit

Test plan

  • pnpm run format:check passes
  • pnpm run lint passes
  • pnpm test passes (259 tests)
  • Pre-commit hook tested — lint-staged runs on commit

Husky + lint-staged runs prettier and eslint on staged files before
every commit. CLAUDE.md documents the check workflow and project
conventions.
@jpr5
jpr5 merged commit cc3906e into main Mar 3, 2026
6 of 8 checks passed
@jpr5 jpr5 mentioned this pull request Mar 19, 2026
4 tasks
jpr5 added a commit that referenced this pull request Apr 3, 2026
Add pre-commit hook and CLAUDE.md
jpr5 added a commit that referenced this pull request Apr 22, 2026
## Summary

Follow-up to #121 — the 19 migration-page quality items that were
out-of-subject for the multi-turn docs + v1.14.4 release PR. All items
per [this Notion
page](https://www.notion.so/34a3aa3818528177bab7e549fa151a8f).

**Stacked on #121** (base: \`docs/multi-turn-coverage\`). Once #121
merges, GitHub will retarget this to \`main\`.

Highlights:
- Fix \`npx @copilotkit/aimock -f …\` invocations across the migration
guides (broken since the 1.7.0 bin split — route flag-driven usage
through \`npx -p @copilotkit/aimock llmock …\`).
- Replace Go / Rust SDK examples that used nonexistent builder APIs
(\`sashabaranov/go-openai\` canonical \`DefaultConfig\` +
\`NewClientWithConfig\`; \`async-openai\` canonical
\`OpenAIConfig::new().with_api_base\`).
- Add fail-fast to Kotlin + Python health-check loops that silently
swallowed exceptions and let tests proceed against a dead server.
- Harden python-mocks conftest fixtures: restore \`OPENAI_BASE_URL\` /
\`OPENAI_API_KEY\` in finally; \`proc.wait(timeout=10)\` with kill
fallback; \`DEVNULL\` instead of \`PIPE\` + no drain thread.
- Correct invalid JSON in chaos-testing journal + piyook fixture
examples.
- Update stale MSW v2 WebSocket claim, add missing \`msw\` import.
- Fix Python \`on_message\` kwarg mismatch (\`content=…\` → positional
dict).
- Remove unsupported Weaviate claim from VectorMock feature card.
- Add \`livenessProbe\` / \`readinessProbe\` to helm \`values.yaml\`
example.
- Harden GitHub Actions docker teardown (\`--rm\` + \`if: always()\` +
\`docker rm -f\`).
- Wrap \`enableRecording\` programmatic example in try/finally with
\`mock.stop()\` cleanup.

Item #3 (docker-compose \`command: aimock --config …\`
double-invocation) was refuted — no \`command: aimock\` patterns remain
in the current tree; the Notion citation appears to have referenced a
prior state.

## Test plan

- [x] \`pnpm run format:check\` passes
- [x] \`pnpm run lint\` passes
- [x] \`pnpm run test\` — 2437 passed / 26 skipped
- [ ] CI green
jpr5 added a commit that referenced this pull request Apr 23, 2026
## Summary

- Adds mokksy/ai-mocks to the COMPETITORS list in the updater script and
surfaces a visible column in the front-page comparison table.
- Links all 6 competitor headers in the comparison table to their GitHub
repos (previously only mokksy had a link).
- Corrects mokksy/ai-mocks feature cells based on their README:
cross-process interception, chat completions SSE, responses API SSE,
Claude Messages API, Gemini streaming, embeddings API, programmatic API
(Kotlin/JVM), A2A agent mocking, multi-provider (5 providers), and
dependencies (JVM).
- Adds parseCurrentMatrix unit test asserting all 6 headers are
extracted, correctly indexed, and that stripping anchor tags causes the
parser to return 0 headers (regression guard).

## Why

The competitive-matrix updater's header regex requires an <a> inside
each <th>. With only mokksy linked, the parser saw 1 column instead of
6, producing zero-change dry-runs. Linking all headers fixes the parser
and makes the table consistent. The mokksy cells were all initialized to
"No" but the README clearly documents support for multiple features.

## Dry-run output

Parser now correctly identifies 6 competitor columns and 32 capability
rows. The 1 remaining auto-detected change (GitHub Action for mokksy) is
a false positive from a CI badge URL in their README.

## Mokksy cells corrected (evidence from README)

| Feature | Evidence |
|---|---|
| Cross-process interception | "mock HTTP server built with Kotlin and
Ktor" -- real server |
| Chat Completions SSE | "response streaming and Server-Side Events
(SSE)", feature matrix shows Chat Completions YES for all providers |
| Responses API SSE | Feature matrix shows "Responses" under OpenAI
Additional APIs |
| Claude Messages API | Anthropic listed as supported service #2 with
Chat Completions and Streaming YES |
| Gemini streaming | Google VertexAI Gemini listed as service #3 with
Streaming YES |
| Embeddings API | Feature matrix shows Embeddings YES for OpenAI and
Ollama |
| Programmatic API | Kotlin/JVM library with Maven Central artifacts |
| A2A agent mocking | A2A Protocol listed as service #5 with full
protocol support |
| Multi-provider | 5 services: OpenAI, Anthropic, Gemini, Ollama, A2A |
| Dependencies | JVM (Kotlin) -- not "No" |

## Test plan

- [x] pnpm lint -- clean
- [x] pnpm test -- 2492 passed, 36 skipped
- [x] Dry-run: parser identifies 6 columns, 32 rows
- [x] New parseCurrentMatrix tests: 4 assertions covering header count,
index mapping, row data, and plain-text regression
- [ ] CI green on this PR
jpr5 added a commit that referenced this pull request Jun 24, 2026
… warn/opt-out (#276)

## TL;DR — what this is

**What aimock does:** records real LLM API calls once, then replays the
recorded responses in tests — no live API, deterministic, fast.

**The change in one sentence:** matching a replay to a recorded fixture
is now anchored on the **content** of the request (the messages/tools
being sent), not on the **turn number** it happened to be recorded at.

**Why:** the old logic treated "this is turn #3" as a hard requirement.
If your test's conversation drifted by even one turn from when it was
recorded, replay would **fail to match** and error out — brittle. Real
conversations reorder, retry, and branch. Content is the stable
identity; turn position is not.

### What actually changed
- **Replay:** `turnIndex` is no longer a hard reject gate. A fixture
matches if its **content** matches; turn position becomes a tiebreaker,
not a veto.
- **Record:** unchanged — still strict (records exactly what happened,
in order).
- **Visibility:** when a fixture matches at a *different* position than
recorded, aimock emits a **divergence warning** (with a `matchedBy`
reason), so drift is never silent.
- **Escape hatch:** `AIMOCK_STRICT_TURN_INDEX=1` restores the old strict
behavior.

### Risk surfaces (public pkg, ~500k downloads/wk) → mitigations
| Risk | What could go wrong | Mitigation |
| --- | --- | --- |
| **Looser matching → wrong fixture** | Replay silently returns a
fixture that isn't the right one | Content predicates are **primary** —
a match still requires the request content to match. Position is only
relaxed *after* content already matches. A truly wrong match needs two
content-identical requests at different turns (rare outside
showcase-style harnesses). |
| **Silent false-green in tests** | A test passes against a mismatched
fixture, hiding a real regression | The **divergence warn** makes every
position-drift visible; `matchedBy` / `describeMatch` explain *why* a
fixture matched. We never hide the drift — we surface it. |
| **Breaking change on a minor bump** | Existing users depending on
strict turn-index matching get different behavior | Change is
**additive** (relaxation only as a fallback); the canonical case
(record→replay at the same position) is byte-for-byte unchanged.
`AIMOCK_STRICT_TURN_INDEX=1` opt-out preserves old behavior → ships as
**minor, not major**. |
| **Surprise / hard to debug** | "Why did this fixture match?" | New
`matchedBy` field + a throttled, human-readable warning explain the
match decision. |

**Bottom line:** it trades a brittle, position-coupled gate for a
content-anchored match that mirrors how conversations really behave —
while keeping strict mode one env var away, recording untouched, and
every divergence loud rather than silent. The empirical blast-radius
check on our corpus showed **zero wrong-fixture matches**.

---

## Summary

Makes aimock's fixture **replay** matching content-anchored: a fixture's
recorded **content/shape** is the primary match key, and `turnIndex` is
no longer a hard reject gate. This kills a class of false-misses where a
multi-bubble / multi-turn run that drifts *past* its scripted
`turnIndex` was spuriously rejected (`empty assistant response`), even
though the content matched. The **record** path is unchanged — strict
per-turn capture is preserved via `MatchOptions.strictTurnIndex`.

## ⚠️ Public replay-semantics change — please confirm intended

A fixture that previously **force-missed** on `turnIndex` will now
**match** if its content predicates match. **Empirically measured** by
replaying the real fixture corpora (showcase d6 + aimock examples)
through the old vs new matcher — **786 fixture sets / 9769 requests**:

- **3213 MISS→MATCH** (the intended false-miss fixes), **0 MATCH→MISS**,
**0 wrong-fixture selections** (the 69 MATCH→DIFF are all same-content
twins / forward-progress).
- **0 changes at the canonical position** (`assistantCount ==
turnIndex`) — divergence happens *only* when a conversation runs
off-by-N from its scripted turn.

So this is a strict improvement on real corpora; it's a **minor** bump.
(Content gating stays strict; multiple content matches → closest
scripted `turnIndex`; all-ahead → unpositioned fallback.)

## Not surprising anyone (detection + reversible opt-out)

For the divergence to be discoverable + reversible by the
~500k-downloads/wk user base:

- **`AIMOCK_STRICT_TURN_INDEX=1`** — process opt-out that restores the
legacy strict-turnIndex hard gate for replay (default = new
content-anchored behavior). This reversible escape hatch is what keeps
the change a minor bump.
- **Surgical divergence warn** — a once-per-fixture, logger-gated `warn`
fires only when a served fixture's `turnIndex` was relaxed (i.e. the
strict gate would have rejected it). **Silent in programmatic use**
(logger default), so it never spams a test suite; for the typical
record→replay user (who sits at the canonical position) divergence is
rare, so the warn is a genuine, useful signal.
- **Diagnostic fields** `turnIndexRelaxed` / `matchedBy` on the match
diagnostic for inspection.

## Review

Converged via two full `cr-loop` passes (content-anchored matching, then
the warn package) — 7 unbiased agents/round + confirmation rounds +
Procedure-3 audits (0 promotions). Notable fixes caught in review:
WeakSet-identity throttle (predicate/RegExp fixtures no longer collide),
accurate `matchedBy` attribution, and the divergence-direction wording.

## Test plan
- [x] `pnpm test` — **4041 passed / 0 failed**; `tsc --noEmit` clean;
lint 0/0; build ok
- [ ] **Confirm the intended replay semantics** (above) before merge

## Known follow-ups (pre-existing, not this PR)
- `getSystemText`/`getTextContent` present-but-empty `""` stray-newline
edge under `useExactMatch`.
- Handler input-validation: unguarded `parts`/`content` derefs
(gemini/cohere/responses/bedrock/bedrock-converse) → TypeError on
malformed JSON.

Not auto-merging — flagging the replay-semantics change for review.
jpr5 added a commit that referenced this pull request Jul 15, 2026
## Summary

- Adds mokksy/ai-mocks to the COMPETITORS list in the updater script and
surfaces a visible column in the front-page comparison table.
- Links all 6 competitor headers in the comparison table to their GitHub
repos (previously only mokksy had a link).
- Corrects mokksy/ai-mocks feature cells based on their README:
cross-process interception, chat completions SSE, responses API SSE,
Claude Messages API, Gemini streaming, embeddings API, programmatic API
(Kotlin/JVM), A2A agent mocking, multi-provider (5 providers), and
dependencies (JVM).
- Adds parseCurrentMatrix unit test asserting all 6 headers are
extracted, correctly indexed, and that stripping anchor tags causes the
parser to return 0 headers (regression guard).

## Why

The competitive-matrix updater's header regex requires an <a> inside
each <th>. With only mokksy linked, the parser saw 1 column instead of
6, producing zero-change dry-runs. Linking all headers fixes the parser
and makes the table consistent. The mokksy cells were all initialized to
"No" but the README clearly documents support for multiple features.

## Dry-run output

Parser now correctly identifies 6 competitor columns and 32 capability
rows. The 1 remaining auto-detected change (GitHub Action for mokksy) is
a false positive from a CI badge URL in their README.

## Mokksy cells corrected (evidence from README)

| Feature | Evidence |
|---|---|
| Cross-process interception | "mock HTTP server built with Kotlin and
Ktor" -- real server |
| Chat Completions SSE | "response streaming and Server-Side Events
(SSE)", feature matrix shows Chat Completions YES for all providers |
| Responses API SSE | Feature matrix shows "Responses" under OpenAI
Additional APIs |
| Claude Messages API | Anthropic listed as supported service #2 with
Chat Completions and Streaming YES |
| Gemini streaming | Google VertexAI Gemini listed as service #3 with
Streaming YES |
| Embeddings API | Feature matrix shows Embeddings YES for OpenAI and
Ollama |
| Programmatic API | Kotlin/JVM library with Maven Central artifacts |
| A2A agent mocking | A2A Protocol listed as service #5 with full
protocol support |
| Multi-provider | 5 services: OpenAI, Anthropic, Gemini, Ollama, A2A |
| Dependencies | JVM (Kotlin) -- not "No" |

## Test plan

- [x] pnpm lint -- clean
- [x] pnpm test -- 2492 passed, 36 skipped
- [x] Dry-run: parser identifies 6 columns, 32 rows
- [x] New parseCurrentMatrix tests: 4 assertions covering header count,
index mapping, row data, and plain-text regression
- [ ] CI green on this PR
jpr5 added a commit that referenced this pull request Jul 15, 2026
… warn/opt-out (#276)

## TL;DR — what this is

**What aimock does:** records real LLM API calls once, then replays the
recorded responses in tests — no live API, deterministic, fast.

**The change in one sentence:** matching a replay to a recorded fixture
is now anchored on the **content** of the request (the messages/tools
being sent), not on the **turn number** it happened to be recorded at.

**Why:** the old logic treated "this is turn #3" as a hard requirement.
If your test's conversation drifted by even one turn from when it was
recorded, replay would **fail to match** and error out — brittle. Real
conversations reorder, retry, and branch. Content is the stable
identity; turn position is not.

### What actually changed
- **Replay:** `turnIndex` is no longer a hard reject gate. A fixture
matches if its **content** matches; turn position becomes a tiebreaker,
not a veto.
- **Record:** unchanged — still strict (records exactly what happened,
in order).
- **Visibility:** when a fixture matches at a *different* position than
recorded, aimock emits a **divergence warning** (with a `matchedBy`
reason), so drift is never silent.
- **Escape hatch:** `AIMOCK_STRICT_TURN_INDEX=1` restores the old strict
behavior.

### Risk surfaces (public pkg, ~500k downloads/wk) → mitigations
| Risk | What could go wrong | Mitigation |
| --- | --- | --- |
| **Looser matching → wrong fixture** | Replay silently returns a
fixture that isn't the right one | Content predicates are **primary** —
a match still requires the request content to match. Position is only
relaxed *after* content already matches. A truly wrong match needs two
content-identical requests at different turns (rare outside
showcase-style harnesses). |
| **Silent false-green in tests** | A test passes against a mismatched
fixture, hiding a real regression | The **divergence warn** makes every
position-drift visible; `matchedBy` / `describeMatch` explain *why* a
fixture matched. We never hide the drift — we surface it. |
| **Breaking change on a minor bump** | Existing users depending on
strict turn-index matching get different behavior | Change is
**additive** (relaxation only as a fallback); the canonical case
(record→replay at the same position) is byte-for-byte unchanged.
`AIMOCK_STRICT_TURN_INDEX=1` opt-out preserves old behavior → ships as
**minor, not major**. |
| **Surprise / hard to debug** | "Why did this fixture match?" | New
`matchedBy` field + a throttled, human-readable warning explain the
match decision. |

**Bottom line:** it trades a brittle, position-coupled gate for a
content-anchored match that mirrors how conversations really behave —
while keeping strict mode one env var away, recording untouched, and
every divergence loud rather than silent. The empirical blast-radius
check on our corpus showed **zero wrong-fixture matches**.

---

## Summary

Makes aimock's fixture **replay** matching content-anchored: a fixture's
recorded **content/shape** is the primary match key, and `turnIndex` is
no longer a hard reject gate. This kills a class of false-misses where a
multi-bubble / multi-turn run that drifts *past* its scripted
`turnIndex` was spuriously rejected (`empty assistant response`), even
though the content matched. The **record** path is unchanged — strict
per-turn capture is preserved via `MatchOptions.strictTurnIndex`.

## ⚠️ Public replay-semantics change — please confirm intended

A fixture that previously **force-missed** on `turnIndex` will now
**match** if its content predicates match. **Empirically measured** by
replaying the real fixture corpora (showcase d6 + aimock examples)
through the old vs new matcher — **786 fixture sets / 9769 requests**:

- **3213 MISS→MATCH** (the intended false-miss fixes), **0 MATCH→MISS**,
**0 wrong-fixture selections** (the 69 MATCH→DIFF are all same-content
twins / forward-progress).
- **0 changes at the canonical position** (`assistantCount ==
turnIndex`) — divergence happens *only* when a conversation runs
off-by-N from its scripted turn.

So this is a strict improvement on real corpora; it's a **minor** bump.
(Content gating stays strict; multiple content matches → closest
scripted `turnIndex`; all-ahead → unpositioned fallback.)

## Not surprising anyone (detection + reversible opt-out)

For the divergence to be discoverable + reversible by the
~500k-downloads/wk user base:

- **`AIMOCK_STRICT_TURN_INDEX=1`** — process opt-out that restores the
legacy strict-turnIndex hard gate for replay (default = new
content-anchored behavior). This reversible escape hatch is what keeps
the change a minor bump.
- **Surgical divergence warn** — a once-per-fixture, logger-gated `warn`
fires only when a served fixture's `turnIndex` was relaxed (i.e. the
strict gate would have rejected it). **Silent in programmatic use**
(logger default), so it never spams a test suite; for the typical
record→replay user (who sits at the canonical position) divergence is
rare, so the warn is a genuine, useful signal.
- **Diagnostic fields** `turnIndexRelaxed` / `matchedBy` on the match
diagnostic for inspection.

## Review

Converged via two full `cr-loop` passes (content-anchored matching, then
the warn package) — 7 unbiased agents/round + confirmation rounds +
Procedure-3 audits (0 promotions). Notable fixes caught in review:
WeakSet-identity throttle (predicate/RegExp fixtures no longer collide),
accurate `matchedBy` attribution, and the divergence-direction wording.

## Test plan
- [x] `pnpm test` — **4041 passed / 0 failed**; `tsc --noEmit` clean;
lint 0/0; build ok
- [ ] **Confirm the intended replay semantics** (above) before merge

## Known follow-ups (pre-existing, not this PR)
- `getSystemText`/`getTextContent` present-but-empty `""` stray-newline
edge under `useExactMatch`.
- Handler input-validation: unguarded `parts`/`content` derefs
(gemini/cohere/responses/bedrock/bedrock-converse) → TypeError on
malformed JSON.

Not auto-merging — flagging the replay-semantics change for review.
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