Skip to content

Structure & orchestration

The automations are Python, but they don't run on anyone's laptop. Five external tools carry the system, and each has one job. This page is a usage guide: for every tool, what it does for us, where the settings that matter live, and how to change them. It's the page to open when something needs adjusting — a schedule, a key, a model.

flowchart LR
    Cron["⏰ cron-job.org<br/>the trigger"] -->|pings| GH
    GH["🐙 GitHub Actions<br/>the runtime"] -->|runs Python| Code
    Code["🐍 The automations"] -->|reasoning| Claude["🤖 Claude Platform"]
    Code -->|inbox| Google["📧 Google Cloud / Gmail"]
    Dev["🛠️ Claude Code (VS Code)<br/>where it's built"] -.->|commits to| GH

A note on the callouts below

Boxes marked “Needs your detail” are where a console account only Benedikt/U2V can see — the exact project name, the cron job, the billing org. Everything else is verifiable from the repo.


GitHub — the runtime

Its job for us. GitHub has two jobs, and only two: it's the single place all the code is stored, and — through GitHub Actions — it's what runs the automations on a schedule. There's no server; a scheduled Action is the whole runtime. The live system always runs from the main branch, so once a change is merged there it goes live on the next pass.

You read code on GitHub — you don't edit it here

The code side of GitHub is for browsing: open and read any file, but code is never changed on GitHub itself. Edits happen on your laptop in Claude Code and are pushed up. (The Actions and Settings tabs below are operational controls — they run and configure the system, they don't edit its code.)

What you'll see — the code view. The repo's front page is a list of u2v-* folders, one per automation, each named for the job it does — u2v-dispatch, u2v-dealflow, u2v-lp-share, and so on. Open any folder and you can read the whole thing: the code under src/, the README.md (what it does and how to run it) and the CLAUDE.md (the operating guide Claude Code reads). Start at the top-level README.md, shown just under the folder list — it introduces the whole project and links to every part. It's the best place to orient yourself before opening anything in Claude Code.

The Actions tab — run a pass now. The automations fire on a timer, but you don't have to wait for the next one. Open the Actions tab, pick the workflow, and click Run workflow to trigger it on the spot. The two schedules (defined in .github/workflows/):

Workflow What it runs Cadence
dispatch.yml One pass over the klaus@u2v.vc inbox (the dispatcher) every 30 min
dealflow-sample.yml The bi-monthly sample deck 1st of the month, odd months only

Their live timing, though, isn't set in the repo — an external cron pinger triggers these runs on schedule (GitHub's built-in scheduler proved unreliable). See The cron pinger below.

The Settings tab — people & secrets.

  • Add a teammate. When someone new joins, add them under Settings → Collaborators so they get access to the repo.
  • Secrets. The keys and tokens the automations need — Attio, Anthropic and Gmail, plus the scoped keys for lp-intake and lp-tasks — live under Settings → Secrets and variables → Actions: encrypted, never in the code, injected as environment variables at run time.

Making changes? Sign in as Klaus first

The repo lives under the shared klaus-u2v GitHub account. To do anything that changes it — merge to main, run a workflow, or edit settings or secrets — sign in to GitHub as that account using the klaus@u2v.vc Google login (the same identity behind the inbox and the Google Cloud project).

One last thing you'll notice while browsing: after each run the workflow commits a little state back to the repo — the roster queue, the "last-featured" record — tagged [skip ci] so it doesn't re-trigger itself. That's the system's memory, kept in git rather than a database (see Design principles).


Claude Code (in VS Code) — where it's built

Its job for us. This whole system was written — and is maintained — in Claude Code, the agentic coding tool, running inside VS Code. It isn't part of the runtime (GitHub is); it's the workbench. It matters here because it's also how the next person works on the code.

Where the settings live. Every project carries a CLAUDE.md file — an operating guide Claude Code reads automatically. The root CLAUDE.md describes the whole system; each u2v-*/CLAUDE.md describes that one automation. These files are what make "ask Claude Code about X" throughout this site actually work: the context is already written down.

How to use it. Never opened VS Code before? Start with the from-zero walkthrough — Getting set up in VS Code — which takes you from downloading the editor to asking Claude Code its first question about this repo. In short: open the repo folder, start a chat, and ask in plain language — "explain how lp-share picks which deals are new," or "add a new workflow that does Y." It has the CLAUDE.md context to answer and to make changes correctly, and adding a new automation is deliberately cheap — the shape is identical every time (see Adding an automation).


The Claude Platform — the reasoning

Its job for us. Every judgment in the system — classifying an email, scoring a startup, researching an LP, ranking a shortlist — is a call to Anthropic's Claude models, all on a single account managed at the Claude platform console.

Who runs it, and when you'd open it. The organisation there is the Motu Ventures team, and Philipp is the admin. For our purposes there are only three reasons to ever go in:

You want to… Where in the console Who can do it
Change the budget or rate limits the automations run under Billing & limits Philipp only
Add someone to the team — approve their pending request to join Motu Ventures Organization settings Philipp only
Copy the API key every automation runs on API keys Anyone with access

That API key is the same value the repo stores as the ANTHROPIC_API_KEY Actions secret (see GitHub, above) — so if Philipp rotates it in the console, that secret has to be updated to match, or the automations stop authenticating on their next run.


Google Cloud — the Gmail connection

Its job for us. The system's front door is a single Gmail inbox, klaus@u2v.vc. Google Cloud's only role is to grant the automations permission to use that inbox through the Gmail API — reading each incoming email and sending the reply. Nothing else in the stack touches Google.

Why it's needed. Code can't simply "log in" to a Gmail account. Google requires an authorised OAuth client to act on the account's behalf, plus a refresh token — a long-lived credential that lets the system keep acting as klaus@u2v.vc on every scheduled run without a human ever signing in. Issuing that client and that token is the entire reason a Google Cloud project exists here.

What we set up. One project holds the whole arrangement:

  • Project claude-automation-501109 — owned by the klaus@u2v.vc Google account, with the Gmail API enabled.
  • An OAuth client named u2v-dispatch local — authorised to read and send mail on the inbox. It supplies the repo's GMAIL_CLIENT_ID / GMAIL_CLIENT_SECRET.
  • A one-time consent (via the helper below) minted the long-lived GMAIL_REFRESH_TOKEN. Those three secrets are all the code needs — it talks to Gmail over plain REST and swaps the refresh token for a short-lived access token on each run.

How to find it — and re-authorise.

  1. Sign in to Google as klaus@u2v.vc — the account that owns the project.
  2. Open the project console: console.cloud.google.com → claude-automation-501109.
  3. Go to Google Auth Platform → Clients and open the u2v-dispatch local client — to view or rotate its ID and secret.
  4. To mint a fresh refresh token — e.g. after rotating the client — run the dispatcher's helper locally and complete the browser consent as klaus@u2v.vc:
    cd u2v-dispatch && PYTHONPATH=src .venv/bin/python -m u2v_dispatch.cli gmail-auth
    
    Put the token it prints into GMAIL_REFRESH_TOKEN (locally in .env, in CI as an Actions secret).

The cron pinger — the trigger

Its job for us. Something has to start each scheduled run, and GitHub's own built-in scheduler proved unreliable — runs got delayed or skipped. So an external service, cron-job.org, is the dependable clock: on a fixed schedule it calls the GitHub API to kick off a workflow.

How it's wired. Each job sends an authenticated request — carrying a scoped GitHub token — to a workflow's workflow_dispatch endpoint (api.github.com/repos/klaus-u2v/automations/actions/workflows/<file>/dispatches). It's the very same "Run workflow" action you can click by hand in the Actions tab, just fired on a timer instead.

The two jobs. Signed in, the dashboard shows exactly two:

Job Starts Timing
U2V Dispatch the dispatcher every 30 min
U2V - Dealflow Sample Document Data Room the sample deck 1st of every month

The sample-deck workflow then gates itself to odd months only, so one monthly ping gives a clean bi-monthly cadence — and it's started only by this pinger (no GitHub schedule), precisely so it can never double-fire and email two decks.

How to get in — and change the timing.

  1. Sign in to Google as klaus@u2v.vc, then open console.cron-job.org/dashboard.
  2. EDIT a job to change its cadence; HISTORY shows its recent runs (handy for confirming a ping actually fired). The timing lives here in the job — not in the repo.

If the pings stop, the automations go quiet

The entire live cadence hangs on this one account and its scoped GitHub token. If the token expires or a job gets paused, the runs simply stop — with no error anywhere in the repo. So if the automations ever go mysteriously idle, this dashboard is the first place to look.


Adding a new automation

Because every project shares the same skeleton, the mechanics of adding one are deliberately cheap — and the repo's root CLAUDE.md now spells out the exact checklist Claude Code follows to wire one up correctly (the --json contract, the dispatcher registration, the CI venv step, scoped keys). So the code is rarely where a new automation succeeds or fails.

The hard part — the part only you can do — is being clear about what you want to build.

Clarity of intent is the real work

Claude Code is genuinely excellent at building. What it cannot do is guess the automation you have in your head — and the quality of what you get out is set almost entirely by the precision of what you put in. So the recommendation is blunt: do the thinking first. Before you open the editor, get clear on

  • What exactly it should do — the one job, start to finish: its trigger (an email? a schedule?), what it reads or writes in Attio, and the result a person gets back.
  • How it should get there — the rough shape of the steps, and the judgment calls: what's plain deterministic code versus where a model is genuinely needed (see the model does judgment, not plumbing).
  • What "done well" means — the edge cases it must handle, what it should refuse to guess rather than get wrong, and what it must never overwrite.

You don't need to work out the implementation — that's Claude Code's job. You need the vision, sharp enough to say out loud.

Then build it with Plan mode

Once you can state that vision clearly, hand it over in Plan mode (see Getting set up in VS Code). Describe what you want — the more precise, the better — and let Claude Code draft a plan before it writes any code. Plan mode is a real thinking partner: it will pressure-test your idea, tell you honestly whether an approach holds together, and raise things you hadn't considered. But it works with your vision, not in place of it — the clearer you are, the better the plan. A good move is to point it at an existing automation to model: "build this like u2v-lp-tasks, but for X." It will replicate the backbone, following the checklist in CLAUDE.md. Read the plan, refine it, and only then let it build.

Think clearly about what you want to achieve and how to get there, then formulate that vision cleanly to Claude Code in Plan mode — and it will make it real.

What actually gets wired

For reference, this is the "definition of done" Claude Code works through for a routed workflow (all of it lives, in full, in the root CLAUDE.md):

  1. Scaffold a new u2v-<name>/ with the standard skeleton, modelled on the nearest sibling.
  2. A CLI honouring the --json envelope.
  3. Register it in u2v-dispatch/workflows.yaml — the classifier learns the new intent from its description; no dispatcher code change for a normal data-shaped workflow.
  4. Wire CI — add its venv to the dispatch.yml build list (and any scoped secrets).

An autonomous one (like the sample deck) skips the dispatcher and ships its own scheduled Action instead.

Go deeper

Want the precise, current wiring? Open the repo in Claude Code and ask "walk me through dispatch.yml step by step," or "what exactly does the gmail-auth command do?"