


Build Data Replication (ETL) Code Without Silent Value Changes or Data Leaks
Use when building or changing code that copies data between databases. It keeps changes small, catches rounding and other silent value changes, returns errors instead of crashing on bad input, keeps secrets and row values out of logs, and checks tests really ran.
To use this AI rule
## Don't Push Code or Touch Migrations Unless Asked
Keep each change focused on the issue being solved. Prefer the smallest coherent diff unless a broader refactor is necessary to preserve correctness.
Before introducing a pattern, dependency, helper, or test fixture, inspect nearby code and follow the established workspace conventions. Treat formatter configuration, compiler diagnostics, and workspace lint settings as the source of truth for enforceable style.
Do not perform repository writes, publish branches, open change requests, or rewrite history unless the user explicitly requests that action.
Do not alter database migrations unless the requested work specifically requires a migration change. This includes comments and non-executable text inside migration files.
Run expensive workspace-wide checks only when their scope is justified. For low-risk documentation or configuration changes, use the smallest relevant validation and report exactly what ran.
## Group Modules by Data Concept and Keep APIs Private by Default
Organize by domain concepts rather than generic buckets. Keep replication behavior and replication-specific types together; place reusable database primitives, source helpers, slot helpers, and metadata access in an integration boundary.
Use package boundaries only when another package has a real dependency on the API. Within one domain, prefer modules with names such as `schema`, `data`, `event`, `store`, `source`, or `slots`.
Keep top-level binaries focused on orchestration. Move implementation details into focused helpers and modules.
Default to private visibility and widen only for a real caller. Prefer private, then package-scoped, then public visibility. Treat every re-export as an API contract:
- avoid wildcard re-exports from internal modules;
- expose only the smallest useful setup surface;
- keep worker orchestration, codecs, runtime plumbing, and implementation details private;
- avoid compatibility facades unless they are an intentional stable API.
Prefer clear, conventional code over clever abstraction. Group types, constructors, public methods, and private helpers in a readable order.
## Catch Rounding and Other Silent Value Changes
Validate values locally when the destination would otherwise accept a silent semantic change, such as rounding, truncation, clamping, coercion, overflow, or precision loss.
If the destination already rejects an unsupported value with an explicit error, do not duplicate expensive validation in the write path without a demonstrated need.
Use checked or trait-based numeric conversions when they are lossless and available. Use intentionally lossy casts only when the loss is understood, range-checked where appropriate, and required by the target representation.
Use an implicit conversion only when the mapping is infallible, semantically lossless, value-preserving, and obvious.
Model patch updates with three distinct states:
- omitted fields preserve the stored value;
- explicit null clears an optional value or resets a defaulted value;
- non-null values replace the stored value.
When converting a complete configuration into patch semantics, use an explicit conversion helper. Do not hide a change from preserve to clear behind a generic conversion trait.
## Return Clear Errors Instead of Crashing on Bad Input
Use typed errors and result values for recoverable failures. Add owned context such as operations, identifiers, counts, types, or statements while preserving the original error as the source chain.
Do not flatten an underlying error into a string unless a deliberately sanitized message is safer for the caller. Public API responses must not expose database-driver errors, internal queries, customer data, or infrastructure details.
Reserve panics for broken programmer invariants. External input, malformed syntax, unsupported values, and system-state failures must return explicit errors.
Parser entry points must handle empty input, unexpected end of input, malformed escapes, invalid byte sequences, invalid ranges, overflow, underflow, and unsupported syntax without panicking.
Prefer checked access, iterators, and byte-slice parsing when input controls offsets. Direct indexing is acceptable only when a local bound or exact-chunk invariant makes it obvious.
Add regression tests for every malformed-input boundary that could previously panic or silently misparse.
## Pick How Background Tasks Stop Based on the State They Hold
Avoid unsafe code unless it is necessary. Every unsafe block must have an adjacent safety explanation that states the invariant making the operation sound.
Prefer ownership and borrowing over unnecessary cloning or interior mutability.
Put long-running asynchronous work behind named tasks or helpers, and choose shutdown behavior according to owned state:
- dropping a task handle detaches the task; explicitly abort discardable background work when it should stop immediately;
- dropping a task set aborts every task it owns;
- do not redundantly abort a task set immediately before leaving the scope that will drop it;
- use explicit abort operations when the task set remains alive but its tasks must stop;
- use graceful signaling and joining when tasks own transactions, buffered destination writes, or retry-sensitive replication state.
Do not add elaborate shutdown coordination for polling or telemetry tasks whose state can be discarded safely.
## Keep Secrets and Customer Data Out of Code and Logs
Treat tracked files, branches, commits, reviews, logs, screenshots, fixtures, generated artifacts, and terminal output as potentially public.
Never place credentials, tokens, private endpoints, customer data, production payloads, personal data, or proprietary configuration in code, tests, documentation, examples, comments, messages, telemetry, panic details, or generated files.
Use invented placeholders in examples. Store real secrets only in approved environment variables, ignored local files, secret managers, or CI secret storage.
If a suspicious value appears real or came from a private environment, do not reproduce it. Stop the publishing path, report the location without quoting the value, and recommend rotation or revocation when exposure may have occurred.
For observability:
- prefer low-cardinality labels;
- log structural metadata such as table names, column names, types, counts, lengths, positions, and operation names;
- never log row values, cell contents, sensitive request or response bodies, credentials, or tokens;
- use safe display formatting for errors and structured fields;
- scrub body capture for sensitive routes and monitoring scopes.
## Test Real Replication Events and Confirm Tests Actually Ran
Run the narrowest relevant tests first, then broaden when shared behavior, regressions, or the request justify it. Verify that expected tests actually executed; a successful command with zero matching tests is not a passing verification.
List tests before applying uncertain filters. Run documentation tests separately when the main test runner does not support them.
Reuse nearby fixtures, database helpers, wait primitives, and assertions before creating custom test plumbing.
For event-driven integration tests:
1. Register notifications or wait handles before the producer can emit the event.
2. Perform source-side writes.
3. Wait for the expected notifications.
4. Shut down and wait for the pipeline before asserting destination state, unless the test explicitly covers in-flight behavior.
5. Assert against cumulative event history unless a restart or isolated phase requires clearing it.
Match expected change-data-capture events to the source database's actual replica-identity behavior. Do not expect partial update rows under modes that cannot emit them.
## Document the Code and Report Which Checks Ran
Document public and private items with concise, precise prose. Link types and methods using the documentation system's supported references. Keep comments punctuated and place them above the code they explain.
Define metric names and label keys as constants, centralize shared labels, and register descriptions once. Keep one focused metric task per source plus a small orchestration surface.
Before completion, confirm the affected scope:
- the changed target compiles;
- formatting passes;
- static analysis passes with the workspace lint configuration;
- relevant unit and integration tests pass and actually ran;
- documentation tests pass when applicable;
- comments and documentation match final behavior;
- metrics, logs, and labels follow naming and data-safety rules.
Report the checks performed, their scope, and any checks intentionally omitted. Do not claim completion from a command's exit status alone when its output shows that no meaningful target was exercised.
