Map Internals

Internal Layering

products/map/
  src/              Pure data model (published to npm)
    loader.js       YAML file loading and parsing
    validation.js   Referential integrity and data validation
    schema-validation.js  JSON Schema validation
    levels.js       Type definitions, skill proficiencies, behaviour maturities
    modifiers.js    Capability and skill modifier utilities
    index-generator.js  Browser index generation
    index.js        Public API exports
  schema/           JSON Schema + RDF/SHACL (published to npm)
    json/           JSON Schema definitions
    rdf/            RDF/SHACL definitions
  activity/         Operational layer (NOT published to npm)
    migrations/     SQL schema definitions
    extract/        Data extraction utilities
    transform/      Data transformation utilities
    storage.js      Storage interface
    queries/        Reusable query functions
      org.js        Organization and team queries
      snapshots.js  GetDX snapshot queries
      evidence.js   Evidence queries
      artifacts.js  GitHub artifact queries
  bin/fit-map.js    CLI entry point (routes to both layers)

Pure layer (src/, schema/) -- Framework schema, validation, data loading. Zero infrastructure dependencies. Published to npm.

Activity layer (activity/) -- Supabase migrations, ingestion pipelines, query functions. Requires runtime infrastructure. Excluded from npm publish.

Layering rule: activity/ may import from src/ (e.g., to validate discipline values during people import). src/ must never import from activity/.

Join convention: Framework entity IDs (discipline, level, track, skill_id, level_id, driver.id) serve as natural join keys between the activity layer and the pure layer. Activity tables store these IDs as bare strings; consumers join them to framework objects in application code. No mapping tables bridge the two layers -- the shared ID namespace is the contract.


Activity Model

Core data product contracts:

Table Purpose
organization_people Unified person model (email PK)
github_events Raw GitHub webhook events
github_artifacts Normalized artifacts (email join to person)
evidence Guide-written skill evidence (artifact_id join)
getdx_snapshots Quarterly snapshot metadata
getdx_teams GetDX team hierarchy (manager_email join)
getdx_snapshot_team_scores Aggregated scores per team per snapshot

Team scope is derived from the manager hierarchy -- not stored as a separate entity.

Unified Person Model

organization_people uses email as the cross-system join key spanning HR, GetDX, GitHub (via commit author), and internal tooling. The discipline, level, and track fields carry the Pathway job profile, so any consumer can call deriveJob(discipline, level, track, data) from libskill to get the full skill matrix for a person.


Ingestion Surfaces

GetDX Snapshots

Map imports GetDX snapshots via the snapshots.list and snapshots.info APIs. The ingestion pipeline diffs against known snapshots, imports new or changed ones, and upserts team scores. GetDX teams are bridged to the internal org model via manager_email.

Three tables capture the data:

  • getdx_snapshots — metadata for each quarterly survey cycle.
  • getdx_teams — GetDX team hierarchy with manager_email bridge.
  • getdx_snapshot_team_scores — aggregated factor/driver scores with comparative metrics (vs_prev, vs_org, vs_50th, vs_75th, vs_90th).

GitHub Webhooks

Map receives GitHub webhook events (pull_request, pull_request_review, push), stores raw events in github_events, and extracts normalized artifacts (PRs, reviews, commits) into github_artifacts. Each artifact is linked to the unified person model via github_username to email.

Evidence Pipeline

GitHub Events -> Map (github_events -> github_artifacts)
                                        |
                                 Guide (interprets against markers)
                                        |
                                 activity.evidence

Guide reads artifacts without evidence rows, assesses each against skill markers from capability YAML, and writes evidence back with skill_id, level_id, marker_text, matched, and rationale.


Query Functions

Map's activity/queries/ modules expose reusable query functions that form part of the data product contract. Consumers import these rather than querying Supabase directly.

All query functions take a supabase client as their first parameter.

Module Function Purpose
org.js getOrganization(supabase) All people from organization_people
org.js getTeam(supabase, managerEmail) Recursive walk of manager_email hierarchy
snapshots.js listSnapshots(supabase) All snapshots ordered by scheduled_for
snapshots.js getSnapshotScores(supabase, snapshotId, { managerEmail }) Team scores, optionally filtered by manager's team
snapshots.js getItemTrend(supabase, itemId, { managerEmail }) Score trajectory across snapshots
snapshots.js getSnapshotComparison(supabase, snapshotId, { managerEmail }) Scores with comparative metrics
evidence.js getEvidence(supabase, { skillId, email }) Evidence rows, filtered by skill or person
evidence.js getPracticePatterns(supabase, { skillId, managerEmail }) Aggregated evidence across a manager's team
artifacts.js getArtifacts(supabase, { email, type }) GitHub artifacts, filtered by person or type

Drivers and GetDX Alignment

Framework drivers are the GetDX drivers. The driver id in drivers.yaml matches the item_id in getdx_snapshot_team_scores -- no separate mapping is needed. Each driver declares contributingSkills and contributingBehaviours, linking the survey-measured outcome back to framework definitions. This is what makes health views possible: a driver's GetDX score can be juxtaposed with marker evidence for its contributing skills.


Consumers

The data product serves five consumers through two interfaces:

Product Layer Consumes
Guide Activity Artifacts (reads), evidence (writes), markers (reads)
Pathway Pure Framework schema (skills, disciplines, levels, tracks)
Basecamp Pure Framework schema
libskill Pure Framework schema for derivation

Pure-layer consumers install @forwardimpact/map from npm. Activity-layer consumers import from @forwardimpact/map/activity/queries as a workspace dependency -- these imports are never published.


Programmatic Access

// Pure layer imports
import { createDataLoader } from "@forwardimpact/map";
import { validateAllData } from "@forwardimpact/map/validation";
import { SKILL_PROFICIENCIES, BEHAVIOUR_MATURITIES } from "@forwardimpact/map/levels";

const loader = createDataLoader({ dataDir: "./data" });
const data = await loader.load();

For activity datasets, consumers import query functions from @forwardimpact/map/activity/queries as workspace dependencies:

// Activity layer imports (workspace only, not published)
import { getOrganization, getTeam } from "@forwardimpact/map/activity/queries/org";
import { listSnapshots, getSnapshotScores } from "@forwardimpact/map/activity/queries/snapshots";
import { getEvidence } from "@forwardimpact/map/activity/queries/evidence";
import { getArtifacts } from "@forwardimpact/map/activity/queries/artifacts";