Search/Change a Multi-Language Data Library Without Breaking Its File Formats or APIs

Change a Multi-Language Data Library Without Breaking Its File Formats or APIs

Use when an AI changes a fast data library with a native core and thin language bindings. It keeps stable file formats and public APIs compatible, uses the documented commands, and adds tests and docs.

To use this AI rule
## Learn the Native Core and Its Thin Language Bindings Treat the repository as one native core with thin bindings for other languages. The core owns data-format behavior, encoding, compression, files, indexes, I/O, object storage, linear algebra, table operations, optional domains, test infrastructure, and developer tools. Bindings expose that behavior without duplicating validation or business logic. Preserve the architecture's principal traits: asynchronous I/O, native columnar memory, versioned writes with manifest tracking, workload-oriented encodings, and one storage abstraction across local and remote object stores. Read the repository guide and the guide for every affected language or directory before planning commands or code changes. Directory-specific rules refine the repository contract rather than replacing it. ## Keep Stable Formats Compatible and Change Unstable Ones Freely Treat every format declared stable as a durable compatibility contract. A change to stable data must preserve both backward compatibility and forward compatibility. Treat every format declared unstable as disposable. It may change freely; do not add compatibility code, migrations, fallbacks, or tests for files created by earlier unstable revisions. Evaluate new compatibility work against the latest released stable version while continuing to honor every stable contract. States that exist only on the current development branch are not compatibility constraints. Do not weaken a cleaner or more complete design merely to preserve an unreleased intermediate state. ## Use the Documented Commands and Build Profiles, Not Another Toolchain Discover the documented check, test, lint, format, coverage, benchmark, and integration commands for the affected language before running them. Use package and test selectors supplied by the repository rather than inventing a parallel workflow. Do not substitute another environment manager or toolchain because a command appears missing, slow, or unavailable. If a language-specific command fails outside the documented workflow, first correct the environment usage and rerun the prescribed command. Only then conclude that a dependency or tool is unavailable. Use repository-defined compilation profiles instead of ad hoc optimization overrides. Benchmark and profile with an optimized build that retains debug symbols. Disable link-time optimization only for local debugging, I/O-bound benchmarks, or compile-time investigations where it cannot affect the measured bottleneck. For integration tests, start only the required fixture services, set the documented environment values, and run the designated integration target. ## Write Focused Code That Streams Data Instead of Loading It All Into Memory Use English in code, examples, and comments. Code should optimize for readability as well as execution. Add comments only when they explain a non-obvious reason, and remove direct debug printing before review in favor of the established tracing or logging system. Extract logic repeated in two or more places into a shared helper. Keep one-use logic at its call site. Introduce a helper only when it materially lowers cognitive load or removes substantial duplication; do not add a thin wrapper that merely renames or forwards a call. Keep changes focused. Avoid drive-by refactoring, reformatting, and cosmetic edits. Preserve streaming behavior instead of collecting batches into memory, and prefer compact bitmap structures over general hash sets for dense integer membership. ## Keep Bindings Thin and Deprecate Old Public APIs Instead of Breaking Them Centralize validation and logic in the native core, and keep every language binding as a thin wrapper. Use the same parameter names across all bindings; rename them everywhere or nowhere. Never break an existing public signature in place. Deprecate the old method with the language's supported annotation and add a new method. Replace groups of mutually exclusive boolean flags with one enum or mode parameter. When core behavior changes, update each applicable binding, generated interface, example, and compatibility test together. A binding must not silently reinterpret a value that the core rejects. ## Name Values by What They Are and Reject Invalid Input With Clear Errors Name a variable after what its value is, not how it happens to be used. Drop redundant prefixes when the containing type or module already provides the domain. Use one approved plural consistently in APIs and documentation, and prefer storage-neutral names over provider-specific terms. When renaming a type, structure, or enum, update methods, fields, variables, tests, and documentation together. Validate input at API boundaries and reject invalid values with descriptive errors. Never silently clamp or adjust them. Reject mutually exclusive builder or configuration options when both are present. Include relevant variable names, values, sizes, and types in error messages. ## Add Few Dependencies and Keep Lockfiles in Sync Prefer the standard library or existing workspace dependencies before adding an external package. Keep lockfile changes intentional, revert unrelated upgrades, and pin a broken dependency only with a comment that links to its upstream issue. When a workspace dependency changes, refresh the root lockfile and every excluded binding lockfile through the documented manifest-specific checks, then commit all corresponding updates. Keep the offline lockfile synchronization hook active. Place optional or domain-specific dependencies behind feature flags. Prefer a separate package for substantial domain functionality so default builds and consumers do not inherit unnecessary code. ## Add a Test for Every Bug Fix and Feature Every defect correction and feature requires corresponding tests. Do not accept code without tests. Use the language's parameterized-test facility when cases differ only by input, and give cases readable names. Replace prints in tests with assertions because output alone does not detect regressions. Extend an existing test when it already owns the behavior instead of adding an overlapping test file. Put additions in the established test location. A skipped test must link to a tracking issue; never add a bare skip annotation without a durable reason. ## Test With Null and Empty Data and Old Stable Files Include multi-fragment scenarios for reads, indexes, and scans. In index tests, cover null elements, all-null collections, empty collections, and null columns. Vector-index tests must assert a recall metric of at least 0.5 rather than checking only that index creation succeeded. For backward compatibility, use checked-in datasets produced by older stable versions. Keep a data-generation script that asserts the producer version, and copy these fixtures to temporary storage before reading them. Do not disable documentation tests merely to avoid compilation. Write examples that compile inside a function-shaped context. Exclude test-only utilities from coverage with the project's approved coverage annotation rather than weakening production coverage thresholds. ## Document Every Public API With an Example That Compiles Document every public API and include an example. Link to the relevant types and methods, and keep examples synchronized with real signatures whenever code is refactored. Use ASCII tree diagrams for hierarchical structures such as encoding layers, file formats, and storage layouts. Follow the documentation system's indentation contract, including four-space indentation beneath admonition directives. Proofread comments and documentation before review. Documentation that no longer compiles or names an obsolete API is a compatibility defect, not a cosmetic issue. ## Label Issues and Pass Lint Checks Before Opening a Change Request When creating an issue through a command-line tool or API, pass the explicit label for defect, feature, or performance work because template automation may be bypassed. Prefix the title with the matching classification so content-based automation has a reliable fallback signal. Use the repository's conventional change-title prefixes, adding a scope when useful. Before creating or updating a change request, run the required lint checks for every touched language surface, even when they are expensive. Follow each language guide's environment workflow for its lint commands. If a required check cannot run, state the blocker explicitly in the change-request summary rather than implying that validation is complete. ## Focus Reviews on Serious Bugs and Security Problems Contributor and maintainer attention is limited. Keep reviews concise and clear, and focus on highest-severity defects, performance regressions, and security concerns. Do not restate detailed changes or repeat what is already correct. Check naming consistency, established error-handling patterns, and test coverage. Cite the affected location and explain the concrete consequence of each blocking finding.