mntablemerge

    Structural three-way table merging with explicit decisions and no invented cell values.

    csv
    table
    three-way-merge
    conflict
    Download zip
    Author
    Version
    0.1.0
    License
    MIT
    Last updated
    5 days ago
    Downloads
    14

    #MoonTableMerge

    Structural three-way merge for keyed CSV tables, written in MoonBit. For developers who maintain catalogs, lookup tables or reference datasets in Git: independent cell edits merge, while destructive row/column changes and unknown intersections require explicit review.

    #Install the library

    moon add cn-hww/mntablemerge@0.1.0

    Import "cn-hww/mntablemerge" @tablemerge in your package's moon.pkg. Follow the library workflow for a minimal merge/review example and the adoption guide for an independent consumer. Version 0.1.0 has an early public API; compatibility rules are in the API contract.

    Quoted TSV transport is also implemented end to end: --format tsv for merge/review/replay/git-driver, plus strict CSV/TSV conversion. See TSV contract; moon run scripts/tsv-demo.mbtx runs the real-data workflow.

    Editable review sheets let reviewers fill choice/value columns in TSV while checking the complete originating inputs and conflict evidence. review-sheet → review-apply-sheet → replay retains the structural rules and rejects stale or damaged sheets. Run moon run scripts/sheet-demo.mbtx for the offline workflow.

    Explicit assignments export/apply only selected current conflicts while retaining complete input bindings and all other pending work. moon run scripts/scope-demo.mbtx demonstrates assignment guards and replay.

    Independent review combination merges agreeing owner results, rejects disagreement and changed baseline choices, and rechecks structural dependencies. moon run scripts/combine-demo.mbtx exercises two assigned reviews and exact replay.

    Review version comparison reports added/changed/revoked resolutions with complete before/after evidence, including dropped structural values. moon run scripts/audit-demo.mbtx exercises destructive revision, comparison and replay.

    Implemented in 0.1.0: compound keys, exact string values, row/column insertion and deletion, structural and cell conflicts, strict CSV, JSON diagnostics/decisions, input-bound offline review with explicit decision revision/revocation, source row ordering, explicit column/row lineage, declared uniqueness review, a Git merge driver, a file CLI, an offline browser reviewer, and real-data examples.

    Column evolution supports explicit field rename versus deletion: a missing branch source is declared as null, and keep/drop is reviewed even for unchanged values or empty tables. moon run scripts/evolution-demo.mbtx exercises complete review, dependent revocation, audit and CSV/TSV replay.

    #Try it

    Install MoonBit. Validated toolchain: moonc v0.10.14+7d59c7ec9, moon 0.1.20260920. Use the current moon.mod / moon.pkg formats.

    moon run scripts/demo.mbtx moon run cmd/main -- merge examples/airports/base.csv examples/airports/ours.csv examples/airports/theirs.csv --key id moon run cmd/main -- merge examples/airports/base.csv examples/airports/structure-ours.csv examples/airports/structure-theirs.csv --key id --report moon run cmd/main -- merge examples/airports/base.csv examples/airports/structure-ours.csv examples/airports/structure-theirs.csv --key id --decisions examples/airports/decisions.json

    The third command exits 1, reporting MissingCell at row 6524, column review_owner. The final command explicitly supplies team-B and produces a complete rectangular CSV. Example provenance and expected output explain every change.

    CLI merge BASE OURS THEIRS --key id[,other] [--decisions FILE] [--report]: complete CSV on stdout; unresolved JSON on stderr; --report sends only JSON to stdout. Exit 0 complete, 1 unresolved, 2 invalid input/I/O. Input files are read only. The CLI accepts arbitrary files, not just the fixtures.

    Use --output FILE with merge or replay to save a complete table to a new file. It writes/syncs an exclusive sidecar and publishes without replacing existing files; unresolved or invalid merges create no output. It cannot be combined with --report. Run moon run scripts/output-demo.mbtx for CSV/TSV, input preservation and failure/retry. Avoid shell redirection over an input file: shells truncate before running the CLI.

    CLI key names are comma-separated; for a header containing a literal comma, use the library's key array API.

    For decisions reviewed outside the immediate process, use input-bound offline review: review → review-resolve → replay. It rejects stale inputs instead of applying old choices to another revision. moon run scripts/review-demo.mbtx demonstrates the complete workflow.

    To correct or undo recorded choices, use review revision: review-revise BUNDLE EDITS. Structural changes require explicit revocation of inapplicable dependent decisions. The previous bundle remains usable; moon run scripts/revision-demo.mbtx exercises failure/retry, undo and reopened review on real source records.

    For CSV tracked in Git, the merge-driver guide documents configuration and conflict recovery. moon run scripts/git-demo.mbtx exercises actual branch merges in disposable repositories, including structural review and error recovery. This helper requires Git and an already configured user.name / user.email.

    #Library workflow

    Uniqueness-aware review detects declared non-primary unique tuples that collide after otherwise valid branch edits. UniqueReview preserves all affected source records, accepts explicit field repairs/record deletion, rejects newly introduced duplicate pairs, and binds rules into offline replay. The CLI and browser support the full workflow; run moon run scripts/unique-demo.mbtx.

    Follow the adoption guide for registry installation, a runnable independent module and local workspace development. moon run scripts/release-check.mbtx verifies the actual local package ZIP on all four library targets; scripts/registry-check.mbtx independently checks an available registry release. Import "cn-hww/mntablemerge" @tablemerge. The generated public interface and API contract describe the supported workflow. Neither consumer check uploads a module. Release evidence is recorded in validation.

    let base = @tablemerge.parse_csv("id,value\nx,old\n")
    let ours = @tablemerge.parse_csv("id,value\nx,ours\n")
    let theirs = @tablemerge.parse_csv("id,value\nx,theirs\n")
    let plan = @tablemerge.merge(base, ours, theirs, key=["id"])
    let reviewed = @tablemerge.resolve(plan, [{
    target: @tablemerge.Cell(@tablemerge.RowKey(["x"]), "value"),
    choice: @tablemerge.Value("reviewed"),
    }])
    let csv = reviewed.finish().table().to_csv()

    Calls raise MergeError; use them in a raising function/test or handle errors. An unresolved plan cannot be exported. Accessors return defensive copies. report() includes affected source values and resolution evidence. See the design contract for three complete workflows.

    #Rules that prevent data loss

    • Values are strings: 007, spaces, empty strings and quoted newlines retain their meaning. Missing data is never invented as an empty string.
    • Headers are exact, nonempty and unique. Keys have nonempty components and are unique per input. Removing a key column fails. Key/header changes are delete/add unless retained rows/columns have explicit lineage declarations; renames are not inferred.
    • Row/column deletion competing with any change on that structure requires keep/drop. Even removing a row on the opposite branch counts as a column change, and conversely; this conservative rule can request review for two independent deletions.
    • Different concurrent inserts with the same key require selection of a whole row. Structural decisions precede cell decisions. Unknown, duplicate, premature, invalid or absent-source choices fail.
    • Output uses base column order plus sorted new headers, and defaults to lexicographically sorted compound row keys. Optional source ordering preserves a chosen input's surviving row sequence. Row order is not identity. CSV formatting is normalized to LF/necessary quoting; semantic cells round-trip.
    • Strict JSON rejects duplicate object keys (including escaped aliases) and unknown fields. Input limitations produce errors, never partial output.

    CSV and quoted TSV support UTF-8, optional leading BOM, comma/tab delimiters respectively, LF/CRLF, doubled quotes and quoted line breaks. Limits: 100,000 rows, 512 columns, 2,000,000 cells and 16,000,000 UTF-16 code units of semantic table data/JSON. Delimited syntax is capped at 40,000,000 units to accommodate doubled quotes for every valid table; CLI reads are capped at 64,000,000 bytes. A BOM stored in the first header is quoted to preserve it as data. Union/output limits are also enforced. No streaming, XLSX, fuzzy keys, type inference or automatic rename detection.

    #Validation and platform scope

    The pure library supports Wasm, Wasm-GC, JavaScript and native. The async file CLI supports Wasm and Linux/macOS native hosts; Windows native CLI is excluded because of the current async dependency's MSVC requirement. A C compiler is required for native execution. CI exercises Linux, Windows and macOS; test outcomes are recorded in validation, separately from planned compatibility.

    moon run scripts/verify.mbtx

    On Windows, if your compiler is absent from PATH, supply its directory as the script's only argument. This runs format/interface generation, strict checks, all four backend tests and actual Wasm/native CLI demos. CI additionally refuses formatting/interface drift. Tests include 100 CSV round trips, 64 scalar combinations, structural conflicts, limits, deterministic ordering, defensive copies and recovery after failed decisions.

    #Scope and roadmap

    The offline browser reviewer imports local CSV/TSV files, handles structural and cell decisions, saves/resumes input-bound review bundles, and exports complete tables/evidence/audits using the same MoonBit core. Build with moon run scripts/build-browser.mbtx and open output/browser/index.html in Chrome. No server or CDN is required. Browser-specific limits and actual UI verification are documented separately from library backend support.

    Scale measurements provide a reproducible native baseline, raw samples and a measured immutable-snapshot optimization. In the recorded 50,000 × 32 synthetic case, median process RSS fell about 24%; timing is mixed and no general performance advantage is claimed. Public array accessors retain defensive copies. moon run scripts/bench-smoke.mbtx checks workload correctness.

    This project's independent boundary is two-dimensional structural merge with complete-table invariants and explicit evidence, complementing CSV, DataFrame, diff and generic JSON merge libraries. Research records adjacent MoonBit APIs, demand, overlap and remaining uncertainty. No ecosystem uniqueness, algorithm originality or performance advantage is claimed.

    Implemented output ordering policies preserve a chosen source's surviving row sequence and append other rows deterministically. Use --row-order base|ours|theirs with merge/review/git-driver; offline review binds this choice. The default remains lexical. moon run scripts/order-demo.mbtx runs the real-data workflow.

    Explicit column lineage merges renamed columns with concurrent value edits, with collision checks and original-input-bound review. Use --columns FILE with merge/review/git-driver; moon run scripts/rename-demo.mbtx exercises the complete real-data workflow.

    Explicit row lineage merges identifier migrations with concurrent non-key edits. Use --rows FILE with merge/review/git-driver; moon run scripts/identity-demo.mbtx exercises output-key conflict review, stale rejection and driver recovery. Column rename/deletion is implemented through the additive evolution API. Row-ID migration versus record deletion, streaming and other formats remain planned and depend on demand.

    MergeError

    pub(all) suberror MergeError {
    InvalidTable(String)
    InvalidKey(String)
    InvalidCsv(record~ : Int, column~ : Int, reason~ : String)
    LimitExceeded(String)
    InvalidDecision(String)
    Unresolved(Int)
    InvalidReview(String)
    InvalidOrder(String)
    InvalidLineage(String)
    } derive(
    Debug
    )

    Diagnostics include locations and corrective actions, not partial output.

    MergeError::diagnostic

    fn MergeError::diagnostic(self : MergeError) -> String

    CellValue

    pub(all) struct CellValue {
    key : RowKey
    column : String
    value : String
    } derive(Eq,
    Debug
    )

    A source value, including its row identity and column name.

    CellValue::equal

    fn CellValue::equal(CellValue, CellValue) -> Bool

    CellValue::not_equal

    fn CellValue::not_equal(x : CellValue, y : CellValue) -> Bool

    Choice

    pub(all) enum Choice {
    Keep
    Drop
    Base
    Ours
    Theirs
    Value(String)
    } derive(Eq,
    Debug
    )

    Keep/Drop resolve deletion conflicts; Ours/Theirs select inserted rows. Base/Ours/Theirs/Value resolve cells. Unsupported combinations are errors.

    Choice::equal

    fn Choice::equal(Choice, Choice) -> Bool

    Choice::not_equal

    fn Choice::not_equal(x : Choice, y : Choice) -> Bool

    Choice::to_repr

    ColumnEvolution

    pub(all) struct ColumnEvolution {
    base : String
    ours : String?
    theirs : String?
    output : String
    } derive(Eq,
    Debug
    )

    An existing ancestor column, optionally absent on either branch. None means declared absence, never an empty cell. A one-sided absence requires Keep/Drop. Existing ColumnLineage remains the retained-in-all-three contract.

    ColumnEvolution::equal

    ColumnEvolution::not_equal

    fn ColumnEvolution::not_equal(x : ColumnEvolution, y : ColumnEvolution) -> Bool

    ColumnLineage

    pub(all) struct ColumnLineage {
    base : String
    ours : String
    theirs : String
    output : String
    } derive(Eq,
    Debug
    )

    Declares the same retained logical column across three source schemas. All source names must exist. Output names are used for keys and decisions. This is explicit intent, never a guess based on similar headers or values.

    ColumnLineage::equal

    ColumnLineage::not_equal

    fn ColumnLineage::not_equal(x : ColumnLineage, y : ColumnLineage) -> Bool

    Conflict

    pub(all) struct Conflict {
    target : Target
    kind : ConflictKind
    base : Array[CellValue]
    ours : Array[CellValue]
    theirs : Array[CellValue]
    } derive(Eq,
    Debug
    )

    Empty source arrays mean absence; an empty string is an actual stored value.

    Conflict::equal

    fn Conflict::equal(Conflict, Conflict) -> Bool

    Conflict::not_equal

    fn Conflict::not_equal(x : Conflict, y : Conflict) -> Bool

    Conflict::to_repr

    ConflictKind

    pub(all) enum ConflictKind {
    ColumnDeleteEdit
    RowDeleteEdit
    ConcurrentInsert
    CellEdit
    MissingCell
    } derive(Eq,
    Debug
    )

    ConflictKind::equal

    ConflictKind::not_equal

    fn ConflictKind::not_equal(x : ConflictKind, y : ConflictKind) -> Bool

    Decision

    pub(all) struct Decision {
    target : Target
    choice : Choice
    } derive(Eq,
    Debug
    )

    Decision::equal

    fn Decision::equal(Decision, Decision) -> Bool

    Decision::not_equal

    fn Decision::not_equal(x : Decision, y : Decision) -> Bool

    Decision::to_repr

    MergePlan

    pub struct MergePlan {
    // private fields
    }

    Owns immutable input snapshots and cumulative decisions.

    MergePlan::apply_review_sheet

    fn MergePlan::apply_review_sheet(self : MergePlan, sheet : Table) -> MergePlan raise MergeError

    Atomically applies selected current conflicts from an input-bound sheet. Blank choice means pending; choice=value with empty value is explicit empty. Reordering conflict rows is safe; deletion/duplication/evidence edits fail.

    MergePlan::apply_review_sheet_for

    fn MergePlan::apply_review_sheet_for(self : MergePlan, targets : Array[Target], sheet : Table) -> MergePlan raise MergeError

    Requires the independently supplied assignment as well as the edited sheet. Omitted assigned rows fail; conflicts outside the assignment stay pending.

    MergePlan::column_evolution

    fn MergePlan::column_evolution(self : MergePlan) -> Array[ColumnEvolution]

    MergePlan::column_lineage

    fn MergePlan::column_lineage(self : MergePlan) -> Array[ColumnLineage]

    MergePlan::conflicts

    fn MergePlan::conflicts(self : MergePlan) -> Array[Conflict]

    MergePlan::decisions

    fn MergePlan::decisions(self : MergePlan) -> Array[Decision]

    MergePlan::finish

    fn MergePlan::finish(self : MergePlan) -> MergedTable raise MergeError

    Refuses to materialize an incomplete table.

    MergePlan::report

    fn MergePlan::report(self : MergePlan) -> Json

    JSON audit report; incomplete plans contain no candidate CSV output.

    MergePlan::review_sheet

    fn MergePlan::review_sheet(self : MergePlan) -> Table raise MergeError

    Editable table of all current conflicts. Only choice/value may change. The first binding record owns full canonical input snapshots and policies.

    MergePlan::review_sheet_for

    fn MergePlan::review_sheet_for(self : MergePlan, targets : Array[Target]) -> Table raise MergeError

    Exports only explicitly assigned current conflicts, binding all original data. Scope order is canonicalized; empty scope makes an explicit no-op assignment.

    MergePlan::revise

    fn MergePlan::revise(self : MergePlan, edits : Array[ReviewEdit]) -> MergePlan raise MergeError

    Replaces/revokes choices without modifying this plan. Every retained choice must remain applicable; revoke dependencies explicitly in the same batch.

    MergePlan::row_lineage

    fn MergePlan::row_lineage(self : MergePlan) -> Array[RowLineage]

    MergePlan::row_order

    fn MergePlan::row_order(self : MergePlan) -> RowOrder

    MergedTable

    pub struct MergedTable {
    // private fields
    }

    Available only after all conflicts have been resolved.

    MergedTable::resolutions

    fn MergedTable::resolutions(self : MergedTable) -> Array[Resolution]

    MergedTable::table

    fn MergedTable::table(self : MergedTable) -> Table

    Resolution

    pub(all) struct Resolution {
    conflict : Conflict
    choice : Choice
    } derive(Eq,
    Debug
    )

    Evidence for an explicit resolution, including original source values.

    Resolution::equal

    fn Resolution::equal(Resolution, Resolution) -> Bool

    Resolution::not_equal

    fn Resolution::not_equal(x : Resolution, y : Resolution) -> Bool

    ReviewBundle

    pub struct ReviewBundle {
    // private fields
    }

    An offline review owns all three exact parsed snapshots, keys and decisions. It is a revision guard, not a signature or trust boundary.

    ReviewBundle::changes_report

    fn ReviewBundle::changes_report(self : ReviewBundle, previous : ReviewBundle) -> Json raise MergeError

    Emits full before/after evidence or refuses an oversized report.

    ReviewBundle::changes_since

    fn ReviewBundle::changes_since(self : ReviewBundle, previous : ReviewBundle) -> Array[ReviewChange] raise MergeError

    Compares validated reviews of exactly the same inputs and policies. Added, changed and revoked decisions are sorted by canonical target. This is a comparison, not an authenticated history or a replayable patch.

    ReviewBundle::combine

    fn ReviewBundle::combine(self : ReviewBundle, reviews : Array[ReviewBundle]) -> ReviewBundle raise MergeError

    Combines independently reviewed copies of this baseline without overwrites. Baseline choices must be retained exactly; agreeing additions coalesce. Conflicting choices or invalid structural dependencies reject the whole batch.

    ReviewBundle::from_json

    fn ReviewBundle::from_json(source : String) -> ReviewBundle raise MergeError

    Strict, versioned import. Revalidates snapshots, keys and cumulative decisions; accepts incomplete review but never an unsupported field/version.

    ReviewBundle::from_plan

    fn ReviewBundle::from_plan(plan : MergePlan) -> ReviewBundle

    ReviewBundle::plan

    fn ReviewBundle::plan(self : ReviewBundle) -> MergePlan raise MergeError

    Recomputes every conflict and checks every supplied decision.

    ReviewBundle::replay

    fn ReviewBundle::replay(self : ReviewBundle, base : Table, ours : Table, theirs : Table, key~ : Array[String]) -> MergePlan raise MergeError

    Only replays against the snapshots actually reviewed. CSV whitespace/quote style/BOM differences are tolerated after parsing; row/header order is bound.

    ReviewBundle::resolve

    fn ReviewBundle::resolve(self : ReviewBundle, decisions : Array[Decision]) -> ReviewBundle raise MergeError

    Adds decisions without modifying the existing bundle. Structure can be resolved and serialized before newly exposed cell conflicts are reviewed.

    ReviewBundle::revise

    fn ReviewBundle::revise(self : ReviewBundle, edits : Array[ReviewEdit]) -> ReviewBundle raise MergeError

    ReviewBundle::to_json

    fn ReviewBundle::to_json(self : ReviewBundle) -> Json raise MergeError

    Refuses an oversized export rather than emitting a bundle it cannot read.

    ReviewChange

    pub(all) struct ReviewChange {
    target : Target
    before : Resolution?
    after : Resolution?
    } derive(Eq,
    Debug
    )

    A change in recorded resolution, including complete source evidence. None means no recorded decision, never a stored empty cell value.

    ReviewChange::equal

    ReviewChange::not_equal

    fn ReviewChange::not_equal(x : ReviewChange, y : ReviewChange) -> Bool

    ReviewEdit

    pub(all) enum ReviewEdit {
    Set(Decision)
    Revoke(Target)
    } derive(Eq,
    Debug
    )

    An explicit atomic update to current review decisions.

    ReviewEdit::equal

    fn ReviewEdit::equal(ReviewEdit, ReviewEdit) -> Bool

    ReviewEdit::not_equal

    fn ReviewEdit::not_equal(x : ReviewEdit, y : ReviewEdit) -> Bool

    RowKey

    pub struct RowKey {
    // private fields
    } derive(Eq,
    Debug
    )

    A compound row identity. Components are never joined with a user delimiter.

    RowKey::RowKey

    fn RowKey::RowKey(parts : Array[String]) -> RowKey

    RowKey::components

    fn RowKey::components(self : RowKey) -> Array[String]

    RowKey::equal

    fn RowKey::equal(RowKey, RowKey) -> Bool

    RowKey::not_equal

    fn RowKey::not_equal(x : RowKey, y : RowKey) -> Bool

    RowKey::to_repr

    RowLineage

    pub(all) struct RowLineage {
    base : RowKey
    ours : RowKey
    theirs : RowKey
    output : RowKey
    } derive(Eq,
    Debug
    )

    One retained record's identities before, on each branch, and in the output. Components follow the merge key's ordered columns after column alignment.

    RowLineage::equal

    fn RowLineage::equal(RowLineage, RowLineage) -> Bool

    RowLineage::not_equal

    fn RowLineage::not_equal(x : RowLineage, y : RowLineage) -> Bool

    RowOrder

    pub(all) enum RowOrder {
    Lexical
    BaseOrder
    OursOrder
    TheirsOrder
    } derive(Eq,
    Debug
    )

    A source order is an explicit output preference, not a merge of row moves. Surviving rows missing from that source are appended in lexical key order.

    RowOrder::equal

    fn RowOrder::equal(RowOrder, RowOrder) -> Bool

    RowOrder::name

    fn RowOrder::name(self : RowOrder) -> String

    RowOrder::not_equal

    fn RowOrder::not_equal(x : RowOrder, y : RowOrder) -> Bool

    RowOrder::parse

    fn RowOrder::parse(value : String) -> RowOrder raise MergeError

    RowOrder::to_repr

    Table

    pub struct Table {
    // private fields
    } derive(Eq,
    Debug
    )

    A validated rectangular table. Constructors and accessors copy their arrays.

    Table::equal

    fn Table::equal(Table, Table) -> Bool

    Table::from_rows

    fn Table::from_rows(headers : Array[String], rows : Array[Array[String]]) -> Table raise MergeError

    Creates a defensive snapshot; values are exact strings, not inferred scalars.

    Table::headers

    fn Table::headers(self : Table) -> Array[String]

    Table::not_equal

    fn Table::not_equal(x : Table, y : Table) -> Bool

    Table::row_count

    fn Table::row_count(self : Table) -> Int

    Table::rows

    fn Table::rows(self : Table) -> Array[Array[String]]

    Table::to_csv

    fn Table::to_csv(self : Table) -> String

    Deterministic CSV. Cell strings round-trip; original formatting does not.

    Table::to_repr

    Table::to_tsv

    fn Table::to_tsv(self : Table) -> String

    Target

    pub(all) enum Target {
    Column(String)
    Row(RowKey)
    Cell(RowKey, String)
    } derive(Eq,
    Debug
    )

    Conflict coordinates are independent of input row and column ordering.

    Target::equal

    fn Target::equal(Target, Target) -> Bool

    Target::not_equal

    fn Target::not_equal(x : Target, y : Target) -> Bool

    Target::to_json

    fn Target::to_json(self : Target) -> Json

    Stable review coordinates. Key components remain an array.

    Target::to_repr

    UniqueConflict

    pub(all) struct UniqueConflict {
    rule : String
    columns : Array[String]
    values : Array[String]
    rows : Array[RowKey]
    base : Array[CellValue]
    ours : Array[CellValue]
    theirs : Array[CellValue]
    merged : Array[CellValue]
    } derive(Eq,
    Debug
    )

    Full affected records in output coordinates; absence is an empty source array.

    UniqueConflict::equal

    UniqueConflict::not_equal

    fn UniqueConflict::not_equal(x : UniqueConflict, y : UniqueConflict) -> Bool

    UniqueReview

    pub struct UniqueReview {
    // private fields
    }

    A separate policy-bound phase. Existing ordinary review formats are unchanged.

    UniqueReview::changes_report

    fn UniqueReview::changes_report(self : UniqueReview, previous : UniqueReview) -> Json raise MergeError

    Compares equal-input/equal-rule reviews with full before/after repair records.

    UniqueReview::conflicts

    fn UniqueReview::conflicts(self : UniqueReview) -> Array[UniqueConflict]

    Complete unique evidence, defensively copied; ordinary conflicts are separate.

    UniqueReview::finish

    fn UniqueReview::finish(self : UniqueReview) -> MergedTable raise MergeError

    Exports only after ordinary and uniqueness phases are complete.

    UniqueReview::from_json

    fn UniqueReview::from_json(source : String) -> UniqueReview raise MergeError

    Rejects duplicate/unknown fields and recomputes every phase; cached results are not trusted.

    UniqueReview::merge_conflicts

    fn UniqueReview::merge_conflicts(self : UniqueReview) -> Array[Conflict]

    Ordinary structural/cell conflicts must be resolved before uniqueness repair.

    UniqueReview::new

    fn UniqueReview::new(review : ReviewBundle, rules : Array[UniqueRule]) -> UniqueReview raise MergeError

    Validates named rules against all aligned sources, then enters merge/unique phases.

    UniqueReview::repair

    fn UniqueReview::repair(self : UniqueReview, decisions : Array[Decision]) -> UniqueReview raise MergeError

    Adds explicit Row/Drop or implicated non-key Cell/Value repairs atomically. New duplicate pairs, duplicate targets and unrelated edits are rejected.

    UniqueReview::repairs

    fn UniqueReview::repairs(self : UniqueReview) -> Array[Decision]

    Recorded unique decisions in canonical target order, separate from ordinary choices.

    UniqueReview::replay

    fn UniqueReview::replay(self : UniqueReview, base : Table, ours : Table, theirs : Table, key~ : Array[String]) -> UniqueReview raise MergeError

    Checks exact original parsed snapshots/key order and retains bound unique rules.

    UniqueReview::report

    fn UniqueReview::report(self : UniqueReview) -> Json raise MergeError

    Full ordinary/unique evidence. Oversized reports fail without truncation.

    UniqueReview::revise

    fn UniqueReview::revise(self : UniqueReview, edits : Array[ReviewEdit]) -> UniqueReview raise MergeError

    Revises ordinary choices. Revoke unique repairs first if their candidate changes.

    UniqueReview::revise_repairs

    fn UniqueReview::revise_repairs(self : UniqueReview, edits : Array[ReviewEdit]) -> UniqueReview raise MergeError

    Sets/replaces/revokes repairs; every retained repair is revalidated.

    UniqueReview::rules

    Canonical rule order; column accessors return defensive copies.

    UniqueReview::to_json

    fn UniqueReview::to_json(self : UniqueReview) -> Json raise MergeError

    Versioned envelope retaining original inputs, ordinary policies and repairs.

    UniqueRule

    pub struct UniqueRule {
    // private fields
    } derive(Eq,
    Debug
    )

    A named exact-string unique tuple, in output column coordinates.

    UniqueRule::UniqueRule

    fn UniqueRule::UniqueRule(name : String, columns : Array[String]) -> UniqueRule

    UniqueRule::columns

    fn UniqueRule::columns(self : UniqueRule) -> Array[String]

    UniqueRule::equal

    fn UniqueRule::equal(UniqueRule, UniqueRule) -> Bool

    UniqueRule::name

    fn UniqueRule::name(self : UniqueRule) -> String

    UniqueRule::not_equal

    fn UniqueRule::not_equal(x : UniqueRule, y : UniqueRule) -> Bool

    column_evolution_json

    fn column_evolution_json(items : Array[ColumnEvolution]) -> Json raise MergeError

    column_lineage_json

    fn column_lineage_json(lineage : Array[ColumnLineage]) -> Json raise MergeError

    Refuses invalid Unicode/oversized export; every emitted declaration array can be parsed back without losing text. Merge checks schema semantics.

    decisions_json

    fn decisions_json(decisions : Array[Decision]) -> Json

    merge

    fn merge(base : Table, ours : Table, theirs : Table, key~ : Array[String], row_order? : RowOrder, column_lineage? : Array[ColumnLineage], row_lineage? : Array[RowLineage], column_evolution? : Array[ColumnEvolution]) -> MergePlan raise MergeError

    Starts a structural merge. Input arrays cannot subsequently alter the plan.

    parse_column_evolution

    fn parse_column_evolution(source : String) -> Array[ColumnEvolution] raise MergeError

    Strict JSON: base/output are nonempty strings, ours/theirs are strings or null. Merge checks presence, one-to-one ownership and complete-schema collisions.

    parse_column_lineage

    fn parse_column_lineage(source : String) -> Array[ColumnLineage] raise MergeError

    Rejects unknown fields/types. Source existence and collisions are checked against all three tables by merge, before any values can be exported.

    parse_csv

    fn parse_csv(source : String) -> Table raise MergeError

    Strict comma-delimited CSV, with LF/CRLF and doubled quotes. A leading BOM is accepted; no spaces, values or unknown columns are discarded.

    parse_decisions

    fn parse_decisions(source : String) -> Array[Decision] raise MergeError

    Unknown fields and non-string values are rejected instead of ignored.

    parse_review_edits

    fn parse_review_edits(source : String) -> Array[ReviewEdit] raise MergeError

    parse_review_targets

    fn parse_review_targets(source : String) -> Array[Target] raise MergeError

    Strict coordinate list for CLI assignment, with no wildcard/inferred matches.

    parse_row_lineage

    fn parse_row_lineage(source : String) -> Array[RowLineage] raise MergeError

    parse_tsv

    fn parse_tsv(source : String) -> Table raise MergeError

    Quoted tab-delimited text; tabs/newlines use doubled-quote CSV rules.

    parse_unique_rules

    fn parse_unique_rules(source : String) -> Array[UniqueRule] raise MergeError

    resolve

    fn resolve(plan : MergePlan, decisions : Array[Decision]) -> MergePlan raise MergeError

    Adds decisions and replays from the owned snapshots; errors leave the old plan usable.

    review_edits_json

    fn review_edits_json(edits : Array[ReviewEdit]) -> Json raise MergeError

    Strict export: refuses invalid Unicode and oversized edit documents.

    row_lineage_json

    fn row_lineage_json(lineage : Array[RowLineage]) -> Json raise MergeError

    Bounds/Unicode checks prevent producing a document that import cannot read.

    unique_rules_json

    fn unique_rules_json(rules : Array[UniqueRule]) -> Json raise MergeError