Onboarding with an AI Agent
Agent skills ship with Premium installations (v2.2.10+).
The installer builds your tables; onboarding is everything after that - deciding which custom parameters to extract, which modules to switch on, whether you want a daily schedule. You can do it by hand with First Configuration, or you can let an AI assistant walk you through it using the ga4df-onboarding skill that ships in your workspace.
The assistant reads your actual installation, proposes a plan, asks for your decisions in batches, and applies only what you approve.
What you need​
- A finished GA4Dataform Premium installation (the installer has run and built tables).
- One of: Claude Code, Cursor, Codex CLI or Gemini CLI.
- A local copy of your workspace - see the next step.
- For BigQuery checks: the
bqcommand-line tool, authenticated (gcloud auth login).
Step 1: Get the workspace onto your machine​
If your Dataform repository is connected to GitHub or GitLab, just clone it - nothing special needed:
git clone <your-repository-url>
If the installer created the repository for you (the default), it has no git remote and cannot be cloned. Copy it through the Dataform API instead, with the script below.
Save it as fetch_dataform_workspace.py, then run:
python3 fetch_dataform_workspace.py <PROJECT> <LOCATION> <REPOSITORY> <WORKSPACE> <DEST_DIR>
Every value comes from the URL of your workspace in the Google Cloud console:
https://console.cloud.google.com/bigquery/dataform/locations/<LOCATION>/repositories/<REPOSITORY>/workspaces/<WORKSPACE>/files/?project=<PROJECT>
A typical installation is around 170 files and copies in a few seconds. The script needs Python 3 (standard library only) and a gcloud account with the Dataform Viewer role on the project; set GCLOUD_ACCOUNT=<email> if your default gcloud account is a different one.
fetch_dataform_workspace.py
#!/usr/bin/env python3
"""Mirror a hosted Dataform workspace to a local folder via the Dataform API.
Usage: fetch_dataform_workspace.py <project> <location> <repository> <workspace> <dest_dir>
Writes a .ga4df-sync.json manifest into <dest_dir> recording the connection
details and a hash per file - push_dataform_workspace.py uses it to detect
local changes and remote conflicts.
Auth: uses your active gcloud account (set GCLOUD_ACCOUNT to pick another
credentialed one). The account needs the Dataform Viewer role or higher on
the project.
"""
import base64
import hashlib
import json
import os
import subprocess
import sys
import time
import urllib.parse
import urllib.request
from concurrent.futures import ThreadPoolExecutor
if len(sys.argv) != 6:
sys.exit(__doc__.strip())
project, location, repo, workspace, dest = sys.argv[1:6]
account = os.environ.get("GCLOUD_ACCOUNT")
cmd = ["gcloud", "auth", "print-access-token"] + (
[f"--account={account}"] if account else []
)
try:
token = subprocess.check_output(cmd, text=True).strip().splitlines()[-1]
except subprocess.CalledProcessError:
sys.exit(
"Could not get a gcloud access token. Run 'gcloud auth login' first, "
"or set GCLOUD_ACCOUNT to a credentialed account."
)
WS = (
f"https://dataform.googleapis.com/v1beta1/projects/{project}"
f"/locations/{location}/repositories/{repo}/workspaces/{workspace}"
)
def get(url):
req = urllib.request.Request(url, headers={"Authorization": f"Bearer {token}"})
try:
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read())
except urllib.error.HTTPError as e:
if e.code == 403:
sys.exit(
f"Permission denied on {project}. Your account needs the "
"Dataform Viewer role (or higher) on this project - or set "
"GCLOUD_ACCOUNT to one that has it."
)
raise
def list_all_files():
files, page_token = [], None
while True:
url = f"{WS}:searchFiles?pageSize=1000"
if page_token:
url += f"&pageToken={urllib.parse.quote(page_token)}"
data = get(url)
for entry in data.get("searchResults", []):
if "file" in entry:
files.append(entry["file"]["path"])
page_token = data.get("nextPageToken")
if not page_token:
return files
def fetch_file(path):
data = get(f"{WS}:readFile?path={urllib.parse.quote(path, safe='')}")
content = base64.b64decode(data.get("fileContents", ""))
local = os.path.join(dest, path)
os.makedirs(os.path.dirname(local), exist_ok=True)
with open(local, "wb") as f:
f.write(content)
return path, hashlib.sha256(content).hexdigest()
start = time.time()
files = list_all_files()
print(f"listed {len(files)} files")
with ThreadPoolExecutor(max_workers=8) as pool:
hashes = dict(pool.map(fetch_file, sorted(files)))
manifest = {
"project": project,
"location": location,
"repository": repo,
"workspace": workspace,
"fetched_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"files": hashes,
}
with open(os.path.join(dest, ".ga4df-sync.json"), "w") as f:
json.dump(manifest, f, indent=2, sort_keys=True)
print(
f"done: {len(files)} files in {time.time() - start:.1f}s -> {dest}\n"
"Push local edits back later with: push_dataform_workspace.py "
f"{dest} --message 'your commit message'"
)
.ga4df-sync.json fileThe copy contains a .ga4df-sync.json manifest recording where it came from and a hash per file. Sending your edits back in step 4 depends on it.
Step 2: Open the copy with your assistant​
Start your assistant in the folder you just created. The skills are already inside it - .claude/skills/ for Claude Code and Cursor, .agents/skills/ for Codex CLI and Gemini CLI - so nothing needs installing.
Then ask for onboarding in your own words:
onboard me
The assistant should pick up the ga4df-onboarding skill. If it starts improvising instead, name the skill directly: "use the ga4df-onboarding skill".
Step 3: Work through the gates​
Onboarding is a fixed plan with named decision points. The assistant does the reading and the estimating; you make the calls.
| Step | What happens | Your part |
|---|---|---|
| Assess | Reads your configuration files and runs one query listing your output tables and when they were last updated | Nothing - it is read-only and free |
| Gate A | One batched set of questions: production or exploration mode, how to handle custom parameters, which modules to enable, advanced GA4 options, assertions | Answer all of them in one go |
| Gate B | If you chose automatic parameter detection: shows the estimated bytes the scan will read | Approve the cost, or skip detection |
| Gate C | Presents the detected parameters and which ones are worth modelling | Pick the ones you want |
| Configure | Applies exactly the approved changes to includes/custom/ | Review the diff |
| Gate D | Shows what the build will scan before running it | Approve the run |
| Verify | Confirms tables refreshed and your new parameters are populated | Sanity-check the numbers |
| Automation | Sets up a daily trigger, or deliberately leaves the project on-demand | Decide which you want |
Two things the assistant is instructed never to do without your say-so: change configuration you did not approve, and run a query against the raw GA4 export without showing you the estimated cost first.
Ask for test mode at the start ("I want to preview the onboarding first"). The assistant walks the entire flow, shows the configuration diffs it would apply and the runs it would start, and changes nothing. When you are happy, re-enter for real.
Step 4: Send the changes back​
Configuration only takes effect once it reaches the repository the pipeline compiles from.
- Git-connected repository: commit and push as usual.
- Installer-created repository: push the copy back with the script that ships inside your workspace, dry-running it first:
python3 .claude/skills/ga4df-managing-runs/scripts/push_dataform_workspace.py . --dry-run
python3 .claude/skills/ga4df-managing-runs/scripts/push_dataform_workspace.py . --message "onboarding configuration"
The dry run lists every file that would be uploaded or deleted without sending anything. The real push refuses to overwrite files that changed in the Dataform interface since you copied the workspace, and never uploads local credential files. Pushing needs the Dataform Editor role.
Then recompile the release - a pushed change does nothing until the project is compiled again. The assistant will prompt you, and the Release and Workflow setup guide covers it.
What to expect from the assistant​
The same request will not produce identical wording, ordering or phrasing twice, and different tools (Claude Code, Cursor, Codex CLI, Gemini CLI) behave differently. The skill constrains what happens - the steps, the gates, the safety rules - but not the exact script.
What this means in practice:
- Read what it proposes before approving. Configuration changes and BigQuery spend are the two places where a mistake costs you something, which is why both sit behind an explicit gate.
- Check the diff, not just the summary. Ask to see the actual file changes before they ship.
- If it goes off track, say so. "Go back to Gate A", "use the ga4df-onboarding skill", or "show me the diff" all pull it back to the plan.
- It may skip steps that don't apply. Re-running onboarding on a half-configured project is normal and expected - it assesses first and only does what is missing.
- Nothing is irreversible without you. Everything it changes locally is a file you can inspect before it is pushed, and nothing runs against BigQuery without an approved estimate.
When you would rather not​
Onboarding by hand is fully documented and equally supported: First Configuration covers the same decisions, and Check Your Output verifies the result. The skills are an accelerator, not a requirement.
Prefer a human?​
Every Premium licence includes onboarding by one of us. If you would rather have a person walk you through your setup - or you started with the assistant and want a second pair of eyes on the result - email support@superformlabs.eu and we will arrange it. There is no separate fee and no expectation that you try the AI route first.
It is worth doing if your setup is unusual: several GA4 properties in one model, a migration from an existing pipeline, custom channel definitions you need to match exactly, or a tracking implementation you are not sure you trust yet.