Getting Started: Map for Leadership
Map is the data product at the centre of every other tool. It has two layers that you set up in order:
-
Framework layer — YAML files defining your
skills, behaviours, levels, disciplines, and tracks. Validated
locally with
npx fit-map validate. This is what Pathway, Basecamp, andlibskillconsume. - Activity layer — A Supabase database that stores your organization roster, GitHub activity, evidence, and GetDX snapshots. Powers Landmark and Summit, and lets Guide write skill evidence back against framework markers.
The framework layer is required. The activity layer is optional but unlocks everything Landmark and Summit do.
Prerequisites
- Node.js 18+
- npm
Install
npm install @forwardimpact/map
Framework: initialize starter data
Bootstrap a complete framework skeleton with editable YAML files:
npx fit-map init
This creates ./data/pathway/ with starter definitions
for levels, disciplines, capabilities, skills, behaviours, stages,
drivers, and tracks. The starter data is a working framework you can
customize to match your organization.
Framework: validate
Run the validator to check your YAML files against the schema:
npx fit-map validate
Fix any errors the validator reports before moving on.
Framework: customize
The starter data gives you a complete foundation. Edit the YAML
files under data/pathway/ to match your
organization's engineering expectations.
Levels
Edit data/pathway/levels.yaml to define your level
structure. Each level sets baseline expectations for skill
proficiency and behaviour maturity.
- id: J040
professionalTitle: Level I
managementTitle: Associate
ordinalRank: 1
baseSkillProficiencies:
primary: foundational
secondary: awareness
broad: awareness
baseBehaviourMaturity: emerging
- id: J060
professionalTitle: Level II
managementTitle: Senior Associate
ordinalRank: 2
baseSkillProficiencies:
primary: working
secondary: foundational
broad: awareness
baseBehaviourMaturity: developing
Capabilities and skills
Edit files under data/pathway/capabilities/ to define
capability groups containing skills. Each skill needs a
human: section with proficiency descriptions at all
five levels.
name: Delivery
description: Ship working software reliably.
skills:
- id: task_execution
name: Task Execution
human:
description: Breaking down and completing engineering work
proficiencyDescriptions:
awareness: >
Understands the team's delivery workflow and follows guidance
to complete assigned tasks.
foundational: >
Breaks work into steps, estimates effort, and completes tasks
with minimal guidance.
working: >
Independently plans and delivers work, adjusting approach when
requirements change.
practitioner: >
Leads delivery across multiple workstreams, mentoring others
on effective execution.
expert: >
Defines delivery practices that scale across the organization.
Disciplines
Edit files under data/pathway/disciplines/ to define
role types that reference your capability skills.
specialization: Software Engineering
roleTitle: Software Engineer
coreSkills:
- task_execution
validTracks:
- null
Use null in validTracks to allow a
trackless (generalist) configuration.
After each change, re-validate with
npx fit-map validate.
Activity: install the Supabase CLI
The activity layer runs on Supabase. You need the Supabase CLI to
start a local instance and to deploy migrations and edge functions
to a hosted project. fit-map wraps the CLI for every
activity workflow and will find it whether you install it via
Homebrew or as an npm package.
# macOS via Homebrew (recommended if you have brew)
brew install supabase/tap/supabase
# Anywhere, as a project dependency
npm install supabase
# Linux / Windows — see https://supabase.com/docs/guides/local-development
fit-map prefers a supabase binary on your
PATH and falls back to
npx supabase (resolving from your project's
node_modules) if one is not found, so the npm-local
install works without any PATH setup.
Verify the install:
supabase --version
# or, for a project-local install:
npx supabase --version
Activity: start the database
Map ships its full Supabase project — config.toml,
migrations, edge functions, and kong.yml — inside the
npm package. fit-map activity start runs
supabase start against the bundled project so you
don't need to cd anywhere:
npx fit-map activity start
The CLI prints the local URL and the service-role key when it finishes booting. Copy the export commands it prints — every ingestion command needs them.
export MAP_SUPABASE_URL=http://127.0.0.1:54321
export MAP_SUPABASE_SERVICE_ROLE_KEY=<service-role key from fit-map activity start>
To stop the local instance:
npx fit-map activity stop
To check whether the local stack is running and the activity schema is reachable:
npx fit-map activity status
For a hosted deployment, link the project once, push the migrations, and deploy all four edge functions:
supabase link --project-ref <your-project-ref>
supabase db push
supabase functions deploy github-webhook getdx-sync people-upload transform
Activity: apply migrations
fit-map activity start applies the bundled migrations
automatically the first time it runs. Three migrations create
everything Map needs:
| Migration | Creates |
|---|---|
20250101000000_activity_schema.sql |
activity schema with
organization_people, GitHub, GetDX, evidence
|
20250101000001_get_team_function.sql |
activity.get_team(email) recursive CTE for
manager-rooted team walks
|
20250101000002_raw_bucket.sql |
raw storage bucket for the ELT extract phase
|
To re-apply migrations against a clean local database (this drops your data):
npx fit-map activity migrate
Activity: push people
The unified person model lives in
activity.organization_people. Email is the join key
across HR, GitHub commits, and GetDX. Each row also carries a
Pathway job profile (discipline, level,
track), so any consumer can derive the full skill
matrix for that person.
Create a people.yaml file with your roster:
- email: ada@example.com
name: Ada Lovelace
github_username: adalovelace
discipline: software_engineering
level: J040
track: platform
manager_email: charles@example.com
- email: charles@example.com
name: Charles Babbage
github_username: cbabbage
discipline: software_engineering
level: J060
manager_email: null
CSV is also supported. Use the same column names as the YAML keys.
Step 1: validate locally
fit-map people validate checks the file against your
framework — every discipline, level, and
track must exist in data/pathway/. It does
not talk to Supabase. Treat this as a fast pre-flight check before
pushing to the database.
npx fit-map people validate ./people.yaml
The CLI reports validation errors row by row. Fix them in
people.yaml and re-run until you see a clean result.
Step 2: push to Supabase
Once validation passes, push the roster into the activity database:
npx fit-map people push ./people.yaml
fit-map people push stores the file in the
raw bucket for audit, then upserts it into
activity.organization_people. People without a manager
are inserted before people with one, so the
manager_email foreign key always resolves. Re-run the
command any time your roster changes — it upserts on
email, so it's safe to run repeatedly.
Behind the scenes, fit-map people push talks to the
same extract and transform helpers that the
people-upload edge function uses. If you prefer to run
the upload server-side — for example from a form or an admin
workflow — POST the file to the hosted function instead:
curl -X POST \
-H "Authorization: Bearer $MAP_SUPABASE_SERVICE_ROLE_KEY" \
-H "Content-Type: application/x-yaml" \
--data-binary @./people.yaml \
https://<project-ref>.supabase.co/functions/v1/people-upload
Activity: ingest GitHub activity
Map ships a github-webhook edge function that receives
GitHub webhook events, stores the raw payload in the
raw bucket, and extracts normalized artifacts into
activity.github_artifacts. Pull requests, reviews, and
pushes are all handled out of the box.
With the local Supabase running, the function URL is:
http://127.0.0.1:54321/functions/v1/github-webhook
For a hosted deployment, the URL is:
https://<project-ref>.supabase.co/functions/v1/github-webhook
In your GitHub organization or repository settings, add a webhook pointing at that URL with these events selected:
- Pull requests
- Pull request reviews
- Pushes
Set the content type to application/json. Each delivery
is stored under raw/github/<delivery-id>.json and
processed into activity.github_events and
activity.github_artifacts. The function joins each
artifact to a person via github_username, so make sure
your people.yaml rows have GitHub usernames filled in
for the engineers you want to track.
Activity: ingest GetDX snapshots
If your organization uses GetDX, Map can pull snapshot results into the same database so Landmark can correlate survey scores with marker evidence.
Get a GetDX API token from your GetDX admin. Then run the sync —
either locally with the CLI, or on a schedule by POSTing to the
getdx-sync edge function.
Ad-hoc or one-shot sync
GETDX_API_TOKEN=<your getdx api token> npx fit-map getdx sync
fit-map getdx sync fetches teams.list,
snapshots.list, and snapshots.info for
every undeleted snapshot, stores each response under
raw/getdx/, and upserts:
-
activity.getdx_teams— the GetDX team hierarchy, bridged to your roster viamanager_email -
activity.getdx_snapshots— quarterly survey metadata -
activity.getdx_snapshot_team_scores— factor and driver scores per team per snapshot, withvs_prev,vs_org, and percentile comparisons
The command prints the imported team, snapshot, and score counts when it finishes.
Scheduled sync
For continuous ingestion, set the GetDX token as a secret on your
hosted Supabase project and schedule the
getdx-sync edge function on any cron that can send an
HTTP POST — GitHub Actions schedule: jobs, a Nomad
periodic, or cron.d:
supabase secrets set GETDX_API_TOKEN=<your getdx api token>
curl -X POST \
-H "Authorization: Bearer $MAP_SUPABASE_SERVICE_ROLE_KEY" \
https://<project-ref>.supabase.co/functions/v1/getdx-sync
Once a quarter is typical — match your GetDX survey cadence. The edge function and the CLI run the same extract-and-transform code, so switching between them is purely a deployment choice.
The driver IDs in data/pathway/drivers.yaml are the
same IDs as getdx_snapshot_team_scores.item_id — GetDX
assigns those IDs, and you mirror them when authoring
drivers.yaml. That shared namespace is what lets
Landmark juxtapose a driver's GetDX score against the marker
evidence for its contributing skills.
Activity: re-run transforms
To reprocess every raw document in storage from scratch — for
example after restoring a database, after upgrading Map to pick up a
transform fix, or to backfill from raw payloads — ask
fit-map to re-run every transform against the
raw bucket:
npx fit-map activity transform
The command reads people, GetDX, and GitHub raw documents in dependency order and upserts on natural keys, so it is safe to re-run. To reprocess a single source instead of all three:
npx fit-map activity transform people
npx fit-map activity transform getdx
npx fit-map activity transform github
The hosted equivalent is the transform edge function,
which runs the same code server-side:
curl -X POST \
-H "Authorization: Bearer $MAP_SUPABASE_SERVICE_ROLE_KEY" \
https://<project-ref>.supabase.co/functions/v1/transform
Trying the activity layer with synthetic data
If you want to explore the activity layer before connecting real data sources, Map can populate the database with synthetic data — a realistic roster, GitHub events, and GetDX snapshots generated from a template.
First, generate synthetic data (requires the
@forwardimpact/libterrain package):
npx fit-terrain data/synthetic/story.dsl
Then seed the activity database:
npx fit-map activity seed
This uploads the generated roster and raw documents, runs all
transforms, and verifies the result. The database will contain
realistic but fictional data you can query with Landmark or Summit.
When you are ready to switch to real data, push your actual roster
with npx fit-map people push — it overwrites the
synthetic entries.
Activity: verify the data
Once people are pushed and at least one data source is available —
either from real ingestion commands above or from
activity seed — verify the database:
npx fit-map activity verify
fit-map activity verify reads
activity.organization_people and at least one derived
table (getdx_snapshots or github_events),
prints the row counts it found, and exits 0 if both are populated.
If either is empty it exits non-zero with a message pointing at the
step that didn't run.
If verification passes, your activity layer is ready for Landmark, Summit, and Guide.
Next steps
- Map product page — feature overview and command reference
- Authoring frameworks — full guide to defining all entity types: levels, disciplines, tracks, capabilities, skills, behaviours, stages, and drivers
- YAML schema reference — complete file format documentation