Skip to content
RoutineOS
Esc
↑↓navigate↵open⌘Jpreview
On this page

Merges

How records from several producers fold together under a type's merge policy, and how people merge two records by hand.

What it is

Merging decides when two findings are the same record. There are two mechanisms:

  • Merge policy, per type: whether different producers that find the same key share one record (merge) or keep their own (separate).
  • Hand merge, per record: a person says this record is really that one.

Why it exists

Two routines on one Desk can both find Northwind Goods. Most of the time people want one company with both routines’ findings. Sometimes they want each routine’s view kept apart. And identity rules are not perfect: a company seen once by domain and once by a slightly different name needs a person to say “same thing”.

How it works

Merge policy

Each type declares merge in the definition (merge by default). People can override it per type in settings/workspace under mergePolicy, which seed fills from the definition:

"mergePolicy": {"company": "merge", "role": "merge"}
  • merge: the id is <type>--<slug(key)>. Every producer that finds the key writes its own bySource slice of the same record.
  • separate: the id is <type>--<slug(producerId--key)>. Each producer gets its own record.

Each run records the policy it used as mergePolicy on the run document. Flipping the policy applies to new ids only; existing documents are never re-keyed.

Hand merge

On a record page, a person opens “Merge into another ”, searches, and picks the target. The Desk writes merges/<entityId> {into: <targetId>, at, by} and says: “A hand merge is recorded now. Applies from the next run, which folds the two records together.”

From then on, the write contract resolves the merged id to its target whenever a run sees it, so new sightings land on the target. Merges can chain (A into B, B into C); a cycle fails the plan with hand merge cycle: A -> B -> A. Unmerge deletes merges/<entityId>.

Since 0.5.0: a side-by-side merge on the record page (pick two records, choose each field’s value, confirm, undo) and bulk Merge for exactly two selected records. It still writes merges/<entityId>.

Worked example

Hiring Signals and a second producer, lead-signals, share a Desk. Both find northwind.example. With company: merge, there is one record, company--northwind-example, with bySource slices for both producers and sources: ["hiring-signals", "lead-signals"].

A third run records a company as “Northwind Goods Pty Ltd” with no domain, so it keys by name as company--northwind-goods-pty-ltd. A person merges it into company--northwind-example. The next time any run sees that name, the sighting goes to company--northwind-example.

Reference

Item Path or field Writer
Type default definition.json types.<type>.merge Builder
Workspace override settings/workspace values.mergePolicy.<type> People
Policy used by a run runs/<runId> mergePolicy Producers
Hand merge merges/<entityId> {into, at, by} People

When producers share a type, compile requires the same merge default in each definition.

Common pitfalls

  • Flipping to separate and expecting a split. Old records stay merged. Only new ids follow the new policy.
  • Merging across types. The Desk only offers records of the same type.
  • Expecting the merge to show at once. The hand merge is recorded immediately; the fold happens on the next run.

Was this page helpful?