


Change a Client Library's Sync and Async Code Together Without Breaking Its API
Use when an AI changes a mature client library that has matching sync and async clients. It keeps public APIs and errors unchanged, applies the same change to both sides, keeps hot paths fast, and tests from one failing case up to the full test matrix.
To use this AI rule
## List the Public Behavior That Must Not Change Before Editing
Before editing, identify the public behavior, supported runtime versions, relevant architecture, and the smallest set of files that owns the change.
- Treat public signatures, argument names, defaults, return types, exceptions, and edge-case behavior as compatibility contracts.
- Locate all parallel implementations of the same behavior, such as synchronous and asynchronous clients.
- Identify routing, parsing, policy, connection, and response-shaping layers that may participate in the behavior.
- Read nearby implementations, tests, and type overloads before choosing a pattern.
- Preserve existing protocol and server semantics unless the requested change explicitly requires otherwise.
- Do not expand the task into unrelated cleanup.
If the requested behavior conflicts with a documented invariant, stop and surface the conflict instead of silently redefining the contract.
## Apply the Same Change to Both the Sync and Async Clients
When a library provides equivalent client stacks, treat parity as a required property.
1. Map the changed synchronous symbol to its asynchronous counterpart, or vice versa.
2. Compare signatures, overloads, type hints, defaults, return shapes, errors, and side effects.
3. Apply equivalent behavior in both stacks unless a documented runtime difference makes that impossible.
4. Keep shared command surfaces, routing tables, parser callbacks, and policy metadata consistent.
5. Add paired tests where both stacks expose the behavior.
Use the codebase's established type syntax and overload conventions. If only one side should change, record the concrete architectural reason and verify that the asymmetry is intentional.
## Change Only the Owning Layer and Reuse Existing Helpers
Prefer a surgical change that reuses existing helpers and conventions.
- Modify only the layer that owns the behavior.
- Preserve public APIs and error semantics.
- Avoid new dependencies unless existing facilities cannot satisfy the requirement.
- Avoid new abstractions when a current helper or pattern is sufficient.
- Keep imports minimal and at module scope unless deferred import is technically necessary.
- Update documentation strings when behavior changes, keeping them consistent with types.
Correctness comes first. Optimize only after the behavior is correct, and never trade correctness for lower allocation or faster execution.
## Keep Hot Paths Fast Without Changing Protocol Behavior
Review hot-path changes for avoidable allocations, repeated parsing, duplicated work, and large temporary structures.
- Reuse established helpers and cached metadata.
- Preserve wire-protocol selection independently from user-facing response shaping.
- Keep routing and aggregation policies consistent with command semantics.
- Handle redirects, retries, topology differences, and error propagation explicitly when relevant.
- Do not silently normalize an edge case that the underlying service exposes distinctly.
Performance changes must retain identical observable behavior. If an optimization complicates correctness or parity, prefer the simpler correct implementation.
## Test from One Failing Case Up to the Full Test Matrix
Build evidence in increasing scope:
1. Add or update a deterministic test that fails before the fix.
2. Run the smallest relevant test or suite.
3. Run the mirrored sync/async coverage.
4. Exercise meaningful protocol, response-mode, topology, or optional-feature combinations.
5. Run linters, formatting checks, and type checks required by the project.
6. Run the full supported test matrix when the change can affect shared behavior.
Do not weaken assertions, remove coverage, or reclassify failures merely to make the suite pass. Tests requiring external infrastructure should remain clearly separated from default local suites.
## Check the Final Diff for Debug Code and Sync/Async Mismatches
Before completion, inspect the full change and verify:
- only relevant files and lines changed;
- no debug code, accidental deletions, or unrelated formatting churn remains;
- public APIs and error behavior are unchanged unless explicitly intended;
- mirrored implementations and type hints remain aligned;
- tests cover success, edge cases, and failure paths;
- linting and required test suites pass;
- documentation matches the implemented behavior;
- no unnecessary dependency, allocation, or abstraction was introduced.
Fix every discovered issue and repeat the affected checks. Report what changed, what was validated, and any explicitly untested external scenario without claiming broader verification.
