Getting Started: Map for Leaders
Map is the data product at the centre of every other tool. It has two layers that you set up in order:
-
Standard layer — YAML files that define your
skills, behaviours, levels, disciplines, and tracks. Validate them
locally with
npx fit-map validate. Pathway, Outpost, andlibskillconsume this layer. - Activity layer — A Supabase database that stores your organization roster, GitHub activity, evidence, and GetDX snapshots. It powers Landmark and Summit. It also lets Guide write skill evidence back against markers in the agent-aligned engineering standard.
The standard layer is required. The activity layer is optional. It unlocks everything Landmark and Summit do.
Prerequisites
- Node.js 22+
- npm
Install
npm install @forwardimpact/map
Agent-Aligned Engineering Standard: initialize starter data
Bootstrap a complete skeleton of the agent-aligned engineering standard with editable YAML files:
npx fit-map init
This creates ./data/pathway/ with starter definitions
for levels, disciplines, capabilities, skills, behaviours, drivers,
and tracks. It also creates a ./config/config.json, so
later fit-map commands anchor their configuration at
the project root. The starter data is a working agent-aligned
engineering standard you can customize to match your organization.
npx fit-map init is a no-op against an existing
project. It keeps the files it already copied.
Agent-Aligned Engineering Standard: validate
Run the validator to check your YAML files against the schema:
npx fit-map validate
Fix any errors the validator reports before you continue.
Agent-Aligned Engineering Standard: 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:
core: foundational
supporting: awareness
broad: awareness
baseBehaviourMaturity: emerging
- id: J060
professionalTitle: Level II
managementTitle: Senior Associate
ordinalRank: 2
baseSkillProficiencies:
core: working
supporting: foundational
broad: awareness
baseBehaviourMaturity: developing
Capabilities and skills
Edit files under data/pathway/capabilities/ to define
capability groups that contain 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. It finds the CLI whether you install it with
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. If it finds none, it falls back to
npx supabase and resolves it from your project's
node_modules. 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 inside the npm package. The
project holds config.toml, migrations, edge functions,
and kong.yml. 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 a one-line ready confirmation when the stack is up.
Every ingestion command reads SUPABASE_URL and
SUPABASE_SERVICE_ROLE_KEY from .env.
just env-setup (for monorepo contributors) or your
hosted Supabase project's API settings provide them. You do not
need an export step.
To stop the local instance:
npx fit-map activity stop
To check whether the local stack is up and the activity schema is reachable:
npx fit-map activity status
For a hosted deployment, link the project once. Push the migrations. 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. The core migrations that
create the structures every Map install needs are:
| 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
|
Additional evolutionary migrations under
products/map/supabase/migrations/ extend the schema
(driver columns, RLS policies, evidence keys). All bundled
migrations apply in lexical order on first start.
To re-apply migrations against a clean local database, run this command. It 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
Map also supports CSV. Use the same column names as the YAML keys.
Step 1: validate locally
fit-map people validate checks the file against your
agent-aligned engineering standard. 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 you push to the database.
npx fit-map people validate ./people.yaml
The CLI reports validation errors row by row. Fix them in
people.yaml. Re-run the command 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. It then upserts the file into
activity.organization_people. The command inserts
people without a manager 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 you can run it repeatedly.
Behind the scenes, fit-map people push talks to the
same extract and transform helpers that the
people-upload edge function uses. 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 $SUPABASE_SERVICE_ROLE_KEY" \
-H "Content-Type: application/x-yaml" \
--data-binary @./people.yaml \
https://<project-ref>.supabase.co/functions/v1/people-upload
Activity: provision auth users
Landmark's row-level security admits a request based on the
JWT's email claim. Supabase Auth only issues a JWT
for an auth.users row that already exists. After you
push the roster, reconcile auth.users against the
roster:
npx fit-terrain substrate provision
The command prints a per-action summary (created,
restored, decommissioned,
unchanged). You can re-run it safely. It upserts on
email and preserves auth.users.id across decommission
and rejoin. See
Provision Engineer Auth Users
for the full operator workflow.
fit-terrain substrate provision makes sure every
engineer's email maps to an authenticable identity. Leaders and
engineers then sign in to Landmark with
fit-landmark login. That command walks Supabase's
magic-link flow. It stores the session in your platform's config
directory (~/.config/landmark/credentials.json on
Linux). See
Sign In to Landmark
for the end-to-end flow, or
Authentication
in the Landmark guide for the short version.
Activity: ingest GitHub activity
Map ships a github-webhook edge function. The function
receives GitHub webhook events. It stores the raw payload in the
raw bucket. It extracts normalized artifacts into
activity.github_artifacts. It handles pull requests,
reviews, and pushes out of the box.
When the local Supabase runs, 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 that points at that URL. Select these events:
- Pull requests
- Pull request reviews
- Pushes
Set the content type to application/json. The function
stores each delivery under
raw/github/<delivery-id>.json. It processes each
delivery into activity.github_events and
activity.github_artifacts. The function joins each
artifact to a person through github_username. Make sure
your people.yaml rows have GitHub usernames 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. Run
it locally with the CLI, or on a schedule with a POST 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. It stores each response under
raw/getdx/. It then upserts:
-
activity.getdx_teams— the GetDX team hierarchy, bridged to your roster throughmanager_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. Then schedule the
getdx-sync edge function on any cron that can send an
HTTP POST. Examples are 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 $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 the choice 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. You mirror them when you author
drivers.yaml. That shared namespace lets Landmark
juxtapose a driver's GetDX score against the marker evidence for
the skills that contribute to it.
Activity: re-run transforms
To reprocess every raw document in storage from scratch, ask
fit-map to re-run every transform against the
raw bucket. Do this after you restore a database, after
you upgrade Map to pick up a transform fix, or to backfill from raw
payloads:
npx fit-map activity transform
The command reads people, GetDX, and GitHub raw documents in dependency order. It then derives artifact-interpreted evidence rows from the ingested GitHub artifacts. Every step upserts on natural keys, so you can re-run it safely. To reprocess a single target instead of all of them:
npx fit-map activity transform people
npx fit-map activity transform getdx
npx fit-map activity transform github
npx fit-map activity transform evidence-artifact
The hosted equivalent is the transform edge function,
which runs the same code server-side:
curl -X POST \
-H "Authorization: Bearer $SUPABASE_SERVICE_ROLE_KEY" \
https://<project-ref>.supabase.co/functions/v1/transform
Trying the activity layer with synthetic data
You can explore the activity layer before you connect real data sources. Map can populate the database with synthetic data. The data is a realistic roster, GitHub events, and GetDX snapshots generated from a template.
First, generate synthetic data (requires the
@forwardimpact/libterrain package):
npx fit-terrain build --story=data/synthetic/story.dsl
Then seed the activity database:
npx fit-map activity seed
This uploads the generated roster and raw documents. It runs all
transforms. It verifies the result. The database then holds
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. That command overwrites
the synthetic entries.
Activity: verify the data
Push people first. Make at least one data source available, either
with the real ingestion commands above or with
activity seed. Then 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).
It prints the row counts it found. It exits 0 if both hold rows. If
either is empty, it exits non-zero with a message that points at the
step that didn't run.
If verification passes, your activity layer is ready for Landmark, Summit, and Guide.
What's next
Map
Turn implicit engineering expectations into an operational definition. It holds skills, behaviours, levels, and operational signals in plain text.
Authoring Agent-Aligned Engineering Standards
Turn 'good engineering' into an operational definition so evaluations start from a shared foundation instead of private mental models.