Org Graph — plain-language explainer
Org Graph turns a flat HRIS export into a queryable, time-aware map of who reports to whom, who pays for whom, and which department rolls up to which leader — with operator corrections layered on top of the raw feed.
A People Analytics Toolbox component. Built to the portfolio Explainer Standard v1.0. Every claim below is grounded in the spoke's own code and contracts (src/spokes/org-graph/, contract 1.0.0); anything not yet built is marked (TBD).
1. What is it?
Org Graph materializes an HRIS org snapshot as a set of typed, time-bounded directed edges and then lets you ask structural questions of it: resolve a person's full manager chain as of a date, roll team-level numbers up to each leader, diff two snapshots to see who moved, and run quality checks for orphans, cycles, and conflicting parents.
The unit it works in is the edge — a from → to link stamped with a hierarchy kind (reports_to, costs_to, dept_parent, division_parent, geo_parent, or a custom_* kind), an effective-from/effective-to date range, a sourceAuthority (hris, operator-override, or inferred), and a confidence score. Because every edge is typed and dated, the same set of people can carry several overlapping hierarchies at once, and any query is answerable "as of" a chosen date.
Visual — Tier B (typographic shape). One person row becomes several typed edges:
worker-42 → mgr-7· kindreports_to· authorityhris· conf0.96· effective2025-01-01 → nullworker-42 → costcenter-9· kindcosts_to· authorityhris· conf1.0org_unit:dept:engineering → exec-1· kindcustom_elt_dominance· authorityinferred· conf0.84
2. What problem does it solve — and why is it different?
The pain it removes: an HRIS export is a flat table of people with a manager_id column (and usually a separate cost-center column, a department label, a supervisory-org field, and so on). The moment you need a chain — "everyone under this VP," "what did this org look like last quarter," "which director does this department actually roll up to" — the flat table fights you. People hand-build these answers in spreadsheets, once, and they go stale immediately.
The difference, stated as a shift:
- FROM one flat table with a single
manager_id, no history, and no way to encode "the org chart says X but we know the real answer is Y." - TO a multi-hierarchy graph where supervisory, financial, departmental, and custom reporting lines are first-class and independent; every edge is dated so any snapshot is reconstructable; and operator overrides outrank the raw feed wherever the HRIS is wrong.
How it differs from the obvious substitutes:
- vs. spreadsheet org charts — those collapse to one hierarchy, lose history the moment the next export lands, and can't tell you what changed. Org Graph keeps every snapshot and diffs them.
- vs. a generic BI tool — BI can pivot the flat table, but it has no notion of a typed temporal edge, no ancestor traversal that stops cleanly before cycles, and no precedence rule that lets a hand-corrected line beat the HRIS line.
- vs. trusting the HRIS field blindly — org reporting data is routinely stale or wrong; Org Graph's
sourceAuthorityprecedence and consistency report exist precisely because the raw feed can't be trusted uncritically.
Visual — Tier B (FROM→TO block). The shift above is the visual; a rendered comparison page is a follow-up (FU-A).
3. How does it work?
Inputs → method → outputs, concretely:
- Input —
graph/build: atenantId, asnapshotId, an array ofpersons(raw HRIS rows as untyped records), an optionalfieldMaptelling the builder which column holds the node id / manager id / department / cost-center / supervisory-org parent, aneffectiveFromFallbackdate, and an optional list ofoverrides. - Method — materialization + precedence. The builder reads each person row through the field map, emits the typed edges (
reports_tofrom manager id,costs_tofrom cost-center parent, the supervisory/department/division/geo parents where present), applies overrides, and writes nodes + edges to theorg_graphschema. A rebuild is idempotent: it deletes the prior rows for(tenantId, snapshotId)and re-materializes from scratch. - Overrides come in three rules, each a typed declaration:
explicit-edge(assert afrom → tolink directly),substring-match(a roster of substrings that, when found in an HRIS label, bridge a worker to a named leader — the workbook ELT pattern), anddominant-dept-inference(empirically attach each department to the leader most of its members already report into). - Querying.
query/resolve-ancestorwalks a single node's parent chain for a chosen hierarchy as of a date;query/ancestor-chainsdoes the same for many leaf nodes in one memoized call. The traversal honors operator overrides before HRIS before inferred edges (thecmpEdgeprecedence), and stops before cycles — once a node is revisited the walk halts (resolveAncestorChain,core/ancestors.ts). - Output: a resolved chain of
{ layer, nodeId, displayName, sourceAuthority, confidence }segments, leader rollup rows, a consistency report, or a snapshot diff, depending on the verb.
The data source is your own HRIS feed — Org Graph is a structural engine, not a reference-data spoke; it brings no external priors (no BLS, O*NET, or NAICS). The science is graph traversal with explicit provenance precedence and effective-dating, modeled on consulting-workbook leader-metrics pipelines (PAT-148). Worker identity arrives upstream from worker-resolution; Org Graph consumes the resolved rows.
Differentiation beat: the practitioner's real question isn't "what's the org chart" — it's "what was the org chart on the date this metric was measured, and do I trust each link in it?" Effective-dating answers the first half; the sourceAuthority + confidence on every resolved segment answers the second.
Visual — Tier B (step flow). persons[] + fieldMap + overrides → build (materialize typed temporal edges) → org_nodes + org_edges → resolve-ancestor / rollup / diff / consistency.
4. What does it enable?
Concrete uses a practitioner would recognize:
- Roll team metrics up to every leader — feed leaf-level measures and a hierarchy;
rollup/leadersreturns one row per leader-per-layer with summed and meaned measures and a rollup count, mirroring a workbook leader-metrics pass without the manual stacking. - Reconstruct an as-of org chart — resolve any worker's manager chain (or cost chain, or division chain) for a date in the past, so a metric measured last quarter is attributed to the org as it stood then, not as it stands now.
- Diff two snapshots —
snapshot/diffreturns who moved (old parent → new parent), span-of-control deltas with an optional magnitude threshold, subtree grafts, and the set of new/removed nodes, so a reorg is auditable. - QA an org feed before you trust it —
consistency/reportsurfaces orphans (no parent in a required hierarchy), loops (cyclic reporting), multi-parent clashes, dangling edges, and name-drift suspects. - Bridge messy ELT labels to real leaders — the substring roster turns free-text HRIS strings into clean reporting edges; dominant-dept inference attaches a department to its empirically dominant leader with a fractional confidence.
- Carry several hierarchies at once — supervisory and financial reporting often diverge; both live in the same graph as independently queryable kinds, so "where someone sits" (reporting tree) and "where their cost lands" (finance org) never get conflated.
Visual — (TBD — a rendered leader-rollup tree with per-layer counts for one snapshot).
5. How it fits in the toolbox
Data flow:
- Consumes — resolved worker rows from
worker-resolution(the two-pass self-healing merge that produces clean, deduplicated person records) and canonical field keys aligned withsegmentation-studio's HRIS normalization. Jobs and people enter as the tenant's own HRIS data; Org Graph adds no external reference data. - Emits — the canonical org-graph contract:
OrgNode,OrgEdge, the ancestor-chain shapes, the consistency report, the diff response, and the leader-rollup rows. Consumers vendorsrc/spokes/org-graph/contracts/types.ts(PAT-12 versioning). - Feeds — any spoke or surface that needs structural rollup or as-of attribution. The org spine sits underneath team-level analytics, leader scorecards, and the min-N rollup gate (
data-anonymizer) every team-level aggregate checks against. - Consumer — Performix is the named (planned) consumer; the spoke ships a Performix integration adapter (
integration/performix/) with HTTP and mock implementations plus a sample request/response fixture.
Visual — Tier B (typographic data-flow). worker-resolution rows + segmentation-studio canonical fields → Org Graph (typed temporal edges) → { leader rollups · as-of ancestor chains · reorg diffs }, consumed by Performix via a vendored contract.
6. Commercialization / packaging
Org Graph is a service component, not a standalone product — it is the structural backbone other analytics compose against, sold (when sold) inside the analytics offerings that need org rollup and as-of attribution rather than on its own.
- Data-license posture: Org Graph holds tenant-owned HRIS data only — no licensed third-party reference data flows through it, so there are no vendor-survey licensing constraints on its outputs. Tenant org data is governed by the standard tenant-isolation and min-N rollup rules of the toolbox.
- Anything about pricing tiers or packaged offerings is (TBD) — not earned yet, so not stated.
Visual — (TBD — product-tier placement diagram).
7. The vision
One org spine that answers "what did this organization look like on this date, and how sure are we of each line" — across every hierarchy a company actually runs on, with corrections that outlast the next HRIS export.
The direction is to deepen the temporal and multi-definition story already in the contract: richer reorg diffing, broader inference for the messy reporting lines HRIS feeds never encode cleanly, and tighter integration as the attribution layer beneath team-level analytics. Calculated structural segments (the kind whose bucket shape stays stable while the algorithm underneath improves) are the natural extension as more org signal comes online.
8. Current status
Grounded in the real code state (contract 1.0.0, status live in src/lib/contracts/registry.ts, schema org_graph):
- Shipped: the full materialization + query surface. Live routes:
GET /health,POST /graph/build,POST /query/resolve-ancestor,POST /query/ancestor-chains,POST /elt/substring,POST /inference/dominant-dept,POST /consistency/report,POST /snapshot/diff,POST /rollup/leaders. Nine MCP tools registered (mcp/register.ts). Tables onorg_graph:heartbeat,org_snapshots,org_nodes,org_edges. Writes gated byTOOLBOX_SERVICE_KEY(PAT-11);GET /healthpublic. Core logic is unit-tested (resolver, diff, consistency, rollup, substring-ELT, graph-builder tests undertests/). A Performix integration adapter ships with HTTP + mock implementations and a sample fixture. - In flight / planned: Performix is registered as a
plannedconsumer (not yet live). Pricing/packaging and the richer reorg/inference roadmap are(TBD).
Visual — Tier B (in-repo reference). The shipped surface is enumerated in src/spokes/org-graph/README.md and the registry block at src/lib/contracts/registry.ts (lines 1555–1637).
Worked example
A single ancestor resolution, grounded in the spoke's real resolveAncestorChain core (core/ancestors.ts) and the shipped Performix fixture (integration/performix/fixtures/).
Input (POST /api/spokes/org-graph/query/resolve-ancestor, the real sample-request.json):
{
"tenantId": "tenant-fixture",
"snapshotId": "snap-fy25",
"nodeId": "worker-42",
"hierarchyKind": "reports_to",
"asOfDate": "2026-04-01"
}
Method. resolveAncestorChain filters to reports_to edges active on 2026-04-01, then walks parent-by-parent from worker-42. At each step it sorts the candidate edges by cmpEdge precedence — an operator-override edge wins over an hris edge wins over an inferred edge — and takes the winner. It records a visited-set and halts the moment a node would repeat, so a cyclic feed can never loop forever.
Output (the real sample-response.json shape):
{
"resolved": [
{ "layer": 1, "nodeId": "mgr-7", "displayName": "Pat Lee",
"sourceAuthority": "workday.reports_to", "confidence": 0.96 },
{ "layer": 2, "nodeId": "exec-1", "displayName": "Sam Rivera",
"sourceAuthority": "workday.reports_to.override", "confidence": 1 }
]
}
What a practitioner does with it. Layer 1 is the direct manager (Pat Lee, confidence 0.96, straight from the HRIS feed). Layer 2 is the next line up — and its sourceAuthority ends in .override, meaning the HRIS said one thing but an operator corrected the link to Sam Rivera, who is now returned with confidence 1. The practitioner attributes worker-42's metrics up this exact two-link chain, trusting the corrected top link over whatever the raw export claimed. Because the request was stamped asOf 2026-04-01, this is the chain as it stood on that date — not whatever the latest export shows today.
The request and response above are the spoke's own shipped fixtures; the precedence and cycle-safe walk are the behavior of resolveAncestorChain. No figure here is invented.