Skip to content
Back to skills

Source Mapping

ASecurity

Drive a raw retail source table through the source-mapping gate before any silver SQL exists. Use when someone asks to map, model, profile, or onboard a new bronze table toward Power BI in the Seshat BI repo -- profile the source, decide grain/PK, fill the five mapping artifacts into mappings/<table>/, and stop at the gate. This skill ENFORCES the rule that no silver.* SQL is written until the map is reviewed and approved. It profiles and authors and stops; it does NOT write silver/gold SQL a...

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 29, 2026
datagobashsqlgitapidatabase

Works with

  • cli
  • api

Security analysis

A92/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned September 29, 2026

npx -y skills add Kemetra/Seshat-BI --skill source-mapping --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Source Mapping?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Source Mapping
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/kemetra-source-mapping/badge)](https://www.skillsdirectory.com/skills/kemetra-source-mapping)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: source-mapping
description: >-
  Drive a raw retail source table through the source-mapping gate before any
  silver SQL exists. Use when someone asks to map, model, profile, or onboard a
  new bronze table toward Power BI in the Seshat BI repo -- profile
  the source, decide grain/PK, fill the five mapping artifacts into
  mappings/<table>/, and stop at the gate. This skill ENFORCES the rule that no
  silver.* SQL is written until the map is reviewed and approved. It profiles and
  authors and stops; it does NOT write silver/gold SQL and does NOT build the
  Power BI model.
---

# source-mapping

The source-mapping gate is the kit's one load-bearing rule (constitution
Principle IV): **before any `silver.*` SQL is written, the source MUST be
profiled and mapped into committed, reviewed artifacts.** This skill runs that
gate: profile -> author the five artifacts -> stop at judgment calls -> hard-stop
before silver. It formalizes Phases 1-4 of the medallion playbook; the
playbook stays authoritative on HOW to decide, the templates on WHAT to record.

## Scope boundary (read first)

This skill profiles a source and authors the mapping artifacts, then STOPS. It
does NOT write `silver.*` or `gold.*` SQL, does NOT call pbi-cli or Power BI
Desktop, and does NOT decide the judgment calls Principle V reserves for a human.
Silver is downstream of an APPROVED map -- approval is the reviewer's action, not
this skill's.

## The five artifacts (write the blanks with `seshat scaffold-source <table>`)

Run `seshat scaffold-source <table>` to materialize the canonical blanks into
`mappings/<table>/`. Do NOT hand-author these files, and do NOT copy from a
`templates/` path: `templates/` exists only in the Seshat development repo, so
that instruction fails for anyone who installed the kit as a tool -- and a
hand-written `source-map.yaml` silently misses the canonical shape that
`seshat validate` and the gold stages require (issue #488). The scaffolder ships
inside the package and never overwrites an existing file.

Per ADR 0003 (mapping-artifact location), a
table's filled set lives in `mappings/<table>/`:

1. `source-profile.md` -- Phase 1 numbers (this skill fills the mechanical ones).
2. `source-map.yaml` -- the machine-readable spine (grain+PK first, per-column
   keep/drop/rename/type/PII/gold-placement, the gold star, derived columns).
3. `assumptions.md` -- which RC1-RC16 defaults were ADOPTED vs DEVIATED (each
   deviation cites its triggering data fact).
4. `unresolved-questions.md` -- the build-blocking judgment calls + who answers.
5. `reconciliation-report.md` -- the blank the later live run fills (RC16).

## Cleaning chain (canonical order -- the HYBRID walk)

A fixed, dependency-ordered chain. The shape is **hybrid**: order-sensitive steps run as
GLOBAL passes, intra-column steps run PER-SURVIVOR (each kept column resolved in one
bundle), cross-row steps run as a GLOBAL tail. The agent recommends a default at every
step; the human decides (Principle V). NONE advance to silver -- the gate (D) is the stop.

**Per-step loop:** present the step (purpose + default + its behavior on the table) ->
human chooses keep / enhance / change the STEP -> apply -> next. The walk both cleans the
table AND lets the pipeline itself be refined.

**A. GLOBAL-FIRST (decide across ALL columns):**
1. **Keep / Drop** (Phase 2.1 / RC3) -- FIRST. One global pass over the inventory.
2. **Grain & PK** (Phase 2.0 / RC1, RC2) -- VALIDATES that the key survived step 1; ERROR
   (or surface) if a needed key column was dropped. May choose a GENERATED SURROGATE key
   over the natural grain (first-class option); if so, keep the natural key SILVER-ONLY
   for the uniqueness/dedup proof and expose only the surrogate to gold (a bare surrogate
   is unique by construction and cannot detect a double-load).
3. **PII** (Phase 2.2 / RC4) -- EARLY. A flagged NAME is NOT auto-PII: distinguish
   individual/patient data (sensitive -> drop default) from STAFF names (KPI dimensions)
   and COMPANY names (B2B org) -- the latter two are legitimate non-sensitive attributes.

**B. PER-SURVIVOR (one kept column at a time, decided TOGETHER):**
4. **Rename** (2.3) + **Type** (2.5 / RC7) + **Missing-value** (2.4 / RC5,RC6) + **Gold
   placement**. Missing-value is decided HERE, per column (no global `''->NULL` baseline),
   and ONLY for columns whose profiled `missing_count > 0` (skip 0-blank columns). A
   present-but-wrong value (e.g. a site code in a customer id) is a VALUE REMAP in a
   derived column, NOT a missing-value sentinel.

**C. GLOBAL-TAIL (cross-row):**
5. **Row filters** (2.6) -- which rows to drop (junk/zero-value); state the count and
   reconcile (`source - dropped = silver`, accounting for OVERLAP). ORDERING (load-bearing):
   a filter that targets BLANKS must evaluate PRE-sentinel (before step B's sentinel
   substitution), else `trim(col)=''` matches 0 rows and those rows wrongly survive.
6. **Derive** (2.7-2.8 / RC8, RC11, RC12) -- is_return from the AUTHORITATIVE column;
   value remaps; surrogate keys (generate the fact SK AFTER filters so it numbers
   surviving rows); analyst-supplied rollups; flat hierarchy. The date dim carries NO
   `-1`/unknown/sentinel member (so Power BI can mark it a date table, S8); an unmatched
   fact date fails via `Date_SK NOT NULL`.

**D. GATE (hard stop; Principle IV):**
7. **Named-human review** -- recording the judgment-call ANSWERS is NOT map approval. No
   `silver.*` SQL until the full map is reviewed.

**Naming:** warehouse SQL identifiers are **snake_case** (`product_id`, `gross_sales`,
`sale_sk`) -- rule **S1** flags quoted non-snake_case columns (a quoted mixed-case name
forces a case-sensitive Postgres column, a footgun). PascalCase is a **Power BI
model-layer** display choice (rename in the semantic model), NEVER a SQL identifier.

> Per-column vs global is by DEPENDENCY, not preference: global when a decision is
> table-wide (PII policy) or cross-row (filters); per-column when it depends only on that
> column's own keep decision. See the medallion playbook, Phase 2, for the rationale.

## Procedure

### 1. Locate
Confirm the bronze `schema.table` exists. Ask the analyst/agent for the candidate
PK column(s) to test (grain is decided FIRST, Phase 2.0 / RC1).

### 2. Profile (mechanical) -- via `seshat profile`
Run the mechanical profiler over a read-only connection and record the numbers
into `mappings/<table>/source-profile.md`. Use the shipped CLI verb (the
internal `seshat.profile.profile()` API is not reachable on a pip/pipx install
-- #400):

```bash
# needs the `db` extra + a DSN (DATABASE_URL / ANALYTICS_DB_* / --dsn)
seshat profile --table bronze.<table> --pk <pk_a>,<pk_b>            # markdown to paste
seshat profile --table bronze.<table> --pk <pk_a>,<pk_b> --format json
```

The output gives: `row_count`, `column_count`, per-column `missing_count` /
`missing_pct` (measured `''OR NULL`, NEVER `IS NULL` alone -- the load-bearing
trap, RC5) / `distinct_cardinality`, and the candidate-PK proof (`total`,
`distinct_pk`, `null_pk`, `is_unique`). The markdown form is already shaped for
the source-profile.md Shape / Per-column / Candidate-PK sections; paste each into
the source-profile table
and the Candidate grain & PK section.

### 3. Profile (semantic) -- PROPOSE, do not invent (Principle V)
The semantic rows -- code<->label 1:1 rate, dimension fan-out (`id -> name`),
hierarchy multi-parent, the AUTHORITATIVE returns column, money-relationship
identities, cross-file drift -- need the table's MEANING. profile.py does NOT
compute these. PROPOSE each from the data + column names, then raise it as an
`unresolved-questions.md` entry for human confirmation. Never invent a business
rollup, a PII ruling, or the returns column.

### 4. Author the map and assumptions
Starting from the RC1-RC16 defaults, fill `source-map.yaml` (grain+PK first, then
per-column decisions, the gold star, derived columns) and `assumptions.md`
(adopted vs deviated, each deviation citing its triggering data fact). Keep all
text ASCII, snake_case silver names, short paths (Windows 260 limit).

### 5. Stop-and-ask (Principle V)
Raise `unresolved-questions.md` entries, each with a who-must-answer owner, for:
business-rollup mapping (analyst supplies the full value->group table), PII
publish-safety (governance sign-off; default drop), grain ambiguity (candidate PK
not unique on the data), sentinel-vs-null choice, and any build-blocking question.

### 6. GATE -- hard stop (Principle IV)
Emit the `reconciliation-report.md` blank and STOP. Before stopping, guarantee
the decision ledger exists even when Step 5 raised zero questions -- run
`seshat mapping-mirror --table <table>`. It writes a stub only if
`unresolved-questions.md` is absent (status derived from the committed
`readiness-status.yaml`, never invented) and never overwrites the ledger; the
downstream dbt and Dagster gates hard-require this file. State plainly: no
`silver.*` SQL may be written until the map is reviewed and approved. Hand the
filled set to the reviewer; do not proceed to silver.

## Deferred/live-boundary mode (no DSN or no `db` extra)

If `resolve_dsn(...)` returns None or psycopg2 is not installed, do NOT traceback
and do NOT pretend a profile ran. The live boundary is deferred BY DESIGN:
credentials + the optional `db` extra are user-supplied under constitution
Principle VIII. In this mode:

- Report the boundary and print the exact enable steps:
  `pipx inject seshat-bi psycopg2-binary` (or `pip install "seshat-bi[db]"`), then
  set `DATABASE_URL` (or the `ANALYTICS_DB_*` vars) in the gitignored `.env`. Never
  commit a real DSN.
- STAY USEFUL: run `seshat scaffold-source <table>` to write the five canonical
  blanks into `mappings/<table>/` (it needs no DB), fill their STRUCTURE, mark the
  mechanical profile numbers `[PENDING LIVE PROFILE]`, still drive the semantic
  stop-and-ask (Step 3) and the gate (Step 6).

## See also

- Gate + principles: the kit architecture note, Sec 5; constitution
  Principles IV, V.
- Method / defaults: the medallion playbook; the retail cleaning-defaults
  decision record (RC1-RC16).
- Live half (after silver/gold exist): the `retail-validate` skill.
- A filled instance: a filled worked example (an
  example, never the universal schema).

## Orchestration

When a table is being driven end-to-end, the `retail-orchestrate` conductor skill
sequences this verb with the others and runs the self-heal loop against the gate
exit code. This skill stays single-purpose: it does its job and STOPS. The loop
(run gate -> classify findings -> auto-fix mechanical / HARD-STOP judgment calls ->
re-run) lives ONLY in `retail-orchestrate`, never here.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…