Augment a Wren project with business context that DB schema cannot carry — enum value meanings, units (USD vs cents, ms vs sec), NULL semantics, magic sentinels (-1 = unknown), soft-delete default filters, business synonyms, time-grain / TZ conventions, cross-system identifiers, currency rules, canonical-table preferences, AND named aggregation metrics (ARR, churn, DAU, WAU, NRR) proposed as cubes. Runs in one of two modes selected at session start: `grill` (one question at a time, user-driven) or `auto-pilot` (agent infers and applies, escalates only on conflicts and high-blast-radius additions like new cubes / views / relationships). Reads everything under <project>/raw/ (PDFs, glossaries, handbooks, code, data dictionaries) and optionally samples low-cardinality columns from the live DB (grill mode), compares against the current MDL / cubes / knowledge (rules + NL→SQL pairs), then fills gaps via the ten-category gap catalog and the cube proposal flow. Confirmed findings are written back to the right sink. Use when: user says 'enrich context', 'augment my project', 'grill me on this project', 'auto-fill my context', 'agent doesn't understand our docs / enum values / units / null meanings', 'business context is missing', 'what does status=A mean', 'is this amount in USD or cents', 'we keep getting wrong aggregations', 'add cubes for ARR / DAU / churn', 'we have a handbook / glossary / data dictionary the agent should know'; or after generating an MDL and noticing the agent lacks business semantics.
This skill exists because most business context never lives in a DB schema — it lives in handbooks, glossaries, finance reports, support playbooks, code comments, Slack rules-of-thumb. The agent reads those raw artifacts, finds what's missing from the Wren project, and either grills the user one question at a time (grill mode) or applies its best inferences directly and hands over an audit (auto-pilot mode) before writing back. The output lands in the sinks each project already has — MDL, cubes/, knowledge/rules/, and knowledge/sql/ — no new artifact, no new tooling.
wren context validate. If it fails, revert that single change and feed the error back. Never leave a project in an invalid state.Drop into grill for three cases. Always interrupt auto-pilot and ask the user when:
knowledge/rules/ / knowledge/sql/ / cubes/).Everything else: apply directly and log to the audit list.
Before touching the project or reading any file, ask the user which mode to run in. Lock the choice for the whole session — no mid-session switching; the user re-runs to change.
Two modes for this session:
a) Grill mode — I walk every gap with you, one question at a time, proposing a draft and waiting for your accept / edit / skip. You stay in the driver's seat. Best when the raw material is sensitive, when you want to learn what I don't know about your project, or when you'd rather review than re-do.
b) Auto-pilot mode — I read raw + current context, make my best inferences, and apply them. I'll only stop to grill you on (1) conflicts between raw and existing MDL and (2) high-blast-radius additions like new metrics, views, or relationships. The session ends with a full diff + confidence-tagged inference list for you to audit.
Which? (a / b)
Remember the choice as MODE = grill | autopilot and use it to branch Steps 6 and 9.
Always ask the user which project to enrich before doing anything else — never assume cwd. A user can have several Wren projects and an ambient ~/.wren profile that doesn't match the one they want to augment.
Offer concrete hints in the question so the user can answer in one round-trip:
# Hint 1 — does cwd look like a project?
test -f wren_project.yml && pwd
# Hint 2 — does ~/.wren/config.yml point at a default project?
grep -E '^project_path:' ~/.wren/config.yml 2>/dev/nullThen ask:
Which Wren project do you want me to augment? a)
$PWD(current directory) ← if Hint 1 matched b)<path from ~/.wren/config.yml>← if Hint 2 matched c) something else — paste the absolute path
After the user answers, lock the path in for the whole session:
cd <chosen-path>
test -f wren_project.yml || {
echo "Error: <chosen-path> is not a Wren project (no wren_project.yml)."
exit 1
}
wren context show >/dev/null 2>&1 || {
echo "Error: wren context show failed — manifest may be invalid."
exit 1
}If either check fails, stop and tell the user — suggest wren skills get onboarding if it's not a project, or wren context validate if the manifest is broken.
From this point on, every command and file path in this skill is relative to the chosen project root. Do not switch projects mid-session — if the user wants to work a different project, end this session and re-run.
wren memory status 2>/dev/null | grep -q "Backend: lancedb"Writing NL→SQL pairs (wren memory store → knowledge/sql/*.md) works regardless — that
sink is always open. This checks which recall backend is active, i.e. whether the optional
memory extra is installed (LanceDB = semantic; grep = dependency-free fallback):
Backend: lancedb) → set MEMORY_AVAILABLE = true. Semantic recall and wren memory fetch are usable, and wren memory index builds an embedding index in Step 8.MEMORY_AVAILABLE = false. Skip the semantic read/index paths below; pair writeback still happens via wren memory store, and grep recall still works.From the project root (cwd is already there from Step 1):
mkdir -p rawIf you just created it (the directory was empty or new):
I've created
raw/at the project root. Drop anything you think helps explain this project's business context — PDFs, glossaries, handbooks, financial reports, data dictionaries, sample queries, code with comments, screenshots of dashboards, anything.Heads-up: the contents may be sensitive. Decide for yourself whether to commit
raw/to git — I won't touch.gitignore.Tell me when you've added the files and I'll start reading.
Wait for the user to confirm before continuing.
Read both sides — the raw material and the current Wren context — before forming any opinion.
Read every file under raw/. Use whatever capability your agent has natively (text, markdown, code, PDF). If you genuinely can't read a particular file, tell the user once which file and suggest converting it to text or pasting the relevant excerpt — then move on to the rest. Do not install extra Python packages, do not reach for new CLI subcommands.
| Source | Command |
|---|---|
| MDL (full) | wren context show --output json |
| Business rules | wren context instructions (reads knowledge/rules/ + any legacy instructions.md) |
| Existing cubes (names) | wren cube list |
| Existing cubes (measures + dimensions) | wren cube describe <cube> for each name above |
| NL→SQL pairs | read knowledge/sql/*.md directly |
| (Memory) stored pairs | wren memory list -n 200 --output json |
| (Memory) schema as text | wren memory describe |
The memory rows only matter when MEMORY_AVAILABLE = true. Reading cubes is essential before any Lane 3 metric proposal — see cube_proposals for the duplication guard.
When raw is silent on a column's enum / unit / null / magic / time semantics, the catalog's column-local categories (#1, #2, #3, #5, #7 in gap_catalog) can often be settled directly by sampling distinct values from the live DB. Read gap_catalog before this step — its Trigger column tells you which columns are probe candidates.
Default policy by mode:
| Mode | Default | How to override |
|---|---|---|
| Grill | Probe on. Before the first query, ask the user once: "I want to sample N columns with LIMIT 30 each to find enum / sentinel / time-grain values — OK?" Lock the answer for the session. | User says no → skip Step 4.5 entirely; rely on Lane 2 + Lane 3 instead. |
| Auto-pilot | Probe off. The skill never queries the live DB in auto-pilot mode. | None — user must re-run in grill mode if probe would unblock high-confidence inferences. |
Candidate selection (no DB call yet):
A column is a probe candidate when all hold:
[tag] (catalog write format).Categories #2 (unit), #4, #6, #8, #9, #10 are not probable — SELECT DISTINCT doesn't reveal units, default filters, synonyms, external mappings, currency conventions, or canonical-table preferences. Those need raw evidence or human judgment.
Probe query:
wren --sql "SELECT DISTINCT <col> FROM <model> LIMIT 30" --output json
# For magic sentinels (catalog #5), also fetch min/max:
wren --sql "SELECT MIN(<col>) AS lo, MAX(<col>) AS hi FROM <model>" --output json[tag] line and surface to user (grill) with confidence "med — probed values, semantics still inferred".Safety:
[tag] line — Universal Rule 1.Hold all three lanes in working memory. Do not write a gaps.yml.
Before sweeping, load gap_catalog — the ten business-semantic categories the schema cannot carry. Each lane consumes the catalog differently: Lane 1 walks it as type-aware mechanical triggers, Lane 2 classifies each atomic raw claim into one of the 10 categories before routing, Lane 3 seeds inference prompts when a trigger fires but raw is silent.
Scan the current MDL and check:
properties.description?primary_key?knowledge/rules/ has real content beyond the scaffold default?knowledge/sql/ has at least a few canonical NL→SQL pairs?Plus, walk every column / model against gap_catalog triggers:
[tag] line present in properties.description?deleted_at, is_active, archived_at, etc.) → is there a ## Default filters rule in knowledge/rules/ covering it (catalog #4)?users / users_v3) → is there a ## Canonical tables rule (catalog #10)?*_currency / fx_rate / external-system ID column → is the matching ## Currency (#9) or ## External identifiers (#8) section present?knowledge/rules/ or raw that don't map verbatim to model / column names → catalog #6 ## Naming conventions rule missing.Each unsatisfied check is a candidate. Combine with Step 4.5 probe results (if available) before moving to Lane 2.
For each raw file, internally extract 5–15 atomic claims — single statements that could be true or false, e.g. "an order has exactly one customer", "user means type=default by default", "ARR equals MRR × 12 minus refunds". Then for each claim, classify against the current Wren context:
| Class | Meaning | Resolution outcome |
|---|---|---|
| covered | already reflected in MDL / instructions / pairs | skip |
| partial | the topic exists but the wording / scope differs | propose tightening |
| new | nothing in current context matches | route to a sink |
| conflict | raw says A, current context says B | grill the user (both modes), but do not edit existing — surface for manual fix |
After reading raw and the current MDL, propose additions the user did not literally state in raw but that would clearly help the agent later. Examples:
quarterly_churn referenced five times in finance.pdf. No existing cube covers it. Want me to add cubes/quarterly_churn/metadata.yml with measure = COUNT(*) FILTER (WHERE churned_at IS NOT NULL) / NULLIF(COUNT(*), 0)?" — see cube_proposals for the YAML template and duplication guard.core users without defining it. Is this users WHERE tier = 'premium'? Want me to make a view?"events.payload is JSON but the column has no description — let me draft one."For any aggregation-shaped proposal (SUM, COUNT, AVG, "by month / by status / per customer" patterns), default to a cube. Run wren cube list + wren cube describe first to confirm no existing cube already covers the measure expression; if one does, skip the proposal and add a knowledge/sql/ example pointing at the existing cube instead. The full decision tree, naming rules, and validation flow live in cube_proposals.
In grill mode, open every Lane 3 question with "I'm guessing — ". In auto-pilot, tag the audit entry with agent inference so the user sees you extrapolated.
Branch on the MODE locked in Step 0.
Use this conversational pattern for every gap surfaced in Lanes 1–3:
Interview the user relentlessly about every gap until we reach a shared understanding. Walk down each branch of the decision tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer.
Ask the questions one at a time.
If a question can be answered by exploring the codebase or the raw files, do that instead of asking.
For every grill turn:
knowledge/rules/ as a rule" / "I'll add this to the users model description in MDL").When the user gives a curve-ball answer ("actually we don't track that") — pivot. The goal is shared understanding, not pushing a pre-built list.
Process every finding from Lanes 1–3 directly — except for the three escalation cases from Universal Rule 7:
For everything else (Lane 1 mechanical fixes, Lane 2 unambiguous new claims, Lane 3 low-impact description tweaks):
wren context validate immediately after any MDL edit. On failure: revert the single change and log the revert with the error.agent inference for Lane 3, structural for Lane 1).Auto-pilot does not pause for confirmation on each item — the user reviews the full diff + audit list in Step 9. They are the reviewer, not the gatekeeper.
Decide the sink as part of the proposal (Step 6.3 in grill mode; Step 6.2 in auto-pilot), so the user can correct routing in grill mode and audit it in auto-pilot.
| Finding type | Sink | How to write |
|---|---|---|
| Schema structure / relationship / view / model or column description | MDL YAML under models/, views/, relationships.yml | Edit the YAML file directly. For catalog #1 / #2 / #3 / #5 / #7 / PII, append a [tag] line to properties.description (prose first, then one tag per category). See gap_catalog for the exact tag format and triggers. |
| Aggregation metric / named measure (with measures + dimensions) | cubes/<name>/metadata.yml | New file per cube. Default sink for any SUM / COUNT / AVG / ratio metric raw defines or Lane 3 infers. See cube_proposals for the YAML template, naming policy, duplication guard, and validation flow. Run wren context validate + wren cube query --cube <name> --sql-only after writing; revert on either failure. Always escalates to grill in auto-pilot (Universal Rule 7b). |
| Default filter / implicit rule / business convention / naming convention / external mapping / currency / canonical table | knowledge/rules/ | Append under the catalog-specified ## section heading (#4 → ## Default filters, #6 → ## Naming conventions, #8 → ## External identifiers, #9 → ## Currency, #10 → ## Canonical tables) inside a topic file under knowledge/rules/ (e.g. knowledge/rules/conventions.md). Create the file/heading if absent; never modify existing text. |
| NL→SQL example (canonical or ad-hoc) | knowledge/sql/ | wren memory store --nl "..." --sql "..." --tags "source:enrich" — writes knowledge/sql/<slug>.md (committable) and indexes it when the extra is present. Works whether or not MEMORY_AVAILABLE. |
Catalog-driven routing means every column-local proposal goes to the column's properties.description with a [tag] line; every cross-model rule goes to knowledge/rules/ under a fixed heading. This keeps re-enrichment deterministic (greppable) and avoids inventing new sink locations.
wren context validateIf it fails:
wren memory store; each becomes a knowledge/sql/<slug>.md with YAML frontmatter (nl, sql, source, optional datasource/tags). Don't hand-write the files — use wren memory store --tags "source:enrich".primary_key, is_calculated, not_null). wren context build converts to camelCase for target/mdl.json.knowledge/rules/ holds free-form markdown, one file per topic. Group rules by topic with ## headings.After Step 6 ends (user says stop in grill mode, or every finding is processed in auto-pilot):
wren context buildThis recompiles target/mdl.json from the YAML edits.
If MEMORY_AVAILABLE = true:
wren memory indexThis rebuilds the index from the MDL schema items and the knowledge/sql/ pairs. (Business
rules in knowledge/rules/ are read by wren context instructions, not embedded by index.)
Print a tight session report:
Wren Enrich Context — session summary (mode: <grill|autopilot>)
Added:
MDL : N model descriptions, N column descriptions, N relationships, N views
by tag: [enum]=N [unit]=N [null]=N [magic]=N [time]=N [pii]=N
cubes : N new (names: <list>) via cubes/<name>/metadata.yml
knowledge/rules/ : N new rules across sections
by section: Default filters=N | Naming conventions=N | External identifiers=N | Currency=N | Canonical tables=N
knowledge/sql/ : N new NL→SQL pairs via wren memory store
Probe : N columns sampled, M failed (grill mode only)
Please fix manually (we don't edit existing fields):
- models/orders/metadata.yml: existing description seems to contradict raw/glossary.pdf p.3
- relationships.yml: existing orders↔customers is MANY_TO_ONE but raw/data_dict.md p.7 says MANY_TO_MANY
- …Append:
Skipped this session: N gaps (re-run /wren-enrich-context to revisit)Append a detailed audit so the user can sanity-check inferences:
Inferred items (please review):
high | MDL model:orders.description | from raw/glossary.pdf p.2 — "Order = ..."
high | knowledge/rules/ rule | from raw/handbook.md §4 — "default tier ..."
med | MDL column:users.signup_source.desc | agent inference from raw/onboarding.md
low | knowledge/sql/: "weekly active customers" | agent inference, no direct raw evidence
Validation:
K successful applies, M reverted after wren context validate failed:
- relationships.yml: <error> → reverted
Escalated to grill (raw vs MDL conflicts / high-impact additions):
- <count> items — see grill transcript aboveThe user should be encouraged to skim the audit and either accept it as-is, manually tweak low-confidence rows, or re-run in grill mode if they want to revisit interactively.
gaps.yml, state.yml, or any other tracking artifact. The session lives entirely in conversation.knowledge/rules/ rule, or knowledge/sql/ pair — only append / add. Surface mismatches on the manual-fix list.pypdf, docling, …) to read raw. Use what your agent already has; ask the user to convert files you can't open.agent inference (auto-pilot).wren memory store works whether or not MEMORY_AVAILABLE — it always writes the knowledge/sql/*.md pair (and indexes it only when the extra is present). No need to fall back to another sink.wren context build after every single MDL edit — once at the end is enough. Do run wren context validate after every edit.raw/ was created by wren context init — it isn't. This skill creates it.[tag] line if the same category tag already exists for that column — Universal Rule 1. Surface contradictions on the manual-fix list instead.knowledge/rules/ section headings. Stick to the five catalog-defined headings (## Default filters, ## Naming conventions, ## External identifiers, ## Currency, ## Canonical tables). Anything that doesn't fit goes on the manual-fix list.base_object — write a knowledge/sql/ example pointing at the existing cube instead. See cube_proposals duplication guard.metrics: entry that already covers the same logic. Surface as "consider migrating to cube" on the manual-fix list.wren cube query --cube <name> --sql-only after creating a cube. Structural wren context validate doesn't catch unresolvable measure / dimension expressions.gap_catalog — the ten business-semantic gap categories, with triggers, default sinks, and write formats. Read this before Step 4.5 and Step 5.cube_proposals — decision tree for when to propose a cube vs view vs calculated column, the cube YAML template, naming policy, duplication guard, and validation flow. Read this before any Lane 3 aggregation-shaped proposal.7830cc7
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.