Agentic Coding for PPC: Claude Code Setup
Claude Code can take on much of the technical work behind marketing: processing data in BigQuery, building AI systems that recommend changes, running analyses, and creating the frontends your team uses. Getting it to write that code is the easy part.
The hard part, with live data and budgets, is deciding what done means and checking that the result meets it. My setup is a skill loop: intent, spec, build, tests, review and close-off. The agent repeats the build and checks until they pass, then records what changed so the next turn starts with the right context.
#1 Global and project instructions
Claude Code reads instructions at both the global and project level. The global one, ~/.claude/CLAUDE.md, holds the method, the same in every repo: the change lifecycle, the standard Python stack, and how the agent writes to me. The project one holds what only that repo knows: the real commands, the architecture boundaries, and the invariants that corrupt data if ignored. A teammate or a CI agent that opens the repo reads the same file.
Shared project instructions and configuration live in git so their changes can be reviewed. Personal overrides and credential files stay out of that history.
Anthropic’s guidance for the file gives the reason to keep both short: a bloated file makes the model ignore the instructions that matter. Three questions decide what goes in.
- Can the model get it from the code, the configs or tool output? Leave it out.
- Is it true in every repo? It goes in the global file or a skill. “Write the test before the code” is method;
make test-bqis a fact. - Can code enforce it? Then the code does (a Makefile default, a test named for the rule), and the file keeps one line that names the check.
Trimmed, from a pipeline repo:
# Paid Search Pipeline > Index, landmines and thresholds for THIS repo.> The change lifecycle lives in the global CLAUDE.md. ## Before starting work- Read tasks/todo.md, then run make test. ## Commands and test tiers- make test: mocked, what CI runs.- make test-bq: real BigQuery, before any SQL or schema commit. ## What NOT to do- Do not run a pipeline without --dry-run unless asked. The Makefile enforces this.- Do not change a BigQuery schema without DDL in sql/ddl/.Present tense throughout; history lives in git and the specs. The project file points the agent to the docs it needs before starting work.
The docs beside it carry what the code cannot. The agent can read the code to see how it behaves. It cannot recover why the thing exists, what counts as done, or what must never break. That is the context engineering in the project setup: giving Claude the reasons and constraints it needs to work on the code.
I give each file a job:
| File | What it tells Claude |
|---|---|
README.md | What the project does and how to start it |
ROADMAP.md | Why it exists, where it is going and what is out of scope |
tasks/todo.md | What is in scope now and what to work on next |
tasks/specs/ | The intent, approach and success criteria for each change |
docs/ | How it works, how to test it and what can go wrong |
docs/decisions/ | Why an approach was chosen, in an ADR (a short architecture decision record) |
That gives a new session the project’s intent and current work to read, so “let’s continue” can pick up where it left off.
Codex works much the same way and uses AGENTS.md for project instructions. You can have that file point to CLAUDE.md, or keep shared rules in AGENTS.md and have Claude read it too. Check each tool’s global instructions, permissions and connections so the workflow is consistent in both.
Many lines in my global CLAUDE.md work around something the model could not do when I wrote them. Periodically I ask what goes wrong if I delete each one. If nothing changes, it goes. If I do not know, I compare a run with the instruction against a run without it. Facts about my world and preferences about how the model writes to me stay.
#2 Tools, skills and permissions
Tools
I write data code in Python and run it with uv. The bq CLI lets Claude Code inspect BigQuery tables, run queries and check the results directly. I also use gcloud and git. Through MCP, Claude Code can reach Google Sheets and Google Docs, Jira and Confluence through Atlassian Rovo, and Figma for design. A tools file in ~/.claude/ lists what each repo can use and where authentication is managed.
Skills
A skill is a named instruction file the agent loads when the job calls for it. I keep reusable skills installed globally so the same workflows are available in every repo. The global CLAUDE.md tells the agent when to use them; the project supplies its own commands and constraints.
Every skill description sits in the prompt on every message, so each is one plain line naming its job. A skill that must fire unasked uses a specific trigger, such as a hand-written UPDATE or DELETE on a production table the team reads. I turn off skills I stop using.
Where a step can be code, the skill points to that code. My production-table skill sends each change through a module that snapshots the rows, checks the counts before and after, and rolls back on a mismatch.
Sandboxing and permissions
Auto mode reviews actions automatically so routine work can continue without permission prompts. I pair it with sandboxing, a boundary enforced by the operating system around shell commands. It limits where those commands can write and which network destinations they can reach; sensitive reads need their own restrictions. I check the active session’s permissions and keep exceptions narrow.
The shell sandbox does not cover every tool. Claude’s file tools have their own permission rules, and MCP connections use the permissions of the connected account. I check what each connection can read, send or change and keep access as narrow as the job allows. Credentials stay in saved logins or a secret store, outside chat and git. I agree in advance which shared systems the agent may change and what it may publish.
Access controls limit what a mistake can touch. Tests and review check whether the work is right.
#3 Development lifecycle
My global CLAUDE.md names the global skills in order and tells Claude Code to follow this process for every build. The sequence is part of its starting instructions, so I do not have to request each skill separately. Each project supplies the commands and checks for its own code.
| Stage | Skill and purpose |
|---|---|
| Intent | interview-me: clarify the goal, constraints and risks |
| Spec | spec-writer: agree the approach and acceptance checks |
| Build | test-driven-development: build the change in tested steps |
| Tests | Run the project’s tests, lint and dry runs |
| Review | /code-review: examine the change and its failure cases |
| Close-off | close-off: report the evidence, refresh docs and capture follow-ups |
How much I do at each stage depends on the change. I can clarify criteria, discuss the design, inspect an intermediate result or redirect the build throughout the loop.
Intent
interview-me turns a request into written intent before code changes. It asks what result I need, what is in scope, what could go wrong and how we will check the result. Questions that do not depend on another answer come together; later rounds follow from what we settle. I confirm the intent before it becomes a plan.
The interview comes before file edits, even small ones, unless I explicitly ask to skip it. What scales with risk is the detail after: a production write needs safeguards, a dry run and an adversarial review; a label change needs a much smaller agreement about the result.
Spec
spec-writer turns the confirmed intent into a spec: the goal, scope, chosen approach, success criteria and safeguards. The criteria have to be checkable by a test, command or query. It opens with a plain-English summary I can read in a minute and push back on. I do not use plan mode: I confirm the criteria before the build and review the finished work against them. The spec is updated as the work changes, and the builder ticks each criterion as it is met.
A success criterion reads “the export has 100 rows with no nulls in the action column”, not “the export works”. The criteria are fixed before the code exists, because once code runs it is easy to convince myself it is correct on the grounds that it runs.
Safeguards name what the change could silently break: the spend definition stays identical across the SQL files that use it, and a dry run never writes. They become the review’s checklist, and the criteria that hold become tests in CI.
Build
test-driven-development turns the spec into small code changes. For a logic change, it starts with a test that fails because the required behaviour is missing. For a bug fix, the test reproduces the bug. Then it writes the least code that makes the test pass, cleans it up and runs the checks again.
That repeats within the larger lifecycle. A failed check sends the work back into the build. I can work through a design choice, inspect the output or ask for a different approach while it is being built. An unexpected choice needs discussion before it becomes an assumption in the code.
Tests
The build has tests for the change itself; this stage runs the project’s checks against the whole result. The report includes the actual results and names any check the agent could not run.
Technical. Does data flow through the pipeline, does the parser handle every format the LLM returns, does a malformed row fail cleanly. The agent writes and runs these: pytest with mocked APIs and ruff are the gates, and mypy runs as a warning.
The mocked tier runs in CI. Nothing runs the real-database tier on a clock, which makes it the one most likely to rot, so it runs by hand before any commit that touches SQL or a schema. The third check is --dry-run: the full pipeline against the real dataset, model calls included, with the final write skipped.
Domain. Do the changes a pipeline recommends match what I would do reading the same data myself. That check is mine, by hand, and it goes into the spec as a success criterion.
Evals. Tests verify the code; evals verify the AI output. A gold set of cases with known right answers scores every prompt change, and a change ships when the score holds. Negative Keyword Automation with AI walks through the gold set.
Every check runs as its own command and gets read before anything acts on it: npm test | grep Tests && git commit asks grep whether the tests passed, and grep only knows whether it found some text.
Unattended work also needs the agent to be able to do the whole job: start the app, drive it, read its logs, see the change working without me. If it cannot, that gets fixed before the feature. The most useful sentence it hands back is “I could not verify X because I cannot do Y.”
Frontend checks
For a frontend change, feedback points at the screen. An annotated screenshot covers most of it, and a Figma frame over MCP works in both directions. When a choice is visual, the agent renders every option on one published page, on fictional data, and I pick by letter. It also screenshots its own work: rendering a mockup headlessly and reading the pixels caught two styling bugs and a wrapping problem that reading the code did not. A skill, check-page-in-browser, runs that check after every change to a page. It opens the page at phone and desktop width, in light and dark, and fails on a console error, a page wider than the phone screen, a file that did not load, or layout shift above 0.1, the top of Google’s good range (CLS). The agent then reads the four screenshots for what the numbers miss.
The last check is using it: I walked every button before anyone else saw it, and a per-keystroke typing bug slipped past every green browser test and showed on the first word I typed.
Review
/code-review examines the change against the spec and its safeguards. For anything with a spec the review is adversarial, either through this command or a workflow where a separate skeptic agent tries to refute each finding. In one session that pass confirmed seven findings in a change about to be committed, including a live-run switch that went live when set to 0.
The skeptic pass misses things too. On one sweep of instruction files, three findings survived it and each died on a single re-measurement: a folder an agent twice reported as deleted was sitting there, a count of eleven was two, a measured number was off by a quarter. Nothing an agent reports gets repeated until one command has re-measured it.
Close-off
close-off gathers the evidence: test and lint results, dry runs, counts and screenshots where they apply. It updates the spec’s checkboxes, refreshes outdated docs, puts follow-ups in the backlog and reports anything it could not verify. That gives me a concrete result to read and decide what can ship.
It also leaves the right context for the next change. A repo fact goes into that repo’s CLAUDE.md, a way of working into the global file, and a reusable step into a skill. The check covers the repo as it stands, because a doc can still point to a retired spreadsheet with every test green. Those updates feed the next interview.
There are plenty of other skills you can use around this loop. One of my favourites is retro. It looks back over recent sessions for repeated mistakes and missed checks, then suggests improvements to the tests, instructions, skills and tools.