The Farewell Gift: What We Shipped on the Last Day of the Engagement
The most useful thing we left behind wasn't documentation — it was a starter repo that let the team use Claude Code safely on day one after we left.

Key Takeaway
On the last day of a five-month engagement, we shipped a git repo with a CLAUDE.md template, hook examples, and a best-practices doc. Not because documentation helps. Because a starter kit lets a new team use AI assistance safely from day one — without rebuilding the guardrails from scratch.
On the last day of a five-month engagement, I shipped a repo that had nothing to do with the migration we'd been hired to deliver.
No new dbt models. No dashboard fixes. No data quality patches. Just a git repository with a README, a CLAUDE.md template, and a handful of hook examples. It took about four hours to put together. It was the most useful thing I left behind.
The insight that prompted it: the cloud security company's data team had watched us use Claude Code across the engagement — seen the guardrails in action, seen what the structured setup made possible, seen the difference between sessions that opened with good context and sessions that didn't. They were going to try to adopt it after we left. And without the framework, they'd spend the first three weeks making the same mistakes we'd already made and moved past.
You can't document your way out of that problem. A handoff doc explains what the guardrails are. A starter repo makes it possible to actually use them.
That's what we built. And it went through three versions before it was ready to hand over.
When analytics teams take over a codebase from a consultant, the standard deliverable is a handoff document. Repo overview. Architecture notes. How to run the models. How to update the seeds. A section on "things to watch out for."
These documents are written by people who know the system deeply, for people who don't know it yet, in a medium that can't be queried. They age immediately. By the time the new team hits a real problem — a discount priority conflict, a JOIN that produces silent duplicates, a pricing tier that changed without documentation — the document answers the question they asked three weeks ago, not the one in front of them now.
AI-assisted workflows make this worse before they make it better.
When the team starts using Claude Code on a codebase they didn't build, the AI will reconstruct the domain logic from model names, column comments, and general dbt patterns. Sometimes it gets it right. Sometimes it generates plausible SQL that violates a business rule nobody wrote down — a constraint that was obvious to everyone who built it and invisible to everyone who didn't. The mistake surfaces in a Finance review two sprints later, and by then the trail back to the root cause is cold.
The handoff problem for AI-assisted workflows isn't documentation. It's that the AI needs different inputs than a human does. A human reads the README and asks questions. An AI reads what's in the repo and generates from it. If the guardrails aren't in the repo — if CLAUDE.md doesn't exist, if the hooks aren't configured — the AI operates on defaults, and defaults don't know your fiscal calendar or your pricing rules or which macros encode multi-year business logic that must not be touched.
A starter kit solves a different problem than a handoff doc. The doc explains the system. The kit makes it possible to use the system safely with AI assistance, starting on day one.
The three versions
v1: The engagement-specific setup
The first version was built for us, not for them.
By week two of the engagement, CLAUDE.md had become the most important file in the repo. Not because we planned it that way — we'd gone back and filled it in after the sessions that went badly, after we learned what Claude needed to be told versus what it would figure out on its own. The file accumulated. Rules got added as we found the holes. Domain context filled in as we understood the business logic well enough to write it down.
By the end of the engagement, v1 was dense. It encoded everything specific to this project:
## Role & Mission
You are a Staff Analytics Engineer working on:
- Migrating legacy BI dashboards to dbt + Snowflake + Sigma
- Starting with ~20 revenue views
- Keeping 100% logical parity first (lift-and-shift) before any refactor
## ABSOLUTE RULES
1. NEVER modify anything under the legacy dashboard directory. Read/Grep/Glob only.
2. NEVER run destructive Snowflake commands (DROP, TRUNCATE, ALTER TABLE ... DROP)
3. NEVER run dbt run without --select. Prefer narrow: dbt run --select fct_revenue_* --target dev
4. NEVER change pricing or billing macros without detailed comments + human approval.
5. For monetary amounts: NEVER use FLOAT. Always NUMBER(38,9).
## Domain Context
pricing_tier maps to contractual commitments, not usage bands.
fiscal_quarter uses a 4-4-5 retail calendar. Do not assume ISO quarters.
client_id in billing tables ≠ account_id in CRM. Joining on the wrong key = silent duplicates.
Regional multipliers apply to one product line only. The other uses a global flat rate.
Quarterly model output lags by one month by design — this is correct, not a bug.
V1 also had the full hook setup: pre-commit hooks blocking DROP and TRUNCATE in model files, a pre-tool-use hook preventing Claude from editing the protected macro directory without explicit confirmation. That protection was non-negotiable — those macros encoded pricing and business logic that predated the current team by years.
This version was useful. It was also completely non-transferable. The role definition referenced the specific migration context. The domain context referenced client-specific keys and product names. The protected paths were hardcoded to this repo's structure. You couldn't hand this to a new team and tell them to adapt it. You'd have to rebuild it from scratch, which is the problem we were trying to solve.
v2: The generalized playbook
The second version lives in Clarivant's own org. It's what we take into new engagements.
The goal was to strip everything project-specific while keeping the structural patterns intact. That turned out to be a useful forcing function: it made us articulate which parts of v1 were engagement logic versus which were the framework underneath the engagement logic.
The answer was cleaner than expected. Three categories of content belonged in any analytics engineering CLAUDE.md, regardless of the specific project:
## Architecture Rules
[Fill in your layer structure: staging → intermediate → marts or equivalent]
[Fill in your naming conventions]
[Fill in your materialization defaults]
Never run dbt run without --select. Full-refresh on prod is a warehouse billing event.
## Hard Boundaries
Never use FLOAT or DOUBLE for financial columns. Always NUMBER(38,9) or equivalent.
Never edit [your protected macro directory] without detailed comments + human approval.
For any model touching revenue, billing, or pricing: pause and request human confirmation.
[Add your environment-specific constraints here — Snowflake native, BigQuery, etc.]
## Domain Context
[This section is intentionally blank — fill in the specifics of your project]
[What does your pricing structure look like?]
[What are the non-obvious key relationships that cause silent duplicates on JOIN?]
[What fiscal calendar do you use?]
[What are the legacy models that must remain read-only?]
The hooks section became a template with commented examples:
# Customize the path list for your project
PROTECTED_PATHS = [
"macros/fiscal/", # date conversion definitions
"macros/pricing/", # pricing engine macros
"seeds/pricing_tiers.csv", # contractual tier structure
]
def hook(tool, tool_input):
if tool == "edit":
path = tool_input.get("path", "")
for protected in PROTECTED_PATHS:
if protected in path:
raise Exception(f"Protected path: {protected} requires manual review")
V2 is what we use. The domain context section is the blank that gets filled in on day one of each engagement. Everything else — the architecture templates, the financial precision rules, the hook patterns — transfers intact.
v3: The farewell gift
The third version was the one that required the most thought about the audience.
V1 was for us. V2 is still for us — it's our internal playbook. V3 was for a team that hadn't built any of this, that hadn't spent five months learning why each rule existed, that would be using Claude Code independently after we left.
That's a fundamentally different design constraint. A team that didn't build the system needs three things: low barrier to entry, enough explanation that the rules make sense without the backstory, and documentation of what to fill in versus what to leave alone.
The README became the primary artifact:
# Claude Code Starter — Getting Started
## What's in this repo
- CLAUDE.md — AI session instructions for your dbt project
- hooks/pre_commit.sh — blocks destructive SQL before it lands in the repo
- hooks/pre_tool_use.py — intercepts Claude before it edits protected paths
- best-practices.md — lessons from production use: when to use what, and why
## Setup (5 steps)
1. Copy CLAUDE.md to the root of your dbt project
2. Fill in the [YOUR PROJECT] sections (see comments inline)
3. Copy hooks/ to your project's .claude/ directory
4. Update PROTECTED_PATHS in pre_tool_use.py to match your macro structure
5. Start a Claude Code session — it reads CLAUDE.md automatically
## The one rule you must not skip
Fill in the Domain Context section before your first session.
This is the section Claude cannot infer from your schema.
It takes 30 minutes and prevents the class of mistakes that take days to debug.
The v3 CLAUDE.md template had clearer inline comments — not just what to put in each section, but why the section exists and what goes wrong if it's empty:
## Domain Context
# -------------------------------------------------------------------------
# This section is the most important one in the file.
# Claude can infer your dbt layer structure from your model names.
# It cannot infer your fiscal calendar, your non-obvious key relationships,
# or which product lines use different pricing rules.
#
# Things to document here:
# - Fiscal calendar: does your quarter match ISO? When does FY start?
# - Key relationships: any IDs that look the same but mean different things
# - Pricing rules: which product lines use which rate structures
# - Legacy constraints: read-only files, tables to never touch, things that
# "look wrong but are actually correct" in your data model
#
# Leaving this blank is valid for a new greenfield project.
# For a migration or an existing system, this is where you earn your money.
# -------------------------------------------------------------------------
[Fill in your domain context here]
The best-practices doc was the last addition — a short write-up of lessons from production use that didn't fit anywhere else. Not a tutorial. Not a reference doc. More like the things you'd tell a new team member on their first day if you had 20 minutes.
What got kept, stripped, and simplified
The clearest way to show the distillation: this is how the domain context section evolved across versions.
v1 — full project-specific context (verbatim pattern, anonymized):
## Domain Context
pricing_tier maps to contractual commitments, not usage bands.
fiscal_quarter uses a 4-4-5 retail calendar. Do not assume ISO quarters.
client_id in billing != account_id in CRM. Wrong join key = silent duplicate rows.
Regional multipliers apply to product line A only. Product line B uses a global flat rate.
Quarterly output lags one month by design — correct, not a bug.
The legacy dashboard repo is read-only. May grep/glob, never edit.
v2 — template with fill-in prompts:
## Domain Context
[What does your pricing tier structure map to? Usage bands? Contracts?]
[What fiscal calendar do you use? 4-4-5? ISO? When does your FY start?]
[Are there ID fields that look the same but join to different entities?]
[Do different product lines use different rate structures?]
[Is there any "looks wrong but is intentional" behavior the AI should know about?]
[What are the read-only legacy sources, if any?]
v3 — same template, with a worked example added:
## Domain Context
# Example (fill in your own):
# pricing_tier maps to [contractual commitments / usage bands / seat count]
# fiscal_quarter uses [4-4-5 / ISO / custom] — FY starts [month]
# [id_field_a] in [table_x] != [id_field_a] in [table_y] — join on [correct_key]
# [product_line_a] uses [rate structure]; [product_line_b] uses [different rate structure]
# [Known legacy behavior that looks wrong but is correct]: [description]
[Your domain context here]
The worked example does a lot of work. It tells a new team member not just what the format is, but what kind of thing belongs in each placeholder — which is the information that's actually hard to figure out on your own.
The hook setup followed a similar arc: v1 was hardcoded to the project's paths, v2 became a template with comments explaining the mechanism, v3 added a five-minute setup walkthrough so someone who'd never touched Claude Code hooks could get them running without reading the source.
What the distillation taught us
A few things that surprised me about the process.
The hardest part of v3 wasn't the code. It was the explanation.
V1 and v2 were written for people who understood why each rule existed. We'd lived through the sessions where the rule was absent and paid the price. The v3 audience hadn't. Writing the inline comments for v3 forced me to articulate the cost of skipping each section — not just "fill this in" but "this is what goes wrong if you don't." That's a different kind of documentation. It's harder to write and significantly more useful to read.
The domain context section is the irreducible human contribution.
Everything else in the framework transfers: the hook patterns, the financial precision rules, the architecture conventions, the session management workflow. The domain context cannot be generated from the codebase. It has to come from the people who built the business logic — the ones who know why the fiscal calendar lags by a month, which ID fields look identical but map to different entities, what the pricing rules actually mean as opposed to how they're implemented. No amount of schema introspection recovers that. You have to write it down.
This is the insight that sits underneath the whole framework: Claude Code is excellent at working within a well-defined context. It cannot define that context for you. The 30 minutes you spend writing the domain context section before a sprint is worth more than a dozen prompt improvements during it.
Three versions is the right number for this kind of distillation.
V1 is specific and immediately useful. V2 is the extracted pattern — useful for reuse but abstract. V3 is adoption-optimized: it gives back enough concrete examples that a new team can start without understanding the full history. Skip v3 and you hand over a template that looks complete but requires too much inference to use well.
This maps to a general pattern: there's always a gap between "technically reusable" and "actually adoptable." V2 is technically reusable. V3 is adoptable. The difference is the worked examples and the explained rationale. It costs four hours. It determines whether the framework gets used.
The guardrails are most valuable right after the consultant leaves.
That sounds obvious in retrospect. But the timing matters more than I'd expected. The moment the engagement ends is exactly when the team is on their own with a system they didn't build, reaching for AI assistance to answer questions they'd previously asked the consultant. If the guardrails aren't in place on day one of that transition, the team's first independent Claude Code sessions run on defaults — and defaults don't know the fiscal calendar or the protected macros or the domain constraints we spent five months documenting.
The v3 starter kit was designed to close exactly that window. Clone the repo, run the five-step setup, start the first session with the domain context already loaded. No gap.
The most expensive handoff failures aren't the ones where the documentation is wrong. They're the ones where the documentation is technically correct but the new team doesn't know what it means.
The v3 starter worked because it was designed to be understood by someone who hadn't built it. That required a different kind of writing: not "this is what we did" but "this is what you'll hit if you skip this step." Less impressive to write. More useful to receive.
The team adopted Claude Code independently after the engagement ended. Not because the framework was sophisticated. Because it was simple enough to actually use.
We write about the guardrails system in detail — CLAUDE.md structure, hook implementation, and the context management approach — in AI-Assisted Analytics Engineering: How Claude Code Changed Our dbt Workflow. The engagement that produced these guardrails is documented in the Cloud Security Company case study.
If you're bringing AI coding tools into your data team and want guardrails that survive after setup, we can build the starter kit for your stack. Let's talk.
Topics
Arturo Cárdenas
Founder & Chief Data Analytics & AI Officer
Arturo is a senior analytics and AI consultant helping mid-market companies cut through data chaos to unlock clarity, speed, and measurable ROI.


