The galaxy
CORE · CONSTELLATION

Operator Core

Operate AI

The sun of the galaxy. You learn to operate Claude Code and AI systems like a pro: environment, context, prompts, version control and deploy. Everything else orbits here.

Powers All projects
8 modules · 8 lessons
CONSTELLATION MODULES Open each module for the full class
OC-01

Operating environment

lesson

Get your terminal, VS Code, and Claude Code installed, authenticated, and verified so you can start building a real project in under an hour.

Without a solid operating environment, you burn hours fighting PATH errors, permissions, and authentication instead of building. 80% of beginner drop-off happens right here, before a single useful line gets written.

THE LESSON

An operator doesn't 'use' AI from a website: they operate it from their own machine, with access to their files, their git, and their terminal. That's Claude Code. Before installing it you need three foundations: a terminal where you run commands, an editor (VS Code) where you see and edit code, and a package manager to install tools. On macOS the terminal is Terminal.app or iTerm2; on Windows use WSL2 (Ubuntu), because Claude Code and most tooling assume a Unix-like environment. Don't try to operate from plain PowerShell: it'll cost you twice the effort.

First, check what you already have. Open Terminal and run `node --version` and `git --version`. If `node` is missing or older than v18, install Node LTS. On macOS the clean route is Homebrew: `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"` and then `brew install node git`. After installing Homebrew, the installer prints TWO `eval "$(/opt/homebrew/bin/brew shellenv)"` lines that you must add to your `~/.zprofile` — if you skip them, `brew` 'disappears' the moment you open a new terminal. This is the first PATH failure you'll hit, and it's worth understanding: the PATH is the list of folders where the shell looks for commands; if a tool isn't in one of them, the shell says `command not found` even though the file exists on disk.

Install Claude Code with `npm install -g @anthropic-ai/claude-code` and verify with `claude --version`. If npm complains about permissions (`EACCES`), do NOT use `sudo npm` — that leaves you root-owned files you can't later delete. The correct fix is to point npm at a prefix inside your home: `npm config set prefix ~/.npm-global` and add `export PATH=~/.npm-global/bin:$PATH` to your `~/.zshrc`. Reload with `source ~/.zshrc`. Now `claude` launches and asks you to authenticate with your Anthropic account via the browser; that token is stored and you never re-enter it.

VS Code is your window onto the code while Claude Code is your hands. Install it from code.visualstudio.com and, inside, open the command palette (Cmd+Shift+P) → 'Shell Command: Install code command in PATH'. That gives you the `code .` command to open the current folder in VS Code from the terminal — the gesture you'll make a hundred times a day. Install at least these extensions: the official one for your language, GitLens, and a formatter like Prettier. Don't bloat the editor with 40 extensions; each one is attack surface and noise.

The real workflow is always the same and you'll internalize it this module. You open a terminal, navigate to your project with `cd ~/my-project`, type `claude` to launch the session, and in another tab (or a split VS Code panel) you have the editor open with `code .`. Claude Code proposes changes, you review them in the editor or with `git diff`, and you accept them. The terminal and the editor don't compete: the terminal is where the action happens, the editor is where you verify. At MOONKEY LAB, for example, a typical session is `cd ~/xop && claude` and asking it to touch an Astro component while you watch the file in VS Code.

Verify the whole environment with a smoke test before you call the module done. Create a folder `mkdir ~/lab-01 && cd ~/lab-01`, initialize git with `git init`, launch `claude`, and ask it to create a `hola.txt` with your name. Exit Claude, run `cat hola.txt` to confirm the file exists on disk, and `code .` to see it in the editor. If all four steps work (terminal navigates, claude launches and writes, the file appears, VS Code opens it) your environment is operational. Document in a personal note the exact versions you installed: when something breaks a month from now, that note saves you an afternoon.

EXERCISE

From scratch, get your machine operational and prove it: install Node LTS, git, VS Code, and Claude Code. Create `~/lab-01`, make it a git repo with `git init`, launch `claude` inside it, and ask it to generate an `entorno.md` file listing the output of `node --version`, `git --version`, and `claude --version`. Open the result with `code entorno.md` and confirm the three versions are the ones you installed.

DELIVERABLE

A `~/lab-01` folder with an initialized git repo and an `entorno.md` file containing the three verified versions (Node ≥18, git, Claude Code), generated by Claude Code and opened in VS Code.

KEY INSIGHT

The terminal doesn't compete with VS Code: the terminal is where the action happens and the editor is where you verify it. An operator always has both open and never trusts what the AI says it did without looking at the file on disk.

MISTAKES TO AVOID

  • ×Using `sudo npm install -g` to work around a permissions error: it leaves you root-owned files you can't later update or delete without more sudo. Fix the npm prefix in your home directory instead.
  • ×Operating from plain PowerShell on Windows instead of WSL2: half the commands in tutorials assume Unix and you'll be translating each one by hand.
  • ×Forgetting to add the `brew shellenv` line (or npm's `export PATH`) to your profile file: the tool 'works' in that terminal but 'disappears' when you open a new one.
  • ×Believing the file was created just because Claude said it created it: always confirm with `cat` or by opening the file in VS Code before moving on.
  • ×Bloating VS Code with dozens of extensions on day one: they add noise, slowness, and attack surface. Start with the formatter and your language extension, nothing more.
Open lesson →
OC-02

CLAUDE.md: persistent context

lesson

Write a CLAUDE.md file that gives Claude Code persistent memory of your project, so every session starts already knowing your stack, your conventions, and your prohibitions without you repeating them.

Without CLAUDE.md, every session starts from zero and the AI invents conventions, adds dependencies you don't want, and touches things it shouldn't. With it, the AI works precisely instead of generically — it's the difference between an intern who forgets everything each morning and one who already knows the house.

THE LESSON

CLAUDE.md is a text file at the root of your project that Claude Code reads automatically when each session starts. It isn't documentation for humans (though it serves that too): it's the context the AI loads before doing anything. If your `package.json` says React but you actually use Astro, or if you have a rule of 'never add dependencies without justifying them', that file is where it lives. The AI doesn't guess your judgment; you write it down once and it reuses it forever.

The beginner's mistake is writing a generic CLAUDE.md ('this is a web project, use best practices'). That adds nothing, because the AI already assumes best practices. The value is in what's SPECIFIC and NON-OBVIOUS: what stack you REALLY use (not the one it looks like), which database tables belong to another project and must not be touched, what i18n pattern you follow, what gets translated and what doesn't. Look at MOONKEY LAB's real CLAUDE.md: it has a section '⚠️ The Supabase DB is SHARED with XHUB IRON' that lists the forbidden tables (`iron_*`, `world_*`) and the allowed ones (`profiles, progress, feedback, leads, proofs`). That single line keeps the AI from deleting another product's data. That's the kind of knowledge only you have and that the file captures.

Structure an effective CLAUDE.md into clear sections with Markdown headings. The ones that matter: (1) Vision — one sentence on what the project is. (2) REAL stack — exact versions and tools, flagging where reality differs from appearance. (3) Security or data model — who can touch what. (4) Conventions — naming, folder structure, typography, whatever has rules. (5) Tone — how the text it generates should sound. (6) Cost rules — what NOT to do. At MOONKEY that last section literally says 'Don't add dependencies without justification. No JS where HTML/CSS suffices.' — concrete prohibitions, not vague advice.

The golden rule is: every line of CLAUDE.md must change a decision. If a sentence doesn't alter what the AI would do by default, cut it. 'Write clean code' changes nothing. 'Hrefs must be locale-aware: use localizedPath(/route, locale), never raw /route (breaks under a language prefix)' changes every link it generates. The latter is gold; the former is filler. A dense 80-line CLAUDE.md is worth more than a 300-line one full of obvious statements, and it consumes less context in every session.

CLAUDE.md is a living document, not a founding act. Every time the AI does something wrong systematically — adds a dependency you didn't want, uses the wrong i18n pattern, translates a brand term that doesn't get translated — that's a failure of the file, not of the AI. Go back and add the rule. Over time the file becomes the distillation of every correction you've made. In practice, a good session ends with 'add this to CLAUDE.md so it doesn't happen again'. Also keep machine-specific things and personal preferences out of the repo's CLAUDE.md (those go in user memory or an unversioned CLAUDE.local.md): the repo's file is shared with the team.

To write it, don't start from a blank template. Run `claude` in your project and ask it: 'Walk the repo structure, read package.json and the config files, and propose a draft CLAUDE.md that captures the real stack and the conventions you detect.' The AI is excellent at reading your own code and summarizing it. Then YOU correct it: you add the prohibitions it can't possibly know (another project's tables, terms that don't get translated, business decisions) and delete the generic stuff. That cycle — the AI proposes from the code, you inject the knowledge that only lives in your head — produces an honest file in twenty minutes.

EXERCISE

In one of your projects (or clone an example), launch `claude` and ask it to generate a draft CLAUDE.md by reading the real repo structure. Then edit it by hand until every section is specific: stack with versions, one concrete prohibition (something it must NOT touch or add), and a convention rule that changes how it generates code. Delete every sentence that doesn't alter a decision. In a fresh session, confirm the AI respects one of your rules without you repeating it.

DELIVERABLE

A `CLAUDE.md` file at the root of your project, between 40 and 120 lines, with sections for Stack, Conventions, a concrete prohibition, and Tone — where every line changes a decision the AI makes — verified in a fresh session.

KEY INSIGHT

Every line of CLAUDE.md must change a decision the AI would make by default. If a sentence doesn't alter its behavior, it's filler that burns context. The file isn't documentation: it's the distillation of every correction you already made, so you never repeat them.

MISTAKES TO AVOID

  • ×Writing a generic CLAUDE.md ('use best practices', 'clean code'): it changes nothing because the AI already assumes it by default. Only the specific and non-obvious adds value.
  • ×Documenting the apparent stack instead of the real one: if package.json suggests one thing but you use another, say so explicitly or the AI keeps following the false trail.
  • ×Treating it as an immutable founding act: when the AI fails systematically, it's a hole in the file. Go back and add the rule.
  • ×Putting secrets, keys, or absolute machine paths in it: the file is versioned and shared. Those go in user memory or CLAUDE.local.md.
  • ×Forgetting the prohibitions — what it must NOT do (other people's tables, dependencies, terms that don't get translated) — which is exactly the knowledge the AI can't deduce on its own.
Open lesson →
OC-03

Prompt Arsenal

lesson

Build a personal arsenal of reusable prompts —each one with explicit role, context, structure, and intent— that produce consistently high-quality work instead of lottery answers.

A vague prompt gives random results and forces you to iterate five times. A structured prompt gives the right result on the first try, and once you save it you reuse it forever. The difference between an amateur and an operator is that the operator doesn't improvise: they have templates.

THE LESSON

A prompt isn't a question: it's a work instruction. When you ask a competent colleague to do something, you don't say 'fix the login'; you say 'the Supabase magic-link doesn't redirect after authenticating, check the callback in src/lib/supabase.ts, the problem is probably the redirect URL in the config'. You give them an implicit role, context, focus, and a hypothesis. A good prompt does the same thing explicitly. The four pillars are: ROLE (who the AI should be: 'you're a Postgres engineer reviewing RLS'), CONTEXT (what it needs to know: the stack, the file, the constraint), STRUCTURE (how you want the output: 'return only the SQL, no explanation' or 'list 3 options with trade-offs'), and INTENT (the real goal behind the request, not just the step).

ROLE isn't theater: it changes what knowledge the AI activates and what standard it applies. 'Review this code' produces a soft review. 'You're an adversarial security auditor: your job is to refute the claim that this RLS is secure, find the escalation path' produces a completely different analysis, because you've given it a stance. At XCAP, research prompts carry the role of 'an analyst who doesn't lie to themselves' precisely because a model's default bias is to please; the role counteracts it.

Output STRUCTURE is what saves you the most time and what gets forgotten the most. If you don't specify the format, the AI picks one and it's usually long prose you have to trim. Say exactly what you want: 'Return a single block of SQL ready to paste, no comments or explanation.' Or 'Answer with a three-column table: option, advantage, risk.' Or 'Give me the diff, not the whole file.' At MOONKEY, where the tone is 'clear, direct, no fluff or emojis', a copy-generation prompt includes that format constraint verbatim, because otherwise the AI adds ornaments you then delete by hand.

INTENT is the why, and it unlocks solutions you didn't ask for. If you say 'add an index to this column', the AI adds it. If you say 'this query takes 2 seconds on the account page and I want it under 200ms; the index is my first idea but I'm open to alternatives', it might tell you the real problem is an N+1 and the index won't help. When you give the intent, the AI can question your proposed solution and offer a better one. When you only give the order, it executes blind.

An arsenal is a versioned collection of these prompts, organized by task, that you reuse. Don't start from scratch every time: have a prompt for 'RLS security audit', one for 'generate copy in the brand voice', one for 'refactor this component preserving behavior', one for 'write the commit message from the diff'. Save them in a `prompts.md` file in your project or in a note. MOONKEY LAB has a whole page, `/prompts`, dedicated to this, because an arsenal is an asset: every tuned prompt is work you don't repeat. Parameterize the prompts with `[FILE]`, `[CONSTRAINT]` slots that you fill in when you use them.

Iterate the prompt, not the output. When a result comes out wrong, the beginner's reflex is to fix the answer by hand. The operator's reflex is to ask 'what did the prompt fail to know' and fix the prompt. If the AI used tabs and you want spaces, don't edit the file: add 'indent with 2 spaces' to the prompt and save it that way forever. Every correction you put into the template is a correction you never make again. Over time your arsenal prompts become surgical and the results come out right on the first try — which is the goal: to stop iterating.

EXERCISE

Pick a task you repeat (generating commit messages, reviewing security, writing copy). Write a prompt with the four pillars explicit —role, context, output structure, and intent— and test it. Then degrade it to a vague version ('make me a commit message') and compare the two outputs. Tune the good one until it gives the right result on the first try and save it, parameterized with slots, in a `prompts.md`.

DELIVERABLE

A `prompts.md` file with at least three reusable prompts, each with role, context, output format, and intent marked, and parameterized with `[...]` slots — plus a short note comparing the structured prompt's output against the vague version of one of them.

KEY INSIGHT

Iterate the prompt, not the output. When a result comes out wrong, don't fix the answer by hand: fix the prompt and save it that way. Every correction you put into the template is work you never do again.

MISTAKES TO AVOID

  • ×Omitting the output format: without it the AI picks long prose that you trim by hand. Say 'only the SQL', 'give me the diff', 'a 3-column table'.
  • ×Confusing role with theater: the role ('adversarial auditor who must refute') changes what standard the AI applies, it's not decoration.
  • ×Giving the order without the intent: 'add an index' executes blind; 'I want to bring this query from 2s to 200ms, the index is my idea' lets the AI propose something better.
  • ×Fixing the output instead of the prompt: you lose the improvement. Next time you make the same mistake again.
  • ×Not saving the prompts that work: a tuned-then-forgotten prompt is wasted work. The arsenal is an asset that compounds.
Open lesson →
OC-04

Git & GitHub for operators

lesson

Version your work with Git and GitHub so you can experiment, break things, and roll back without fear — using branches, atomic commits, and diffs to review every change the AI proposes.

Without version control, a bad change from the AI can destroy hours of work with no way back. With Git, every good state is saved and you can undo anything. It's the safety net that lets you work fast and without fear.

THE LESSON

Git solves a single problem, but it's the most important one: being able to return to any earlier state of your project. Every time you `commit`, you freeze a snapshot of the project you can always come back to. This changes your working psychology: when you know the last good state is saved, you let the AI make aggressive changes without fear, because the worst case is `git restore` and you're back. An operator without Git works in fear; with Git, they work fast. The minimal flow is: `git status` to see what changed, `git add -p` to review and pick changes chunk by chunk, `git commit -m 'message'` to freeze, and `git log --oneline` to see your history.

The daily cycle is see-review-freeze. After a Claude Code session, NEVER trust that the change is correct: run `git diff` and read exactly what it touched. This is where you discover whether the AI changed something it shouldn't have, deleted a line by accident, or added a dependency. `git diff` is your number-one review tool and you use it every session. Only when the diff convinces you do you `git add` and `git commit`. This habit —reading the diff before accepting— is what separates an operator from someone who copies and pastes blind.

Commits should be atomic: one commit, one logical change, with a message that explains the WHY, not the what. 'Fix the login' is bad (what was wrong?). 'Fix magic-link redirect: the callback URL didn't include the locale and broke under /en/' is good: six months from now, that message tells you why you touched that file. Claude Code is excellent at writing commit messages from the diff — ask it 'write a commit message for these changes explaining the why' and it usually nails it. But review it: sometimes it describes the what and you have to ask for the why.

Branches are parallel universes for experimenting without touching what works. The main branch (`main`) is your stable version. When you're about to try something risky —a big refactor, a new feature— you create a branch with `git checkout -b my-experiment`, work there, and if it goes well you merge it; if it goes badly, you delete it and `main` never knew. Operator rule of thumb: never work directly on `main` for non-trivial changes. Create a branch. At MOONKEY LAB every security or RLS change goes in its own branch and merges only when `get_advisors` gives the green light.

GitHub is Git in the cloud: your remote backup and the place your project deploys from. You connect your local repo with `git remote add origin git@github.com:user/repo.git` and push with `git push`. From then on, `git push` after each good session keeps your work off your machine — if your disk dies, your project lives. On top of that, platforms like Cloudflare Pages (which deploys MOONKEY LAB from `garciafradepablo-pixel/xop`) watch your GitHub repo and automatically publish every push to `main`. So Git isn't just versioning: it's the engine of your deploy, which you'll see in the next module. To talk to GitHub from the terminal, install the `gh` CLI and authenticate with `gh auth login`.

A `.gitignore` is as important as the commits: it lists the files Git must IGNORE. In here go `node_modules/` (reinstalled from package.json, not versioned), `.env` files (secrets — NEVER push keys to GitHub), and build artifacts. Pushing a `.env` with a key to a public repo is one of the most common security incidents in the world, and once the key is pushed, even if you delete the commit, it stays in the history: you have to rotate it. That's why `.gitignore` is set up BEFORE the first commit. Ask Claude Code 'generate a .gitignore for an Astro project with Supabase' and check that it includes `.env` and `node_modules`.

EXERCISE

Take a project, initialize it with `git init` and create a `.gitignore` that excludes `node_modules` and `.env`. Make an initial commit. Create a branch `git checkout -b experimento`, ask Claude Code for an aggressive change, review the result with `git diff`, and commit with a message that explains the why (ask the AI for it and correct it). Go back to `main` with `git checkout main` and confirm the change isn't there. Push everything to a new GitHub repo with `gh repo create` and `git push`.

DELIVERABLE

A repo on GitHub with a correct `.gitignore` (excluding `.env` and `node_modules`), a history of at least two commits with messages that explain the why, and an `experimento` branch separate from `main` demonstrating isolated work.

KEY INSIGHT

Read the `git diff` before accepting any change from the AI. It's your number-one review tool: that's where you discover what the AI touched without telling you. Accepting blind is the fastest way to introduce a bug you won't know where it came from.

MISTAKES TO AVOID

  • ×Working directly on `main` for risky changes: if they go wrong, you contaminate your stable version. Create a branch and isolate the experiment.
  • ×Pushing a `.env` file with secrets to GitHub: it stays in the history forever even if you delete the commit; you have to rotate the key. Set up `.gitignore` BEFORE the first commit.
  • ×Accepting the AI's changes without reading `git diff`: you swallow deleted lines or added dependencies you won't notice until something breaks.
  • ×Commit messages that describe the what ('login changes') instead of the why: six months from now they tell you nothing. Explain the cause.
  • ×Giant commits mixing five different changes: impossible to review and to revert selectively. One commit, one logical change.
Open lesson →
OC-05

Automations and deploy

lesson

Turn a manual script or task into a system that runs and publishes on its own — from automating a repetitive command to deploying your site automatically on every push and scheduling tasks that run without you.

Everything you do by hand and repeat is wasted time and a point where you forget a step. Automating the deploy and recurring tasks turns your work from 'I do it every time' to 'the system does it on its own and does it right', which is the difference between a hobby and an operation.

THE LESSON

Automation starts small: a command you repeat becomes a script. If every time you finish you type `npm run build && git add -A && git commit -m wip && git push`, that's a four-line `deploy.sh` script you run with `./deploy.sh`. The principle is: what you do more than three times, you save. Ask Claude Code 'create a bash script that builds, commits with a timestamp, and pushes, and aborts if the build fails' — the `&&` between commands already guarantees that if one fails, the next ones don't run, which is exactly what you want in a deploy.

The modern deploy of a static site is almost free to set up and it's called continuous deployment. Platforms like Cloudflare Pages watch your GitHub repo: every time you `git push` to `main`, they clone your repo, run your build command (`npm run build` for Astro), and publish the result to a real URL. MOONKEY LAB works exactly like this: `git push` to the `garciafradepablo-pixel/xop` repo and a few seconds later the changes are live at `moonkeylab.pages.dev`. You don't upload files over FTP, you don't touch a server: your only deploy gesture is `git push`. This connects the Git module to this one: Git not only versions, it's the trigger for the deploy.

Setting up continuous deployment is a one-time process. You connect your Cloudflare account (or Vercel, or Netlify) to GitHub, pick the repo, and declare two things: the build command (`npm run build`) and the output folder (`dist` in Astro). Cloudflare saves that config and from then on every push fires a build. If the build fails, it does NOT publish — your previous site stays alive. That's a huge safety property: a broken commit doesn't take down your site, it simply doesn't deploy and warns you. You'll see the build log in the Cloudflare panel; when something fails on deploy but works locally, that log is the first thing you read.

Environment variables are the bridge between your deploy and your secrets, and here it crosses with security. Your code needs the Supabase URL and the anon key, but those do NOT go hardcoded in the repo (you saw this in the Git module). Locally they live in a `.env`; in the deploy you put them in the Cloudflare panel as environment variables, and the platform injects them into the build. A critical distinction you'll learn in depth in Ops & Security: the Supabase anon key CAN live in the client (it's protected by RLS), but a service-role key NEVER — that would grant total access bypassing all security. Knowing which key goes where is operator competence.

The next level is automating recurring tasks that don't depend on a push: things that must run on a schedule. These are scheduled tasks (cron). At XCAP, for example, there are 'autopilot ticks' — loops that run periodically to ingest market data and update the Market Memory ledger without anyone pressing a button. The general idea: you define a command and a schedule ('every day at 6:00', 'every 15 minutes'), and the platform runs it on its own. For a static site this usually lives in serverless functions (Cloudflare Workers) or in cron jobs. The operator key is that the loop be honest and idempotent: that running twice doesn't duplicate data, and that it accumulates learning instead of noise — exactly the discipline of XCAP's 'capital invariant', where state only changes on real events, not on every tick.

Automating without observability is dangerous: a system that runs on its own and fails silently is worse than a manual one. For every automation, have a way to know whether it worked. Cloudflare's build emails you if it fails. A cron task should log its result somewhere you check. The rule: never automate something you can't verify afterward. Always start with a version that runs and warns you, and only once you trust it do you let it truly run on its own. An automatic deploy without checking the log the first time is how you publish a broken site to production without noticing.

EXERCISE

Take the GitHub repo from the previous module and connect it to Cloudflare Pages (or Vercel): configure the build command and the output folder, and put the environment variables in the panel instead of in the code. Make a small change, `git push`, and confirm in the build log that it deployed on its own to a real URL. As a bonus, write a `deploy.sh` that does build+commit+push and aborts if the build fails.

DELIVERABLE

A site deployed to a real public URL (Cloudflare Pages or similar) that rebuilds automatically on every `git push`, with the secrets in the panel's environment variables (not in the repo) and a `deploy.sh` that aborts on a failed build.

KEY INSIGHT

Never automate something you can't verify afterward. A system that runs on its own and fails silently is worse than a manual one. Every automation needs a channel that warns you — the build log, an email, a record you check — before you trust that it runs on its own.

MISTAKES TO AVOID

  • ×Hardcoding the Supabase URL and keys in the code instead of using environment variables: you push them to GitHub and they get exposed. They go in the local `.env` and in the deploy panel.
  • ×Confusing the anon key with the service-role key: the anon CAN go to the client (RLS protects it), the service-role NEVER — it would grant total access bypassing security.
  • ×Leaving an automatic deploy without checking the log the first time: if the build fails in the cloud but works locally, you publish a broken site or nothing without noticing.
  • ×Writing non-idempotent cron tasks: running twice duplicates data. The loop must accumulate learning, not noise (the discipline of XCAP's ticks).
  • ×Automating without observability: a script that fails silently gives you a false sense that everything's fine until the damage is large.
Open lesson →
OC-06

ChatGPT → Claude migration

lesson

Move your work from ChatGPT to Claude Code by setting up a documented, versioned system — where context lives in CLAUDE.md and repo files instead of getting lost in chat threads, and where the AI acts on your real files instead of just conversing.

In ChatGPT your context is trapped in threads that get lost and the AI doesn't touch your real files: you copy and paste by hand. Migrating to Claude Code turns that chaos into a documented, versioned, operational system. It's the leap from 'conversing with an AI' to 'operating an AI over your real project'.

THE LESSON

The fundamental difference isn't about the model, it's about the paradigm. ChatGPT is a conversation: you type, the AI replies with text, and you copy that text into your project by hand. The context lives in the thread, and when the thread grows too long or you close it, that context evaporates. Claude Code is an operator: it lives in your terminal, reads and writes your files directly, runs commands, makes commits. The context doesn't live in an ephemeral conversation — it lives in CLAUDE.md and your repo, which are permanent and versioned. Migrating is moving your knowledge from a place that gets erased to one that persists.

Start by auditing what valuable context you have trapped in ChatGPT. You probably have threads where you defined a project's stack, architecture decisions, style conventions, prompts that worked well. All of that is an asset that right now only exists in threads. The first step of the migration is to extract it: go through your important conversations and pull out the durable decisions (not the whole chat, the conclusions). 'We use Astro, not React, because X', 'the tone is direct without emojis', 'I share the DB with another project and these tables are untouchable'. That's exactly the material for a CLAUDE.md.

The heart of the migration is building the CLAUDE.md from that extracted context. What in ChatGPT you had to repeat at the start of every thread ('remember I use Astro and Supabase and the tone is...'), in Claude Code you write once in CLAUDE.md and it loads on its own every session, forever. This connects directly to module OC-02: the migration is nothing more than dumping the scattered context from your threads into the persistent-memory file. A good exercise is to ask ChatGPT itself: 'summarize all the technical decisions and conventions from this thread in bullet form for a context file' — and use that output as a draft.

Change how you ask for things, too. In ChatGPT you ask 'write me the code for X' and you get a block you paste. In Claude Code you ask 'create the file src/lib/x.ts that does X, following the conventions in CLAUDE.md, and show me the diff' — the AI creates the file in its place, you review the diff with git, and you commit. The copy-and-paste gesture disappears. This requires unlearning a habit: stop treating the AI as a text generator and start treating it as a collaborator that acts on your project. At first it's hard to trust that it'll touch the right files; that's why `git diff` and the commits from module OC-04 are your safety net.

Migrating well means setting up the complete system, not just switching tools. An operator's system is: a repo with git, a CLAUDE.md with the context, a .gitignore protecting secrets, an arsenal of prompts in a prompts.md, and continuous deployment connected. When you have those five elements, you've stopped 'using an AI' and set up an operation. Each of the previous Operator Core modules was one piece; this module assembles them by migrating a real project from the chaos of ChatGPT threads to that documented system. The result is that any new session — yours or a collaborator's — starts with all the context loaded.

The value you'll feel immediately is continuity. In ChatGPT, picking a project back up after a week means re-reading threads and remembering where you were. In the migrated system, you open the terminal, `cd` to the project, `claude`, and the AI already knows the stack, the conventions, and the prohibitions because they're in CLAUDE.md; you see where you were in `git log`. The project documents itself. That continuity is what lets you operate many projects at once without them blending together — as you need when you're running XNLAB, XHUB, XCAP, Espejo, and this school in parallel: each with its own CLAUDE.md, its own repo, and its own isolated context, all operated with the same flow.

EXERCISE

Pick a project you've been running in ChatGPT. Extract the durable decisions and conventions from your threads (ask ChatGPT to summarize them in bullets). Create the repo with git, dump that context into a CLAUDE.md, add a .gitignore that protects secrets, and save in a prompts.md the 2-3 prompts you used most. Then launch `claude` and ask it for a real project task, asking for the diff instead of a text block — review it with git and commit. Compare the experience with how you did it before.

DELIVERABLE

A migrated, operational project: git repo with CLAUDE.md (context extracted from your threads), .gitignore protecting secrets, prompts.md with your key prompts, and at least one commit of a real change made by Claude Code over the files (not copy-pasted).

KEY INSIGHT

The real change isn't about the model, it's about the paradigm: you go from conversing (context trapped in threads that get erased) to operating (context alive in CLAUDE.md and the repo, permanent and versioned). Migrating is moving your knowledge from a place that evaporates to one that persists and loads on its own.

MISTAKES TO AVOID

  • ×Copying the full content of your ChatGPT threads instead of extracting only the durable decisions: you fill the CLAUDE.md with conversational noise instead of rules that change decisions.
  • ×Still using Claude Code like ChatGPT — asking for text blocks to copy-paste — instead of letting it act on the files and reviewing the diff. You lose all the value of the paradigm.
  • ×Migrating the tool but not setting up the system: without CLAUDE.md, without git, without .gitignore, you're still in chaos, just in a different terminal.
  • ×Trusting blindly that the AI touched the right files without reviewing `git diff`: right when you migrate, when you don't yet have intuition for its behavior, that safety net is essential.
  • ×Mixing several projects' context in one place: each project needs its own repo and its own isolated CLAUDE.md, or one's conventions leak into another.
Open lesson →
OC-07

Verificación y testing

lesson

Build a reusable verification checklist and apply it to a real Claude Code change before publishing it, proving the change does what it says.

The number-one failure of the novice operator isn't writing the prompt badly: it's believing the AI's "done, it works now". Claude Code hands you a diff that compiles, sounds confident, and sometimes even runs, but verifying that it compiles is NOT verifying that it works. Verification is the boundary between someone who moves files and an operator who can be trusted: it means that when you say "it's done", it's done. It isn't an optional phase at the end of the work; it is the work. An unverified change is a hypothesis, not a deliverable, and shipping hypotheses to production is how the academy loses clients and how you lose the next morning fixing what you broke yesterday.

THE LESSON

Verification is NOT automated testing, and it's NOT only about security. It's the general discipline of proving that a change behaves as you expect BEFORE publishing it. It has four levels that apply in order and you almost never need all four: (1) does it compile / start without error? (2) does the diff say what you thought you were asking for? (3) did the observable behavior change as you expected? (4) did you break something that worked before (regression)?

The cheap, mandatory level: READ THE DIFF. Before accepting anything, `git diff` (or the editor's diff panel). Claude Code is excellent at describing what it did and mediocre at confessing what it did extra: a file you didn't ask it to touch, a forgotten `console.log`, an added dependency, a block deleted 'in passing'. If the diff touches more than your sentence asked for, that's a signal, not a coincidence. Rule: never accept a change whose diff you don't understand line by line.

"It seems to work" is a comfortable lie with three faces. Face 1: it started without error → you confuse 'doesn't crash' with 'does the right thing'. Face 2: I tested it once with the happy path → you didn't test the empty input, the duplicate, the user without permission. Face 3: the AI told me it verified it → the AI didn't run anything, it predicted text. The antidote is always the same: observe the real behavior with your own eyes, don't accept the report of whoever made the change (human or AI).

Smoke test = the shortest path that proves the central piece breathes. It isn't full coverage; it's the 20% of tests that catch 80% of the disasters. For a web app: bring up `npm run dev`, open the route you touched, do the main action, watch that the expected thing happens. For a script: run it with a known input and compare the output with what you KNOW it should produce. You define the smoke test BEFORE asking for the change, not after, so you don't fool yourself by tuning the test to the result.

Assertion over observation: don't stare at the screen looking for confirmation, define the falsifiable claim in advance. Bad: "let's see if it loads okay". Good: "when I submit the form with no email, the error 'email required' must appear and NO row must be inserted into `leads`". A good verification can be written as a sentence that would be clearly false if the change failed. If you can't write that sentence, you don't know what you're verifying.

Regression: the new change often breaks the old. The question that separates the senior from the junior isn't "does my feature work?" but "what worked before that my change might have broken?". In this school, touching `galaxy.ts` can break the SVG map and every constellation page, because they're generated from the same file. Before publishing, mentally list what OTHER things depend on what you touched and check at least one.

How to use Claude Code AS a verifier, without delegating the judgment to it: ask it to run the real command (`npm run build`, the test, the script) and paste you the raw OUTPUT, not its summary. "Run the build and paste me the full output" is verifiable; "does the build work?" invites the obliging hallucination. The AI verifies by running and showing evidence; you verify by reading that evidence. Never the other way around.

Verification proportional to risk (not all verification costs the same). Changing a copy string: reading the diff is enough. Touching RLS, a Postgres trigger, or the payment flow: build + smoke + regression + reviewing advisors. The mature operator calibrates the verification effort to the cost of being wrong, doesn't verify everything the same or trust everything the same.

EXERCISE

Take a small, real change in this repo (e.g. tweaking a module's `blurb` in `src/data/galaxy.ts`, or adding a term to `glosario.ts`). BEFORE touching anything, write in a `VERIFY.md` file three falsifiable assertions about the expected result (e.g. "the page /constelacion/operator-core shows the new blurb", "`npm run build` finishes without error", "the rest of the modules still appear the same"). Ask Claude Code for the change. Then: (1) read the full `git diff` and note any line you didn't expect; (2) run `npm run build` and paste the output; (3) bring up `npm run dev` and check your three assertions with your own eyes, marking each PASS/FAIL. If any fails, do NOT publish: you fix it and repeat the cycle.

DELIVERABLE

A `VERIFY.md` with the 3 falsifiable assertions written BEFORE the change, each marked PASS/FAIL with the observed evidence (pasted build output + what you saw on screen), and confirmation that the `git diff` contains no unexpected lines. It's the proof that you verified behavior, not that the AI said yes.

KEY INSIGHT

The operator's question isn't "did the AI say it works?" but "what evidence do I have, with my own eyes, that it works — and what might it have broken that worked before?". Whoever ships without that evidence isn't faster: they're borrowing time from tomorrow's self, with interest.

MISTAKES TO AVOID

  • ×Accepting the diff without reading it because "it compiles" — compiling proves syntax, not intent; always read line by line what it touched.
  • ×Testing only the happy path and declaring victory — the bug lives in the empty input, the duplicate, and the user without permission, not in the path you already knew would work.
  • ×Asking Claude Code "does this work?" instead of "run the command and paste me the output" — the first invites an obliging hallucination; the second produces evidence.
  • ×Forgetting regression: verifying only the new feature and not checking what depended on what you touched (in this repo, `galaxy.ts` feeds the map AND every constellation page).
  • ×Writing the test AFTER seeing the result and tuning it to pass — the assertion is defined before the change or it doesn't count as verification.
OC-08

Sesiones largas y orquestación

lesson

Design a long-session protocol: detect context degradation, set up a handoff that restarts clean without losing state, and delegate a branch of work to a subagent. Deliver the protocol applied to a real session.

This is the DAILY failure mode of the AI operator, and almost nobody names it. The context window isn't infinite: as the session grows, Claude Code starts forgetting decisions from an hour ago, repeating work, contradicting what you agreed on, editing the wrong file. It's not that the model got dumber; it's that you kept pushing a session that was already saturated. The novice operator endures it, fights against an increasingly lost AI, and loses the afternoon. The professional recognizes the signal early and does the counterintuitive thing: stops, summarizes, restarts or delegates. Knowing WHEN to start from scratch and how to do it without losing the thread is worth more than any perfect prompt, because no prompt survives a rotten context.

THE LESSON

The signals of context degradation, in order of appearance: (1) the AI asks you again something you already answered; (2) it re-implements code that already existed or undoes an agreed change; (3) its answers turn generic, losing the concrete names of your project; (4) it edits the wrong file or invents paths. The moment you see signal 1-2, you're in the red zone. Don't wait for signal 4: by then you're cleaning up a mess, not making progress.

The right instinct is counterintuitive: when the session goes off the rails, stopping and restarting is FASTER than insisting. Fighting a saturated context is negative compound interest: every turn adds noise to noise. The question isn't "how do I explain it better?" but "is this context still helping me or already getting in my way?". If it's getting in the way, the fix isn't a better prompt, it's a fresh context.

The handoff: how to restart WITHOUT losing state. Before closing a derailed session, ask for the handoff summary: "Summarize in one block what we were trying to do, what decisions we made, what files we touched, what's left, and what the concrete next step is". That block is your portable state. New session → you paste the summary → Claude Code starts with fresh context and memory of what matters, without dragging the noise along. The handoff turns a total-loss restart into a near-zero-cost one.

Your persistent memory lives on disk, not in the window. What truly matters must not live only in the chat: it lives in `CLAUDE.md` (project decisions, conventions, what NOT to touch) and in repo note files. A new session that reads your `CLAUDE.md` already knows the essentials. Rule: if you'll need a decision tomorrow, don't leave it in the chat — write it in `CLAUDE.md`. The context window is volatile RAM; the repo is your disk.

Prevention: short, single-focus sessions beat the marathon. One session = one bounded objective. "Fix the RLS verification" is a good session; "refactor the whole backend" is a marathon that's going to degrade halfway through. When a task is large, you split it into independent sessions with a handoff between them, you don't cram the whole thing into a window that can't hold it.

Delegation to subagents: parallelism and context isolation. A subagent is a child session with its OWN context window, to which you give a bounded task and a defined deliverable. Two gains: (1) parallel work — one subagent audits security while another writes tests; (2) isolation — the subagent burns its context exploring a huge file and returns just the summary, without contaminating your main session. You delegate what's bulky to explore but compact to report.

How to write a good subagent assignment: as if the subagent had no memory of your conversation — because it doesn't. Absolute paths, self-contained context, and an explicit deliverable ("return X to me"). A vague assignment to a subagent is worse than not delegating: you spend a whole window to receive generic garbage. The subagent shines on tasks with a clear boundary, not on "help me with this".

Recovering the thread when Claude Code gets lost mid-task: don't keep pushing. Stop, ask it to tell you in ITS own words what it thinks it's doing and why. If its version doesn't match yours, that's the drift — correct it explicitly or hand off to a clean session. The worst option is the novice's: piling on more instructions over a model that already misunderstood, stacking corrections on top of a fundamental misunderstanding.

EXERCISE

In your next real work session in this repo, keep a live log. (1) Note the first moment you detect a degradation signal (the AI repeats, forgets a name, touches the wrong file) and which one it was. (2) At that point, instead of insisting, ask for the handoff block (what we were trying to do, decisions, files, what's left, next step). (3) Open a new session, paste the handoff, and verify it picks up without re-asking what was already resolved. (4) Separately, delegate to a subagent ONE bounded task with an explicit deliverable — e.g. "Read `src/data/galaxy.ts` and return me the list of all module `code`s with their `status`, nothing else" — and check that the report is usable without the subagent having known your conversation.

DELIVERABLE

A `SESSION-LOG.md` with: the degradation signal you detected (which one and on what turn), the handoff block you generated, confirmation that the new session picked up without re-asking, and the subagent's assignment + report. It's the proof that you know how to operate the session as a finite resource, not endure it until it breaks.

KEY INSIGHT

The senior operator's invisible skill isn't writing the perfect prompt: it's knowing when the current context has become dead weight and having the reflex to restart clean with a handoff instead of fighting. The context window is volatile RAM; your handoff discipline and your `CLAUDE.md` are the disk. Whoever confuses the two loses the afternoon defending a session that was already dead.

MISTAKES TO AVOID

  • ×Insisting on a saturated session with more and more prompts — it's negative compound interest; every turn adds noise, the fix is fresh context, not a better prompt.
  • ×Restarting without a handoff and losing all the state — always ask for the summary block (intent, decisions, files, what's left, next step) before closing.
  • ×Leaving important decisions only in the chat instead of writing them in `CLAUDE.md` — the chat is volatile RAM; what you'll need tomorrow goes to disk.
  • ×Cramming a huge task ("refactor everything") into a single marathon that degrades halfway — split it into single-focus sessions with a handoff between them.
  • ×Delegating to a subagent with a vague assignment and no deliverable — without absolute paths, self-contained context, and an explicit "return X to me", you spend a whole window to receive generic garbage.

Next constellation

Data & Systems

Backend that holds