Watchline methodology

The Owner-Identity Edge Layer

How landlord records are wired together — six edge types, two families
Reference Every edge Watchline draws between landlords_with_connections nodes — how each is built, its precision guard, and which graph layer consumes it. The mechanism beneath the case studies.

Two Families: Operational Nexus vs. Owner Identity

Watchline connects landlord nodes with six edge types in two families. The operational-nexus edges (inherited from Who Owns What — shared name, shared office) answer "who operates through here." The owner-identity edges (Watchline's four) answer "who actually owns this." They feed different graph layers — which is exactly why the same graph can over-merge on a shared back-office (the Portfolio) yet resolve owners correctly (ResolvedEntityV2). Every edge is an undirected pair over landlords_with_connections nodeids (Actor ACT-LL-<nodeid>).

◆ At a glance — the six edges

Relationship · methodFamilySignalBuilt fromPrecision guard
CONNECTED_BY_NAMEnexusshared owner nameWoW name_match_infozip/street gate (WoW)
CONNECTED_BY_ADDRESSnexusshared business officeWoW bizaddr_match_infoaggregator degree cap
CONNECTED_BY_SPLINK · splink-fellegi-sunteridentitysame person (probabilistic)Splink model on HPD contactsaggregator-address mask · name-anchored blocking
CONNECTED_BY_SPLINK · registered-llcidentitysame DOF owner entityPLUTO ownernameentity markers · degree cap 100
CONNECTED_BY_SPLINK · curated-same-owneridentityhand-verified same ownercurated tablehuman evidence required
CONNECTED_BY_DEED · acris-deedidentityco-conveyed on one deedACRIS deedslatest-deed · successor · hub cap

All four owner-identity edges emit weight-100 clique/star groups over the co-owned nodes; the two nexus edges carry Who Owns What's own match weight (name weighted above address).

I. Operational-nexus edges (legacy / WoW)

Materialized by pipeline.py straight from Who Owns What's precomputed match columns on landlords_with_connections. Deterministic — dropped/rebuilt cheaply via MERGE. These say two records operate through a shared name or office; that is not proof of shared ownership (a shared back-office is a nexus, not an owner).

CONNECTED_BY_NAMEoperational nexus

Source
WoW's name_match_info JSON on each landlords_with_connections node — its own head-officer name matches.
How built
pipeline.py unrolls each node's name-match list into undirected (a)-[:CONNECTED_BY_NAME]-(b) pairs.
Guard
WoW's rule gates a name match on same zip + street similarity, so it doesn't fuse common names city-wide.
Weight
WoW's match weight — weighted above address (a shared name is stronger evidence of common control than a shared office).
Consumed by
the Portfolio layer (operational nexus).

CONNECTED_BY_ADDRESSoperational nexus

Source
WoW's bizaddr_match_info — landlords sharing a Geosupport-standardized business address.
How built
Same unroll as name, into (a)-[:CONNECTED_BY_ADDRESS]-(b) pairs.
Guard
_aggregator_addresses drops any office shared by more than MAX_ADDR_DEGREE landlords (a registered-agent / mail-drop hub) before building edges.
Weight
WoW's match weight — below name.
Consumed by
the Portfolio layer. This is the edge that over-merges when a real shared office slips under the degree cap — the Miller case.

II. Owner-identity edges (Watchline)

Watchline's four owner-identity signals. Each is Neo4j-free — its module returns a [src, dst, weight] frame that pipeline.py::step_splink loads; each stamps a distinct method for provenance. Three ride the CONNECTED_BY_SPLINK relationship (with a separate CONNECTED_BY_DEED for the deed signal).

CONNECTED_BY_SPLINKsplink-fellegi-sunterowner identity

Source
splink_bridge.py runs the Splink model (defined in splink_source.py::_settings) full-population; splink-fellegi-sunter is stamped in pipeline.py.
Features
5 comparisons: last_name & first_name (Jaro-Winkler + term-frequency), biz_house (Levenshtein), biz_street_norm (Jaro-Winkler, suffix-normalized), biz_zip (exact + TF).
Guard
Aggregator-address mask — address fields are blanked for any office shared by >25 landlords, so a shared back-office contributes no match. Name-anchored blocking — a pair is only scored if it shares last_name + first initial (the address-only blocking rule was removed as a precision hole).
Weight
100 (clique/star over each cluster's nodes).
Consumed by
Portfolio · OwnerGroup · ResolvedEntityV2.

CONNECTED_BY_SPLINKregistered-llcowner identity

Source
llc_edges.py — the DOF owner-of-record (pluto_latest.ownername). Fills the gap WoW misses: it links on the head officer (a person), never the registered owning entity.
How built
Every entity owning ≥2 buildings emits a clique between those buildings' landlord nodes. Same legal-entity name == same owner, definitionally — precision-1 by construction.
Guard
Must match an entity marker (LLC/CORP/REALTY…, whole-word); institutional owners (HDFC/NYCHA/City) excluded; degree cap of 100 lots (above = placeholder/nominee).
Weight
100.
Consumed by
Portfolio · OwnerGroup. Not read by ResolvedEntityV2 — a name-only LLC match links co-officers (distinct people) of one entity, so it isn't a same-party signal (only an id+jurisdiction registered-llc-id would be). The edges still exist and drive the Portfolio and OwnerGroup layers; ResolvedEntityV2's identity clustering simply ignores them.

CONNECTED_BY_SPLINKcurated-same-ownerowner identity

Source
curated_owners.py — a hand-maintained table of verified "these nodes are the same real owner," matched by normalized name variants (+ optional BBL disambiguator).
Why
The residual the model cannot close: an operator whose records share only an exact, rare full name — no shared corp, no shared address — that the model was trained to distrust. Force-emits a clique so WCC merges it. (Croman's ROCKSOLID remnant → CENTENNIAL main.)
Guard
Human responsibility. Add only with cited evidence (ownership docs, shared principal); edges only ADD, so it can merge fragments but never split. Seeds stay within one surname.
Weight
100.
Consumed by
Portfolio · OwnerGroup · ResolvedEntityV2 (top precedence — a deterministic, human-verified signal).

CONNECTED_BY_DEEDacris-deedowner identity

Source
deed_edges.py — ACRIS deeds (real_property_master/legals/parties). Buildings conveyed on one deed share a grantee → same owner, regardless of LLC names. The only signal that pierces the shell game.
How built
Two sources unioned per deed: (A) held-since — buildings whose latest deed is a shared multi-parcel deed (staleness guard); (B) linked-successor — joint purchases the grantee later re-deeded into per-building shell LLCs. One clique per deed.
Guard
Latest-deed rule (no stale merges); successor must be a shell (≤3 buildings, else an arms-length sale); 2–25 parcels (mega-deeds excluded); deed-hub cap drops serial co-investors (>20 deeds).
Weight
100.
Consumed by
OwnerGroup only — never the Portfolio (a deed is ownership evidence, not an operational nexus), and not read by ResolvedEntityV2 (deed is unioned into identity only in the OwnerGroup layer). The CONNECTED_BY_DEED edges remain in the graph; ResolvedEntityV2's identity clustering ignores them.

III. Which layer consumes which

Three layers read these edges, and the differences are the whole architecture. The Portfolio (GDS WCC+Louvain, algorithms.py) groups by operational nexus. The two ownership layers group by owner identity — OwnerGroup (current-materialized) and ResolvedEntityV2 (the newer, method-selective resolution).

Edge · methodPortfolio
name + addr + splink
OwnerGroup
splink ∪ deed
ResolvedEntityV2
method precedence
CONNECTED_BY_NAME✓——
CONNECTED_BY_ADDRESS✓——
SPLINK · splink-fellegi-sunter✓✓✓
SPLINK · curated-same-owner✓✓✓
SPLINK · registered-llc✓✓✗ name-only excluded
DEED · acris-deed✗✓✗ deed excluded
Why the same graph gives two answers The Portfolio consumes CONNECTED_BY_ADDRESS, so a shared office fuses its members (the Miller over-merge). The owner-identity layers never read the raw address/name edges — they group only on identity signals — so the same shared office does not merge those owners. Reading owner identity from the resolved-entity layer, not the Portfolio, is the correction.
ResolvedEntityV2 is method-selective It applies a precedence — curated-same-owner > registered-llc-id (not yet in the graph) > splink-fellegi-sunter — and deliberately excludes both name-only registered-llc (it links co-officers, i.e. distinct people, of one LLC) and CONNECTED_BY_DEED (unioned into identity only in the older OwnerGroup layer). Those edges stay in the graph and drive the Portfolio/OwnerGroup layers — ResolvedEntityV2's identity clustering simply doesn't read them. So the same CONNECTED_BY_SPLINK relationship is read differently depending on its method.
The deed distinction — the two identity layers are not the same edge set Both OwnerGroup and ResolvedEntityV2 are owner-identity layers, but they do not cluster over the same edges. They share only splink-fellegi-sunter + curated-same-owner. OwnerGroup additionally unions CONNECTED_BY_DEED (and the name-only registered-llc subtype); ResolvedEntityV2 reads neither. So the accurate shorthand is OwnerGroup = SPLINK (all methods) ∪ DEED versus ResolvedEntityV2 = curated-same-owner + splink-fellegi-sunter only. The ∪ DEED clause belongs to OwnerGroup alone — do not attribute it to ResolvedEntityV2.

When the two layers happen to agree — e.g. Miller's 7 owners — it is because the deciding signal there is the exclusion of CONNECTED_BY_ADDRESS, which both layers share, not because they read identical edges. Deed- and LLC-heavy portfolios are exactly where they diverge: OwnerGroup merges on the deed/LLC evidence, ResolvedEntityV2 does not — which is why ResolvedEntityV2 currently shows lower recall on those cases until an id-based registered-llc-id edge is built.

IV. Shared mechanics

  • Node identity. Every edge is an undirected pair over landlords_with_connections nodeids, materialized between Actor {actor_id: "ACT-LL-<nodeid>"} nodes.
  • Clique or star. Each owner-identity group becomes a full clique (all pairs) up to STAR_ABOVE = 150 members, then a hub-and-spoke star to bound the edge count.
  • Weight 100. All four owner-identity signals use SPLINK_WEIGHT = 100; the two nexus edges carry WoW's own (lower) match weights.
  • Drop-and-rebuild. CONNECTED_BY_SPLINK is fully dropped and rebuilt every run (it derives from a stochastic resolution); the deterministic name/address edges are just re-MERGEd.
  • Provenance. The method property on each edge is what lets one relationship type carry several signals and lets downstream layers select among them.
This page describes the watchline/discovery/ingest/portfolio pipeline modules (splink_bridge, splink_source, llc_edges, curated_owners, deed_edges, pipeline, algorithms, owner_groups, resolved_entity). Guards/thresholds are current as of this writing and live in code, not here.