Conflict workflows & diagrams¶
Visual references for how TOVIO treats conflicts as data, not events.
Prose explanations live on Conflicts as data and Resolve conflicts — this page is the map.
How to read these
Rectangles are states or artifacts, rounded shapes are actions, and diamonds are decisions. A stored conflict is a success glyph (✓), not an error — the merge completed and the open question is durable data attached to the resulting change.
1. Conflict-as-data: the mental model¶
Git treats a conflict as a halt; TOVIO treats it as a typed object the repository holds for you.
flowchart LR
subgraph GIT[" Git model "]
G1["merge"] --> G2{"auto-merges?"}
G2 -- yes --> G3([✓ merged])
G2 -- no --> G4[["✗ HALT<br/>working tree filled<br/>with <<<<<<< markers"]]
end
subgraph TOVIO[" TOVIO model "]
V1["land / sync"] --> V2{"auto-merges?"}
V2 -- yes --> V3([✓ merged])
V2 -- no --> V4[["✓ merged<br/>+ conflict object stored<br/>(first-class data, keyed by path)"]]
V4 --> V5["Repo stays operational:<br/>commit, switch, sync, stack<br/>keep working"]
end
classDef halt fill:#fdd,stroke:#c33,color:#600;
classDef ok fill:#dfd,stroke:#3a3;
class G4 halt;
class V3,V4,V5 ok;
See Conflicts as data.
2. The conflict object — anatomy¶
A conflict is content-addressed data in the object graph. It carries the base, both sides, its kind, and a stable identity that resolutions can key against.
flowchart TB
C["Conflict object<br/>(content-addressed · one per path)"]
C --> K{{"kind"}}
K --> KC["content"]
K --> KDM["delete/modify"]
K --> KRE["rename/edit"]
K --> KRR["rename/rename"]
K --> KSEM["semantic (advisory)"]
C --> BASE["merge base<br/>(common ancestor region)"]
C --> OURS["ours side<br/>(local change)"]
C --> THEIRS["theirs side<br/>(incoming change)"]
C --> ID["stable identity<br/>(survives amend/rebase/absorb)"]
C --> STATUS{"unresolved · partially resolved<br/>· resolved · deferred"}
classDef obj fill:#eef,stroke:#557;
class C,BASE,OURS,THEIRS,ID obj;
3. Typed three-way merge — decision flow¶
tovio-core runs a typed three-way merge over the commit DAG. Auto-merge succeeds unless both sides
edit the same region incompatibly.
flowchart TD
S([Start merge]) --> BASE["Find merge base<br/>(common ancestor)"]
BASE --> DIFF["Compute (base → ours) and<br/>(base → theirs) region diffs"]
DIFF --> LOOP{{"For each region"}}
LOOP --> BOTH{"Both sides<br/>touch it?"}
BOTH -- no --> KEEP["Keep the side that changed<br/>(or base if neither)"]
BOTH -- yes --> SAME{"Same result?"}
SAME -- yes --> KEEP
SAME -- no --> KIND{"Classify by kind"}
KIND --> STORE[["Store typed conflict object<br/>on the resulting change"]]
KEEP --> NEXT
STORE --> NEXT([next region])
NEXT --> DONE([Merge completes ✓])
DONE --> OUT{"Any stored conflicts?"}
OUT -- yes --> OPEN["Surface in status &<br/>tovio conflicts"]
OUT -- no --> CLEAN([Clean merge])
4. Kinds of conflict → how tovio resolve dispatches¶
tovio resolve is type-directed. A rename/rename isn't a text merge; a delete/modify is a
keep-or-delete decision.
flowchart LR
R["tovio resolve <path>"] --> K{"conflict.kind"}
K -- content --> C1["per-region choice or hand-merge,<br/>or --ours / --theirs / --base"]
K -- delete/modify --> C2["--keep · --delete"]
K -- rename/edit --> C3["Usually auto-merged;<br/>surfaces only if edits overlap"]
K -- rename/rename --> C4["--rename-to <path>"]
K -- semantic --> C5["Advisory finding<br/>(warn; blocks only if<br/>protected-lane policy requires)"]
C1 --> APPLY["Apply resolution<br/>(structured Resolution)"]
C2 --> APPLY
C3 --> APPLY
C4 --> APPLY
C5 --> ADV["Recorded as warning"]
Prose: Resolve conflicts → kinds.
5. The state machine of a single conflict¶
stateDiagram-v2
[*] --> Unresolved: merge stored it
Unresolved --> PartiallyResolved: tovio resolve --region n<br/>(some regions settled)
PartiallyResolved --> Resolved: remaining regions settled
Unresolved --> Resolved: tovio resolve<br/>(--ours / --theirs / --keep /<br/>--delete / --rename-to / interactive)
Resolved --> Resolved: same identity seen elsewhere —<br/>rerere replay, no re-fight
Resolved --> [*]: change lands
These are the conflict object's own lifecycle states, and its status field spells them —
unresolved (the value a conflict carries with the field absent), partially_resolved, resolved, and
a fourth the diagram leaves out: deferred, a conflict deliberately left open, which no shipped command
records today. A refused tovio land is deliberately not a state here either: the land gate (§6) reads
the stored conflicts and rejects the land, leaving the conflict unresolved exactly as it was — a
verdict on the land, not a state the conflict enters. There is likewise no shipped verb for deliberately
reopening a resolution — one is specified, but it is not in the CLI today.
6. The one hard limit — the land gate¶
Conflicts are non-blocking within your stack. They are strictly blocking at the land boundary itself — the source tip, the target tip, and the merge base must all be conflict-free — and a protected lane runs conflict-freedom as an explicit gate on top of that, because shared code must stay buildable.
flowchart TD
L["tovio land my-lane --into main"] --> H["tovio change health chg:002"]
H --> Q{"unresolved conflicts?"}
Q -- no --> GATES["Other land gates<br/>(policy, plugins, review)"]
Q -- yes --> B[["✗ Land refused<br/>resolve them first"]]
B --> FIX["tovio resolve"]
FIX --> L
GATES --> OK([✓ Landed])
classDef stop fill:#fdd,stroke:#c33,color:#600;
class B stop;
Prose: Conflicts as data → strictly blocked.
7. Non-blocking scope vs. cross-developer isolation¶
A conflict on someone else's change does not silently spray markers into your working tree. sync
is gated; --isolate builds against the last-known-clean version.
sequenceDiagram
autonumber
actor Alice
actor Bob
actor Carol
participant Repo as Repository
Alice->>Repo: land chgA (forces conflict onto Bob's chg-002)
Repo-->>Alice: ✓ landed
Repo-->>Bob: chg-002 now stores a conflict on cgm.ts
Note over Bob: Bob's conflict — Bob resolves when ready.<br/>Alice is not blocked.
Carol->>Repo: tovio sync (depends on chg-002)
Repo-->>Carol: ⚠ chg-002 is conflicted.<br/>Not materializing markers into your tree.
Carol->>Repo: tovio sync --isolate
Repo-->>Carol: Built against last-known-clean chg-002
Prose: Conflicts as data → the boundary that matters.
8. Resolve once, applies everywhere (rerere-by-default)¶
Because a conflict has a stable identity, a resolution recorded once is replayed automatically wherever
the same conflict recurs — through amend, rebase, and the descendant cascade a land triggers.
flowchart LR
R1["Resolve cgm.ts<br/>in chg-002"] --> CACHE[("Resolution cache<br/>keyed by conflict identity")]
CACHE --> A1["chg-002 amended"]
CACHE --> A2["chg-002 rebased"]
CACHE --> A3["chg-002 re-derived by a<br/>land's descendant cascade"]
CACHE --> A4["Same conflict shows up<br/>on a peer's stack"]
A1 --> APPLIED["Resolution replayed — no re-fight"]
A2 --> APPLIED
A3 --> APPLIED
A4 --> APPLIED
APPLIED --> REOPEN{"Deliberately<br/>reopen?"}
REOPEN -- yes --> RE["deliberate reopen<br/>(specified · not yet shipped)"]
REOPEN -- no --> STAY([Stays resolved])
9. Region-level substrate & operation resolutions¶
Conflicts aren't just per-file blobs — the substrate resolves at the region level. Each conflicted
region takes its own choice — ours, theirs, the base, both sides, or a hand-merge — recorded as a
per-region resolution that replays on a later land (tovio resolve <path> --region <n> --ours, or the
interactive walk, which never writes a conflict marker into your file).
flowchart TB
F["File cgm.ts"] --> CO["One stored conflict object<br/>(file-granular, content-addressed)"]
CO --> RG["Regions derived on demand<br/>from base / ours / theirs"]
RG --> R1["Region A<br/>(non-overlapping edits)"]
RG --> R2["Region B<br/>(overlapping edits)"]
R1 --> M1(["Auto-merged"])
R2 --> OP{"Per-region choice"}
OP --> OP1["ours / theirs / base"]
OP --> OP2["take both"]
OP --> OP3["hand-merge inline<br/>(or --working-tree)"]
OP --> OP4["--ai proposal<br/>(applied like --ours)"]
OP1 --> APPLY["Apply through the<br/>normal Resolution path"]
OP2 --> APPLY
OP3 --> APPLY
OP4 --> APPLY
10. Semantic conflicts — advisory by default¶
A semantic conflict fires when a change breaks references another change relied on (e.g., a renamed symbol). It's a warning by default; policy can promote it to a required check.
flowchart TD
S["Semantic analyzer<br/>(symbol graph, refs, types)"] --> D{"broken reference?"}
D -- no --> OK([no conflict])
D -- yes --> SC["Store a conflict, kind=semantic"]
SC --> POL{"Protected lane policy<br/>requires semantic check?"}
POL -- no --> ADV[["Advisory warning<br/>(does not block)"]]
POL -- yes --> BLK[["Blocks land<br/>until resolved"]]
classDef warn fill:#ffe,stroke:#aa5;
classDef stop fill:#fdd,stroke:#c33,color:#600;
class ADV warn;
class BLK stop;
11. Divergence reconciles on the lane¶
Two replicas advancing the same lane to concurrent commits is not an error. On sync TOVIO reconciles the two on the lane into a two-parent commit — it never picks a pointer winner and strands the loser, and never asks you to force-push. Both diverged tips become parents of the new lane tip, so nothing lands off-lane.
flowchart LR
A[["Replica A<br/>advances main → X"]] --> SYNC{{"sync brings a<br/>divergent tip"}}
B[["Replica B<br/>advances main → Y"]] --> SYNC
SYNC --> BASE["merge-base(X, Y)<br/>+ typed 3-way merge<br/>(same engine as land)"]
BASE --> Q{"merge cleanly?"}
Q -- yes --> MC[["✓ two-parent merge commit<br/>parents = X and Y"]]
Q -- no --> CC[["✓ two-parent commit<br/>+ conflict object(s) stored<br/>parents = X and Y"]]
MC --> LANE["main advances to the<br/>reconciliation tip<br/>(both X and Y reachable)"]
CC --> LANE
classDef ok fill:#dfd,stroke:#3a3;
class MC,CC,LANE ok;
The CRDT / HLC-LWW ref register still runs underneath, but only as the mechanism that propagates that reconciliation pointer between replicas — it governs the lane pointer, not the content.
Prose: Offline & distributed.
12. tovio resolve --ai — the resolver-plugin seam¶
AI-assisted resolution asks a configured resolver to propose a Resolution and applies an accepted
proposal through the same path --ours/--theirs use. A configured ai.provider selects the built-in
LLM resolver at the CLI edge; with none configured, the request falls through to the sandboxed plugin
framework in resolver mode; if no resolver plugin is bound either, the conflict stays untouched and a
typed no-resolver error is returned.
flowchart TD
C["Open conflict on a path"] --> AI["tovio resolve --ai"]
AI --> PROV{"ai.provider<br/>configured?"}
PROV -- yes --> LLM["Built-in LLM resolver<br/>(decrypted sides; a protected path<br/>needs consent + a signed audit<br/>entry before any egress)"]
PROV -- no --> BOUND{"resolver plugin<br/>bound to<br/>resolve-requested?"}
BOUND -- no --> NORES[["No-resolver typed error<br/>(REQ-PLUGIN-079)<br/>conflict stays open"]]
BOUND -- yes --> SB["Sandboxed execution<br/>(WASM/WASI, deny-by-default)"]
LLM --> PROP{"Proposed Resolution?"}
SB --> PROP
PROP -- no --> KEEP["Leave conflict for human"]
PROP -- yes --> ACCEPT{"Actor accepts?"}
ACCEPT -- no --> KEEP
ACCEPT -- yes --> APPLY["Apply via normal<br/>Resolution path<br/>(REQ-PLUGIN-080)"]
APPLY --> DONE([✓ Resolved])
Full plugin flow: Plugins → Workflows · Plugins → concepts.
13. End-to-end: merge → work → resolve → land¶
The whole loop on one page.
sequenceDiagram
autonumber
actor Dev as Developer
participant CLI as tovio-cli
participant Core as tovio-core
participant Repo as Repository
Dev->>CLI: tovio land feature/cgm-sync --into my-lane
CLI->>Core: typed 3-way merge
Core->>Repo: store conflicts (dexcom.ts, types.ts)
Core-->>CLI: ✓ merged + N stored
CLI-->>Dev: success glyph + list
Dev->>CLI: keep working (commit, switch, sync, stack)
CLI->>Core: allowed — conflicts don't quarantine
Dev->>CLI: tovio conflicts
CLI-->>Dev: each open conflict, its kind, how to resolve it
Dev->>CLI: tovio resolve src/integrations/cgm/dexcom.ts --theirs
CLI->>Core: apply Resolution
Core->>Repo: mark resolved, cache for replay
Dev->>CLI: tovio land my-lane --into main
CLI->>Core: land gate: any open conflicts?
Core-->>CLI: none — proceed
CLI-->>Dev: ✓ landed
Where to go next¶
- Conflicts as data — the model in words.
- Resolve conflicts — the everyday CLI walkthrough.
- Offline & distributed — why divergence isn't an error.
- Plugins → Workflows — the resolver-plugin flow in the plugin frame.
Last reviewed September 9, 2026
Suggest an improvement to this page Not for security reports — see disclosure