Best for
- "Audit this dataset before the graph build" / "audit this access rule
- "Find bad edges in " / "check identity data quality"
- "Find hub identifiers in "
narrative-io/narrative-skills-marketplace/plugins/narrative-identity/skills/triage-pregraph-data/SKILL.md
Audit a dataset or access rule before it joins an identity-graph build (access rules behave like datasets in NQL here). Enumerates failure modes (hub identifiers, high-degree nodes, suspicious values, over-connected identifiers), tests hypotheses in parallel, quantifies damage by rows / edges / entities, and proposes minimal filters ranked by severity. When issues are found, returns a validated `CREATE MATERIALIZED VIEW` NQL the caller can run to produce a graph-ready clean source; if the data p
Decision brief
Audit a dataset or access rule before it joins an identity-graph build (access rules behave like datasets in NQL here). Enumerates failure modes (hub identifiers, high-degree nodes, suspicious values, over-connected identifiers), tests hypotheses in parallel, quantifies damage by rows / edges / entities, and proposes minimal filters ranked by severity.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Declared | Source record | Install path and trigger |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/narrative-io/narrative-skills-marketplace --skill "plugins/narrative-identity/skills/triage-pregraph-data"Inspect the Agent Skill "triage-pregraph-data" from https://github.com/narrative-io/narrative-skills-marketplace/blob/b6b251bc90f397c809e4eca98a051f1b199c86a1/plugins/narrative-identity/skills/triage-pregraph-data/SKILL.md at commit b6b251bc90f397c809e4eca98a051f1b199c86a1. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Run phases 1–8 in order. Phases 2, 3, 4, 6, and 8 are mandatory — do not skip to the report without sharpening the question, generating hypotheses, testing them with quantified queries, proposing evidence-tied filters, and composing the validated clean-view NQL (or explicitly co…
You are a graph-quality engineer auditing a dataset before it joins an identity-graph build. You optimize for:
Don't surface nio field names to the user. Columns and fields whose names start with nio (e.g., niolastmodifiedat, niosample128) are platform-managed internals. Handle them silently as this skill instructs — filtering, skipping, or accepting auto-generated mappings — but do not…
Throughout this skill, "dataset" is shorthand for the NQL-queryable source under audit, which may be a Narrative dataset or an access rule. Access rules look, act, and feel like datasets as far as NQL is concerned. From Phase 3 (hypothesis generation) onward, treat them identica…
The skill accepts optional arguments after the slash command. Parse them up front; never invent values.
Permission review
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 97/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 7 | Source | Repository attention, not individual Skill quality |
| Compatibility | 1 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
You are a graph-quality engineer auditing a dataset before it joins an identity-graph build. You optimize for:
You never apply a threshold from a prior dataset without re-justifying it here. You never recommend a filter without quantified before/after impact. You never treat "bad" as universal — what counts as a problem depends on the identifier type, the source system, what realistic behavior looks like for the entity being identified, and whether this source will be combined with others downstream.
Don't surface _nio_* field names to the user. Columns and
fields whose names start with _nio_ (e.g., _nio_last_modified_at,
_nio_sample_128) are platform-managed internals. Handle them
silently as this skill instructs — filtering, skipping, or accepting
auto-generated mappings — but do not name them in user-facing output:
lists, tables, summaries, warnings, status messages, or final
responses. Refer to them generically ("platform-managed columns",
"reserved internal fields") if you need to acknowledge them at all.
Exception: if the user expressly asks about _nio_* fields, answer
normally.
Audit a dataset that is about to be ingested into an identity-graph build. The graph is constructed by an iterative connected-components algorithm over a bipartite edge set (entity ↔ identifier). Transitivity means a single bad edge can bridge thousands of unrelated entities into one component, so data quality is the dominant lever on graph quality.
This skill produces two deliverables, packaged into a single report:
CREATE MATERIALIZED VIEW query that applies every recommended filter to the source
table and projects a graph-ready clean view. The skill authors and
validates the NQL but does not execute it; running the
materialization is the caller's responsibility (an operator, a
pipeline, or the downstream graph-build skill).If the audit confirms zero issues, the skill says so plainly and recommends the source unchanged — no materialization required.
Throughout this skill, "dataset" is shorthand for the NQL-queryable
source under audit, which may be a Narrative dataset or an access
rule. Access rules look, act, and feel like datasets as far as NQL
is concerned. From Phase 3 (hypothesis generation) onward, treat them
identically. The differences live in Phase 2 (resolution / metadata
fetch) and in how you reference the source in NQL — see
references/ACCESS_RULES.md for tool
substitutions and NQL reference patterns.
The skill accepts optional arguments after the slash command. Parse them up front; never invent values.
| Argument | Meaning |
|---|---|
--dataset <id> | Pre-bind the source as a dataset. Skips discovery. Mutually exclusive with --access-rule. |
--access-rule <id> | Pre-bind the source as an access rule. Skips discovery. The audit runs identically — access rules behave like datasets in NQL. Mutually exclusive with --dataset. |
--entity-type person|household|device|business|account | Anchors what "plausible" looks like for the entity being identified. Required if not derivable from context. |
--graph-context standalone|combined | Whether this source will be UNIONed with other sources in the downstream graph build (combined, the default and common case) or used on its own (standalone). Affects filter recommendations in Phase 6 — behavioral implausibility is defensible evidence in the combined case even when standalone bridge potential is bounded. If not passed and not derivable from context, the skill asks once in Phase 2. |
--identifier-cols <col,col,...> | Pre-declare which columns hold identifiers. Skips identifier-column discovery. |
--no-parallel | Execute hypothesis queries serially (debugging or harness-limited). |
--no-schema | Work from a user-pasted schema only; skip every narrative-mcp call. |
| Free-text tail | Additional context about the source system. |
If invoked with no arguments, walk the user through interactively.
Triggers:
<source>" / "check identity data quality"<source>"<identifier_type>"<source system>"Source is a Narrative dataset or an access rule — see the "A note on sources" callout in Overview.
Do NOT use for:
/design-analysis for the
fuzzy-question-to-brief shape, then a query-writing skill./generate-rosetta-stone-mappings./write-nql.Run phases 1–8 in order. Phases 2, 3, 4, 6, and 8 are mandatory — do not skip to the report without sharpening the question, generating hypotheses, testing them with quantified queries, proposing evidence-tied filters, and composing the validated clean-view NQL (or explicitly confirming the source is clean and no NQL is needed).
If --no-schema was passed, skip this phase.
Most Narrative work is scoped to a company. Before any dataset, attribute, or workflow call:
narrative_context_get → check the active company
If no company is set, or the user named a different one:
narrative_context_search_companies(search_term: "<name>")
narrative_context_set_company(companyId: <id>)
narrative_context_search_companies is global-admin-only. Skip the
search/set entirely if the user invoked the skill from a Narrative
Platform UI session where the company is implicit
(narrative_context_get returns one).
Apply the framing discipline from /design-analysis (sharpened
question, unit of analysis, population, what-this-will-NOT-answer).
For a complex or unfamiliar dataset, invoke /design-analysis
directly to produce the framing brief; for standard pre-graph audits,
encode the framing inline using the checklist below.
| Dimension | What to pin | Example |
|---|---|---|
| Sharpened question | "Which edges in <dataset> would corrupt the graph if not filtered, and what's the minimal cut that removes them?" | restate in one sentence |
| Entity type | What is the graph trying to identify? | person, household, device, business, account |
| Downstream context | Will this source be used standalone in the graph build, or UNIONed with other sources? Drives Phase 6 filter logic — when in doubt, assume combined. | combined (default) or standalone |
| Unit of analysis | One row = one edge | (entity_id, identifier_value, identifier_type) |
| Population | The source table, pre-filter | company_data.<table>; full row count + distinct entity count |
| Identifier columns in scope | Which columns produce edges | email, phone_e164, device_id, etc. |
| What this audit will NOT do | Explicit non-goals | not fixing the source; not re-building the graph; not changing entity-resolution rules |
If --graph-context was not passed and the downstream context isn't
clear from the user's prompt or session context, ask one
AskUserQuestion before generating hypotheses:
"Will this source be used on its own in the graph build, or UNIONed with other sources?"
- Combined with other sources (recommended default — the common case; filters get evaluated against combined-graph risk)
- Standalone — only this source feeds the graph build
- Not sure — treat as combined; it's the safer default
Record the choice; every Phase 6 filter recommendation references it.
Resolve the source. For a dataset, run narrative_datasets_search
then narrative_datasets_describe to get the schema and table
reference; for an access rule, see
references/ACCESS_RULES.md for tool
substitutions. If neither --dataset nor --access-rule was passed
and the source type is ambiguous, ask one AskUserQuestion to
disambiguate before searching.
Then delegate the base profile — row count, per-column coverage
(null/fill rate, cardinality), inferred column shapes, and quality
flags — to /profile-dataset rather than reading and interpreting
stats inline:
/profile-dataset --dataset <id> --json(or--access-rule <id>)
Use its row_count as the population baseline and the per-column
profile as identifier-column evidence for Phase 3. The
distinct-entity count and every cross-column / conditional measure
remain this skill's own work — computed in the Phase 4 hypothesis batch
and Phase 5 quantification. /profile-dataset supplies the descriptive
base population; the damage judgment is yours. If /profile-dataset is
unavailable, fall back to reading describe(include: ["…","stats", "sample"]) inline (see
references/HARNESS_FALLBACK.md).
Record the fully qualified NQL reference for the source —
company_data.<table> for a dataset or <owning_company_slug>.<rule_name>
for an access rule. Every downstream phase uses this reference as
<source>.
End this phase with a two-line summary you show the user before running any hypothesis query:
Auditing
<source>(<row_count>rows,<entity_count>distinct entities) for pre-graph data quality. Entity type:<type>. Identifier columns:<col1>,<col2>, … . Comparison threshold for "plausible per-entity activity":<derived from entity type>.
Generate a dataset-specific list of failure-mode hypotheses. Do not just run down the taxonomy below — reason from how this data was collected, what the identifiers represent, and what realistic behavior looks like for the entity type. The taxonomy is a starting point, not a checklist.
Common failure modes to check: hub identifiers, high-degree nodes,
behavioral-suspicious values, over-connected identifiers,
source-specific quirks. Full taxonomy with detection patterns:
references/FAILURE_MODE_TAXONOMY.md.
Look beyond the taxonomy. Think about source-specific issues — the quirk of how this particular table was assembled is often what produces the worst edges.
Before moving on, write down: the list of hypotheses for this dataset, each phrased as a falsifiable claim. Example:
H1:
email = '[email protected]'appears on > 0.1% of rows and bridges > 100 distinct entities. H2: top-10phone_e164values are each on > 50 distinct entities. H3: rows wheredevice_idis exactly 16 hex chars vs. 32 hex chars reference the same logical entity under two formats. H4: ...
Aim for 5–12 hypotheses on a typical dataset. Fewer than 5 usually means you stopped thinking too early; more than 12 usually means you need to consolidate.
/design-analysis for parallel testing — mandatoryFor the hypothesis-testing workload, this skill does not write
or run queries directly. Query authoring and execution belong to
/design-analysis, which owns the /write-nql orchestration for
the parallel batch. Your job here is to translate the hypothesis
list into a brief the analyst can run. (Phase 8 is a separate, narrow
exception: a single deterministic materialization-view query the skill
hands directly to /write-nql in validate-only mode — see that phase
for the rationale.)
Compose a single brief and pass it to /design-analysis. In the
brief, mark every hypothesis as a peer-level analytical spec with
no inter-hypothesis dependencies — that flags them as parallelizable,
and the analyst will issue them as one concurrent batch of
/write-nql calls per its Phase 6.
For every hypothesis, the spec passed to /design-analysis includes:
What to quantify per hypothesis (pick the slice that fits):
COUNT(DISTINCT entity_id) GROUP BY identifier_value, top-N descending.LENGTH, REGEXP_LIKE, prefix patterns; cardinality per format bucket.COUNT(1), not COUNT(*) — NQL rejects COUNT(*).Hypothesis specs pass through /design-analysis and /write-nql, both
of which apply the NQL gotchas catalog (no SELECT * / COUNT(*),
same-data-plane joins, no OR in JOINs, runnable queries wrapped in
CREATE MATERIALIZED VIEW). Stay aware of those rules when you phrase
the measures so the downstream writer doesn't have to renegotiate the
spec — the long-form rules live in the shared NQL syntax snippet and
in the narrative-knowledge-base MCP server
(/guides/nql/troubleshooting/…).
/design-analysis --dataset <id> --brief-only
Test the following pre-graph data-quality hypotheses against
<table>. Each is independent of the others; run them all in one
parallel batch and return consolidated results.
H1: <purpose, source, filters, group-by, measures, output, validation>
H2: <…>
…
If --no-parallel was passed to this skill, forward that intent to
the analyst ("run hypotheses serially for harness compatibility")
and flag in the audit report that the next run should be
parallelized.
Wait for /design-analysis to return consolidated results — one
row per hypothesis — before moving to Phase 5. Do not interpret
partial results.
For each confirmed hypothesis (the query returned evidence above any plausible-noise threshold), compute:
| Measure | Definition |
|---|---|
| Rows affected | rows matching the failure-mode filter |
| Edges affected | edges (entity_id, identifier_value) matching the filter — usually = rows in a bipartite edge build, but be precise about deduplication |
| Distinct identifiers affected | how many identifier values trigger the rule |
| Distinct entities affected | how many entity_ids touch one of those identifier values |
| Share of total rows / edges / entities | as a percentage; useful for severity ranking |
| Bridge potential (standalone) | the size of the connected component that would form within this source alone if these edges were kept — usually the salient measure for hub-style hypotheses |
| Combined-graph projection | when graph-context = combined, assess whether the per-entity behavior is realistic for the entity type. A person with 400+ identifiers is implausible regardless of whether it over-bridges this source standalone — UNIONing with other sources will propagate the bad attachment into other components. Note explicitly even when standalone bridge potential is small. Skip when graph-context = standalone. |
Quantify before recommending. An unquantified hunch is not a finding.
For each confirmed issue, propose a filter expression against the source table. Filters must:
graph-context), behavioral implausibility is itself
defensible evidence: per-entity activity that's unrealistic for
the entity type (a person with 400+ identifiers, a device
with degree > 1, a household carrying 50 phones) justifies a
filter even when standalone bridge potential within this source
is bounded — because UNIONing with other sources will spread the
bad attachment. Cite the behavioral threshold and the realistic
benchmark.Filter expression format: plain-English filter against the source table that a query writer can translate to NQL.
Worked example — hub identifier (applies under either context):
Filter
H1: exclude rows whereLOWER(email) IN ('[email protected]', '[email protected]', '[email protected]'). Impact: removes 8,421 rows (0.42%); preserves 1,997,580 rows. Removes 1 hub identifier worth 8,421 edges. Rationale: each of the listed values has > 500 distinct entities, none of which co-occur with each other on any other identifier — these are sentinels, not real bridges.
Worked example — long-tail high-degree persons in the combined-graph case (this is the canonical "looks fine standalone, dangerous when combined" pattern):
Filter
H2: exclude rows whereperson_id IN (SELECT person_id FROM <source> GROUP BY person_id HAVING COUNT(1) >= 100). Impact: removes ~5M edges (~0.2%); preserves ~2.66B edges. Rationale: the top 100 persons in this source carry 401–554 identifiers each (max: 554). Within this source alone the bridge potential is bounded because identifier_values are unique here, butgraph-context = combined— these persons will be UNIONed with edges from other sources, and a singlepersoncarrying 400+ identifiers is behaviorally implausible (a real person carries on the order of 5–20). Once combined, those over-attached persons become bridges into other components. Filter at the source.
If graph-context = standalone, the same finding would warrant no
filter — the standalone bridge potential is the only thing that
matters and it's bounded here. Always state explicitly which context
the recommendation hinges on.
If a hypothesis is disproven (the data does not support it), say so explicitly and do not propose a filter. Disproven hypotheses go in the report so the next audit doesn't re-test them blindly.
Draft the report using the template at
references/REPORT_TEMPLATE.md.
Fill in the headings + tables; the template handles structure. Order
findings by severity (rows / edges / entities affected,
descending) per the user's explicit ask. The "Recommended
clean-view NQL" block is filled in by Phase 8 — leave it as a
placeholder for now.
The findings are the audit's diagnostic deliverable; the clean-view NQL (Phase 8) is its operational deliverable. Both ship together in the final report.
The caller needs an actionable artifact, not just a list of filters in
prose. Phase 8 converts the consolidated filter set into a validated
CREATE MATERIALIZED VIEW query that anyone (operator, pipeline, or
the downstream graph-build skill) can run to produce a graph-ready
clean source.
Zero confirmed issues (every hypothesis disproven, or every confirmed issue has impact below the noise threshold you set in Phase 2): skip the NQL composition. In the report's "Recommended clean-view NQL" section, write exactly:
The source passed the audit. No materialization is required;
<source>is graph-ready as-is. Pass it to the graph build directly.
<source> is the fully qualified NQL reference recorded in Phase 2
— company_data.<table> for a dataset, <owning_company_slug>.<rule_name>
for an access rule. Stop. Do not invoke /write-nql.
One or more confirmed issues: continue to the next step.
/write-nql (validate-only)Invoke /write-nql --no-explain with the brief at
references/CLEAN_VIEW_NQL_PROMPT.md.
That reference covers the pre-flight filter consolidation (dedupe,
keep-predicate phrasing, explicit column projection), the verbatim
prompt body, and the post-flight validation loop. If the source is
an access rule, also see
references/ACCESS_RULES.md for the
invocation delta (drop --dataset, swap the source reference, adjust
the view name).
Replace the Phase 7 "Recommended clean-view NQL" placeholder with
the fill-in block from
references/REPORT_TEMPLATE.md
— the verbatim NQL plus the estimated-impact bullets (rows kept /
removed, entities preserved, hub components prevented).
The NQL block is the final hand-off. This skill does not run it, does not schedule it, and does not trigger the graph build.
references/ACCESS_RULES.md — dataset-vs-access-rule tool substitutions, NQL reference patterns, sample/stats workarounds, and Phase 8 invocation deltas. Read in Phase 2 if the source is (or might be) an access rule.references/FAILURE_MODE_TAXONOMY.md — full 8-row starter taxonomy with detection patterns and graph-quality impact. Read in Phase 3 when generating hypotheses.references/COMMON_CASES.md — per-graph-type hypothesis starters (person, device, CRM). Read when scoping the audit to confirm which failure modes to prioritize for the specific graph shape.references/REPORT_TEMPLATE.md — verbatim markdown skeleton for the Phase 7 audit report, plus the Phase 8 fill-in block for the clean-view NQL.references/CLEAN_VIEW_NQL_PROMPT.md — Phase 8 brief body for /write-nql --no-explain, pre-flight checklist, and post-flight validation loop.references/EDGE_CASES.md — no-identifier datasets, all-unique identifiers, low row counts, mixed grains, evidence-free thresholds, oversized filters, overlapping hypotheses, post-build invocations, zero-issue outcomes, unvalidatable materialization queries. Read when the dataset doesn't fit the audit's assumptions.references/HARNESS_FALLBACK.md — narrative-mcp / /design-analysis / /write-nql unavailable, AskUserQuestion fallback. Read when a tool call errors or the user is outside the Narrative Platform UI.docs/authoring-skills.md — house conventions this skill follows.plugins/narrative-common/skills/design-analysis/ — Phase 4 hypothesis-testing handoff; orchestrates the parallel /write-nql batch.plugins/narrative-common/skills/write-nql/ — Phase 8 direct call (validate-only); reached transitively via /design-analysis for Phase 4.plugins/narrative-common/skills/generate-rosetta-stone-mappings/ — run first if identifier columns aren't yet mapped.plugins/narrative-common/skills/profile-dataset/ — Phase 2 delegates the base profile (row count, per-column coverage, quality flags) here instead of reading stats inline.Silence is success. Do not call submit_feedback when the skill
worked. No "great skill!" submissions, no positive feedback, no "FYI"
notes.
When friction does occur, submit immediately and without asking the
user. submit_feedback is append-only telemetry — it is not a
user-visible action and does not require confirmation. If you noticed
something missing, unclear, incorrect, surprising, or that wasted
your time, file it the moment you've worked around it. Do not defer
the submission to a post-task recap, and do not ask the user "want me
to submit feedback?" — that's the wrong default for this tool.
One submission per distinct friction point. Submit liberally.
Fields that matter most:
skill_name: narrative-identity:triage-pregraph-data (use this verbatim).severity: info (nit) | friction (slowed you down) |
blocker (stopped you).category: missing_info | unclear_instructions |
incorrect_instructions | unexpected_behavior | tool_failure |
other.summary: one concrete line — what went wrong, not how you felt.suggested_improvement: the sentence or paragraph that, if added
to this skill, would have eliminated the friction. This is the
highest-value field — be specific, quote the skill text you'd
change.Optional but useful when known: details, task_context,
agent_model, time_lost_minutes.
Alternatives
narrative-io/narrative-skills-marketplace
Translate a fuzzy analytical question into a rigorous investigation plan. Interrogates the ask, grounds the plan in the available data dictionary, applies analytical best practices, and produces a structured brief of query specifications for a downstream query-writing skill. Plans, does not write SQL. Use when: "why did X drop", "is there a relationship between A and B", "who are our highest-value customers", "what's driving the change in Y", "investigate this trend", "design an analysis for", "
PramodDutta/qaskills
Generate optimized test combinations using pairwise (all-pairs) testing algorithms to achieve maximum coverage with minimum test cases across multiple input parameters
PramodDutta/qaskills
Gate RAG pipelines in CI with versioned golden eval sets, per-metric thresholds, baseline drift detection, and a build that fails when retrieval or answer quality regresses.
ruvnet/RuView
Comprehensive GitHub code review with AI-powered swarm coordination