


Change a Managed Wrapper and Its Native Library Without Breaking the ABI
Use when editing a cross-platform managed wrapper around a native C library. It traces each change through every layer, builds native binaries from source instead of using stale downloads, keeps the public ABI stable, handles pointer ownership and threads, and never skips failing tests.
To use this AI rule
## Trace Each Change from the Managed Wrapper Down to the Native Code
Treat the library as a layered system: managed wrapper, interop declarations, stable C-compatible API, and native implementation.
- Validate public inputs in the managed wrapper.
- Keep the C-compatible bridge small and predictable; it should translate and forward rather than duplicate policy.
- Preserve exact agreement among managed declarations, exported C symbols, parameter types, return types, and native implementations.
- Determine which layer owns every validation rule, allocation, error conversion, and lifetime transition before editing code.
- Trace a change through every affected layer instead of fixing only the first compile error.
A change that compiles in the managed layer can still fail at runtime when the native binary lacks the new symbol or uses a different signature.
## Choose Prebuilt or Source-Built Native Binaries by Change Type
Native binaries must exist before the managed wrapper can build, but the correct source of those binaries depends on what changed.
- For managed-only work, use the repository's approved prebuilt native artifacts for the current released milestone.
- For native implementation, C API, dependency, submodule, version, or milestone changes, build native binaries from source for the target platform and architecture.
- After any native-facing change, downloading current prebuilt binaries is forbidden until those changes have shipped to the artifact source.
- If the native source build fails, diagnose its toolchain, dependency, path, or network prerequisite. Never fall back to stale downloads merely to make the managed build pass.
- Clean platform caches when they can retain an older native module and create false test results.
Using stale native binaries after an API change can produce a silently incorrect build followed by missing-entry-point failures at runtime.
## Regenerate Bindings Instead of Editing Them and Keep the ABI Stable
Do not manually edit generated bindings, generated documentation, rendered release pages, or other declared generated output. Change the editable source and run the repository's generator after any C API change.
Maintain a stable application binary interface:
- Add overloads, methods, or types rather than changing existing signatures.
- Do not remove public APIs or change their return types.
- Prefer overloads to new default parameters because defaults can alter binary compatibility.
- Deprecate an API instead of removing it, and provide actionable migration guidance.
- Keep naming and parameter conventions consistent with surrounding public APIs.
- Regenerate bindings and verify the resulting diff before building.
When documentation has generated pages and editable source sidecars, edit only the designated source and re-render the output.
## Classify Native Pointer Ownership and Keep Mutable Objects on One Thread
Classify every native pointer before writing a wrapper:
- A borrowed parameter does not transfer ownership.
- A manually owned native object requires the wrapper's native-disposal path.
- A reference-counted object must use the wrapper's reference-count contract.
Match failure behavior to the layer. The C-compatible API returns a null pointer or `false`; a managed factory returns `null`; a managed constructor throws when it cannot establish a valid object.
Some native operations may return the same instance they received. Compare identities before disposing the source, or the returned value may reference an object that has already been released.
Do not share mutable native objects across threads unless the native contract explicitly permits it. Give each thread its own mutable state; share only objects documented as immutable and thread-safe.
Never pass a temporarily fixed managed pointer to native code that can retain it beyond the fixed scope. Use a pinned handle with an explicit lifetime or allocate unmanaged memory. Violating this rule allows the garbage collector to move memory while native code still holds the old address.
## Use Feature Branches in Both the Parent Repo and the Native Submodule
Create a feature branch before making commits. Never commit directly to protected branches.
Apply the same rule independently to a native submodule or nested repository: it has its own branch state, protected branches, commits, and review path.
1. Create a focused feature branch in the parent repository.
2. If the nested native repository changes, create its feature branch before editing or committing there.
3. Keep all commits on the corresponding feature branches.
4. Submit changes for review through the repository's approved pull-request workflow.
5. Confirm that the parent records the intended reviewed native revision.
Do not modify upstream-owned native directories outside the repository's declared shim or extension surface.
## Test Against the Right Native Binaries Without Skipping Failures
A successful build is not completion. Run the relevant managed tests against native binaries produced by the correct bootstrap path.
- Tests must pass before completion is claimed.
- Do not skip a failing test or introduce a skip exception to bypass a regression.
- A skip is acceptable only for a demonstrated hardware or platform limitation, such as a missing display, unavailable graphics device, or unsupported platform feature.
- Regenerate bindings before testing when the C API changed.
- Clean cached native web or platform artifacts before validating version changes.
- Verify representative platforms and architectures for native-facing work.
Write tests that fail when ownership, null handling, exported symbols, ABI, thread scope, or disposal behavior is wrong. Include parity tests and benchmarks for performance changes when the exact behavior or benefit is not already proven.
## Find Cross-Layer Bugs from the Shape of the Failure
Establish a known-good baseline, change one variable at a time, record each result, and treat platform differences as evidence. If a change makes the state worse, revert it before testing another hypothesis.
Use failure shape to select the first boundary to inspect:
- A missing managed type suggests bindings were not regenerated or included.
- An unresolved native symbol suggests the C declaration and implementation disagree.
- A missing entry point at runtime strongly suggests stale native binaries or an export mismatch.
- An access violation suggests ownership, disposal, pinning, or signature corruption.
- A null-reference failure after a factory call suggests the native layer returned failure and the caller ignored it.
- Random cross-thread crashes suggest mutable native objects were shared.
Before completion, inspect generated diffs, build native code when required, build the managed wrapper, run tests, and verify the affected platform matrix. Report the exact bootstrap source and architecture used. Do not claim success when tests fail, required native builds were replaced by downloads, or platform verification remains incomplete.
