// data.jsx — all cheatsheet content, kept in one place so editing copy doesn't
// require touching layout code.

const LANES = [
  {
    id: "lane-1",
    n: 1,
    short: "Branch-switch",
    title: "Minor fix in the main repo",
    tag: "fast · solo · in-place",
    accent: "var(--l1)",
    accentBg: "var(--l1-bg)",
    accentInk: "var(--l1-ink)",
    when: "Solo, simple change, no parallel agent, no PR needed.",
    whyNot: "PR ceremony is overkill for one-line fixes. Git is already the audit trail. Re-installing node_modules for a 30-second fix is absurd.",
    steps: [
      { h: "Sync main", c: "git switch <base>\ngit pull" },
      { h: "Branch off", c: "git switch -c <branch>" },
      { h: "Edit · commit", c: "git add -p\ngit commit -m \"fix(onboarding): typo in welcome copy\"" },
      { h: "Merge back · clean up", c: "git switch <base>\ngit merge <branch>   # fast-forward\ngit branch -d <branch>\ngit push" },
    ],
    diagram: "timeline-l1",
    gotchas: [
      { h: "Don't mix with parallel agent work", b: "Switching branches mid-conversation corrupts the agent's mental model of the file tree." },
      { h: "Use git switch, not git checkout", b: "git switch refuses to silently restore a file if you typo a branch name as a path. See the Concepts panel." },
      { h: "Fast-forward only", b: "If the merge isn't a fast-forward, your dev branch moved — pull again, or you really want Lane 2." },
    ],
    quick: "git switch dev && git pull\ngit switch -c <branch>\n# ... edit, commit ...\ngit switch dev\ngit merge <branch>\ngit branch -d <branch>\ngit push",
  },
  {
    id: "lane-2",
    n: 2,
    short: "Worktree + PR",
    title: "Parallel work · review · clean build",
    tag: "parallel · isolated · reviewable",
    accent: "var(--l2)",
    accentBg: "var(--l2-bg)",
    accentInk: "var(--l2-ink)",
    when: "Two real shapes of this workflow — pick the one that matches what just happened.",
    whyNot: null,
    modes: [
      {
        id: "manual",
        label: "Manual",
        sublabel: "You make the worktree from scratch",
        title: "Worktree + PR — you in the driver's seat",
        tag: "parallel · isolated · reviewable",
        when: "You want to start parallel work yourself: an isolated checkout to commit · push · open a PR while your main repo keeps moving.",
        painPoint: {
          h: "The Expo pain point",
          b: "Every worktree wants its own node_modules (often 1–2 GB). Three escape hatches:",
          table: [
            { approach: "pnpm (recommended)", setup: "Migrate package manager once", cost: "~10s install", note: "Hard-links from global store at ~/Library/pnpm/store. Add shamefully-hoist=true to .npmrc if a native module breaks." },
            { approach: "post-checkout hook", setup: "Add a hook once", cost: "0s when lockfile unchanged · full install when it changes", note: "See the snippet under Gotchas." },
            { approach: "Accept the install tax", setup: "Nothing", cost: "Full npm install every time", note: "Fine if you rarely make worktrees." },
          ],
        },
        steps: [
          { h: "Create worktree on new branch", c: "git worktree add <path> -b <branch> <base>" },
          { h: "Enter · install", c: "cd <path>\nnpm install                  # or: pnpm install (~10s)" },
          { h: "Work · commit · push", c: "# Edit · commit normally (you or a local Claude Code agent)\ngit add -A\ngit commit -m \"feat(swipe): export to mp4\"\ngit push -u origin <branch>" },
          { h: "Open PR", c: "gh pr create --base <base> --title \"feat(swipe): export to mp4\"" },
          { h: "After merge → Lane 3", c: "# proceed to cleanup" },
        ],
        diagram: "parallel-l2",
        gotchas: [
          { h: "post-checkout hook (skip install when lockfile unchanged)", code: "# .git/hooks/post-checkout  (chmod +x after creating)\n#!/bin/sh\n[ \"$3\" = \"1\" ] || exit 0          # branch checkout only, not file checkout\ngit diff --quiet \"$1\" \"$2\" -- package-lock.json || npm install" },
          { h: "Don't symlink node_modules between long-lived worktrees", b: "Breaks the moment lockfiles diverge — and in a Mode A worktree where you're actively editing deps, they will diverge. Either pay the install tax, or move to pnpm. (Mode B's short-lived verification is a different case — see Concepts → 'ln -s node_modules'.)" },
          { h: "If a Claude Code agent already pushed the branch — use Mode B", b: "When the work is done remotely (cloud agent, or local agent with isolation:\"worktree\" that pushed), don't create a fresh worktree. Just fetch + checkout in your main repo." },
        ],
        quick: "git worktree add .claude/worktrees/<name> -b <branch> dev\ncd .claude/worktrees/<name>\nnpm install                  # or pnpm install\n# ... edit, commit ...\ngit push -u origin <branch>\ngh pr create --base dev",
      },
      {
        id: "claude",
        label: "With Claude Code",
        sublabel: "Review the agent's PR — local or cloud",
        title: "Preflight · verify on device · merge",
        tag: "remote agent · in-place verify",
        when: "Claude pushed a branch like claude/gallant-wozniak. Two sub-cases with different entry points: a LOCAL Agent (isolation:\"worktree\") left a worktree at .claude/worktrees/<name> — cd into it. A CLOUD bg agent left only the remote branch — fetch + checkout in your main repo. Same destination, different first step. Always preflight to find out which one you're in.",
        painPoint: {
          h: "What about node_modules?",
          b: "Path 2b (checkout in main) keeps your existing node_modules — usually enough. Path 2a (local worktree) starts empty; symlinking to main is the cheapest fix when the lockfile is unchanged. Either way, install only if the lockfile changed or a new native module appeared.",
          table: [
            { approach: "Local worktree + lockfile unchanged", setup: "—", cost: "~0s", note: "Share main repo's deps: ln -s ../../../node_modules node_modules. See step 3." },
            { approach: "Lockfile unchanged · JS-only", setup: "Nothing", cost: "0s", note: "npm start, open your dev client, Shake → Reload. No rebuild." },
            { approach: "Lockfile changed", setup: "Nothing", cost: "Full install", note: "npm install in your current dir (worktree or main) before npx expo run:ios." },
            { approach: "Native config / new module", setup: "Nothing", cost: "Full rebuild", note: "npx expo run:ios --device. New pods / app.config changes won't reload." },
            { approach: "Quick lockfile check", setup: "—", cost: "—", note: "git diff dev..claude/<name> -- package-lock.json" },
          ],
        },
        steps: [
          { h: "Preflight — find out where the agent's work lives", c: "# fetch = grab the latest from GitHub without changing your files\ngit fetch origin\n\n# Did Claude leave a worktree on disk?\ngit worktree list\n# → look for a line like: <claudePath>  [<claudeBranch>]\n\n# Is there a PR for it yet? (only matters for step 5)\ngh pr list --search \"head:<claudeBranch>\"\n\n# Outcomes:\n#   Local worktree exists           → step 2a (cd in)\n#   No worktree, branch on origin   → step 2b (checkout in main)\n#   Neither shows up                → ask Claude if it pushed; rerun git fetch origin" },
          { h: "2a · Local worktree exists — cd into it", c: "cd <claudePath>\n\n# What you just entered is a real working copy of <claudeBranch>.\n# It shares the .git folder with your main repo but has its own files,\n# same as if you'd cloned the repo into a sibling directory.\n\n# Important: do NOT also `git checkout <claudeBranch>` in your main repo.\n# Git enforces one-worktree-per-branch and refuses with:\n#   fatal: '<branch>' is already used by worktree at '<path>'\n# (See Gotchas below if you hit this.)" },
          { h: "2b · No local worktree — checkout in your main repo", c: "# Safe to check out here because no other worktree has this branch.\ngit checkout <claudeBranch>\n\n# Your main repo's node_modules carries over from <base> — usually enough.\n# Only reinstall if the lockfile changed (next step)." },
          { h: "Make node_modules available", c: "# The lockfile records exact dep versions. If it changed, you need to\n# install. If it didn't, you can borrow main repo's node_modules.\ngit diff <base>..<claudeBranch> -- package-lock.json\n\n# A. No output (lockfile unchanged) — borrow main repo's node_modules:\n#    Only needed for path 2a (cloud-path 2b already has them from main).\nln -s ../../../node_modules node_modules    # path 2a only · ~0s\n\n# B. Diff shown (lockfile changed) — install fresh in this directory:\nnpm install                                    # ~1–3 min\n\n# When in doubt, `npm install` is always safe — just slower than the symlink." },
          { h: "Verify on-device", c: "# JS-only changes (fastest): reload your existing dev client.\nnpm start                       # → open dev client → Shake → Reload\n\n# Native changes (new pods, native module, app.config edits):\nnpx expo run:ios --device       # or: --android (full rebuild)\n\n# If Metro errors with 'Cannot find module …',\n# node_modules isn't there yet — go back to step 3." },
          { h: "Decide what to do with this", c: "# A. Looks good → ship it.\n#    If no PR yet:\ngh pr create --base <base> --title \"<one-line summary>\"\n#    Then merge:\ngh pr merge --squash --delete-branch\n\n# B. Needs more work → keep iterating.\n#    From the worktree (path 2a) or main (path 2b):\ngit add -A && git commit -m \"...\" && git push\n#    Then return to step 4. Tell Claude what you want next.\n\n# C. Not what you wanted → discard it.\n#    Don't push or merge. Jump to Lane 3 to clean up the worktree\n#    (if any) and delete the branch." },
          { h: "Sync your working branch (only after merging)", c: "# Skip this step if you chose B (iterate) or C (discard) above.\n\n# If you were inside a worktree, hop back to main first:\ncd /path/to/main-repo\n\n# Fast-forward your local <base> to the merged state:\ngit checkout <base>\ngit fetch origin <base>            # NOT 'origin/<base>' — space, not slash\ngit merge origin/<base>\n\n# Then Lane 3 to remove the agent's worktree if step 2a applied." },
        ],
        diagram: "review-l2",
        errorTable: [
          { sym: "fatal: '<branch>' is already used by worktree at '<path>'", cause: "A local Claude Code agent with isolation:\"worktree\" has the branch checked out at .claude/worktrees/<name>. Git refuses to check out the same branch in two places at once — that's the worktree contract.", fix: "Don't checkout in main. cd <path> and verify there (recommended). Or remove the worktree first: git worktree remove <path>, then git checkout claude/<name>." },
          { sym: "Cannot find module … / npm start exits in the worktree", cause: "The agent's worktree is a bare git checkout — no node_modules dir. Metro can't bundle without it.", fix: "Step 3 — Make node_modules available. Lockfile unchanged → ln -s ../../../node_modules node_modules. Lockfile changed → npm install in the worktree. Concepts → 'ln -s node_modules' covers the safe/unsafe split." },
          { sym: "Unable to resolve module ...", cause: "package-lock.json changed on the agent's branch but you skipped npm install (or the symlinked node_modules is stale).", fix: "npm install in the worktree (or main repo, whichever you're verifying in), then retry npm start / run:ios. The symlink trick only works when the lockfile matches." },
          { sym: "Native module 'X' is not installed", cause: "The agent added a native dep — pods/Gradle haven't been built for it yet.", fix: "npx expo run:ios --device (full rebuild). A reload in the existing dev-client won't pick up new native code." },
        ],
        gotchas: [
          { h: "Local agent? Don't checkout — cd into the worktree", b: "git worktree list will show .claude/worktrees/<name> bound to claude/<name>. That's a working checkout already. Trying to git checkout the same branch in main repo errors with 'already used by worktree' — git enforces one-worktree-per-branch." },
          { h: "Worktree has no node_modules? Symlink instead of reinstalling (when safe)", b: "The agent's worktree is just a bare checkout. If git diff dev..claude/<name> -- package-lock.json is empty, share the main repo's: ln -s ../../../node_modules node_modules. Zero disk cost, ~0s. Concepts → 'ln -s node_modules' covers when this is safe vs. when it silently breaks." },
          { h: "JS-only PR? npm start + Reload beats a full rebuild", b: "If the PR doesn't touch native config, app.config.ts, or pods, your existing dev client already has the right native shell. npm start → open dev client → Shake → Reload bundles fresh JS in seconds." },
          { h: "git fetch origin dev — not 'origin/dev'", b: "git fetch <remote> <branch> takes them as two arguments separated by a space. 'origin/dev' is the local ref name git creates after the fetch; you don't pass it back to fetch as one token." },
          { h: "After merge, clean up the agent's worktree (Lane 3)", b: "If you went via path 2a, the worktree at .claude/worktrees/<name> is now stale. git worktree remove <path> from the main repo, then git branch -d claude/<name>." },
          { h: "Want true parallel verify on a cloud-agent PR? Promote it to a local worktree", b: "git worktree add ../<dir> claude/<name> — your main repo stays on dev, verification runs in the worktree, after merge you clean up with Lane 3." },
        ],
        quick: "# 1 · preflight — what state are you in?\ngit fetch origin\ngit worktree list                            # local worktree? → 2a, else → 2b\ngh pr list --search \"head:<claudeBranch>\"    # PR exists yet? (affects step 5)\n\n# 2a · local worktree\ncd <claudePath>\n\n# 2b · no worktree (cloud agent, or local push without checkout)\ngit checkout <claudeBranch>\n\n# 3 · make node_modules available\ngit diff <base>..<claudeBranch> -- package-lock.json\nln -s ../../../node_modules node_modules     # path 2a + lockfile unchanged\nnpm install                                    # any path + lockfile changed\n\n# 4 · verify on-device\nnpm start                                    # JS-only: dev client → Reload\nnpx expo run:ios --device                    # native changes: full rebuild\n# 'Cannot find module …' → back to step 3\n\n# 5 · decide\n# A. ship:    gh pr create --base <base>  (if no PR)  →  gh pr merge --squash --delete-branch\n# B. iterate: git add -A && git commit && git push    →  back to step 4\n# C. discard: do nothing here — jump to Lane 3 to clean up\n\n# 6 · sync (only after merging)\ncd /path/to/main-repo\ngit checkout <base>\ngit fetch origin <base>\ngit merge origin/<base>\n# then Lane 3 if step 2a applied",
      },
    ],
  },
  {
    id: "lane-3",
    n: 3,
    short: "Cleanup",
    title: "Remove a finished (or stuck) worktree",
    tag: "always from the main repo",
    accent: "var(--l3)",
    accentBg: "var(--l3-bg)",
    accentInk: "var(--l3-ink)",
    when: "You're done with a worktree, or one's gone sideways and you need it gone.",
    whyNot: null,
    steps: [
      { h: "Confirm the path", c: "git worktree list" },
      { h: "Remove the worktree", c: "# from the main repo — NEVER from inside the worktree itself\ngit worktree remove <path>" },
      { h: "Delete the branch", c: "git branch -d <branch>\n# use -D if unmerged but you're sure" },
    ],
    diagram: "lifecycle-l3",
    stuck: {
      h: "Universal \"stuck\" fixer",
      b: "Safe to run blindly on a stuck worktree:",
      c: "git worktree unlock <path> 2>/dev/null\ngit worktree remove --force <path> 2>/dev/null\ngit worktree prune -v\ngit branch -D <branch-name>          # optional",
    },
    errorTable: [
      { sym: "'X' is not a working tree", cause: "You already rm -rf'd the dir; only the admin entry remains.", fix: "git worktree prune -v" },
      { sym: "worktree is locked", cause: "An agent locked it for safety, then exited without unlocking.", fix: "git worktree unlock <path>, then remove or prune." },
      { sym: "not a working tree (path looks right)", cause: "Path typo · trailing slash · symlink-resolved mismatch.", fix: "Copy-paste path verbatim from git worktree list." },
      { sym: "branch 'X' is checked out at <path>", cause: "Branch is still bound to a (possibly stale) worktree.", fix: "Remove the worktree first, then delete the branch." },
    ],
    gotchas: [
      { h: "1 · git worktree remove first — never rm -rf", b: "rm -rf leaves git's admin entry orphaned. Use prune to recover." },
      { h: "2 · If you already rm -rf'd it, use prune (not remove)", b: "remove looks for a working tree that no longer exists. prune cleans the dangling admin entry." },
      { h: "3 · If locked, unlock is a prerequisite", b: "Claude Code locks each agent's worktree with the agent's PID and leaves a stale lock if the agent crashes." },
      { h: "4 · Run all worktree commands from the main repo", b: "Not from inside a worktree — git refuses, and the error message is confusing." },
    ],
    quick: "# happy path\ngit worktree remove .claude/worktrees/<name>\ngit branch -d <branch>\n\n# stuck path (universal fixer)\ngit worktree unlock <path>; git worktree remove --force <path>; git worktree prune -v\ngit branch -D <branch>",
  },
];

const MATRIX = [
  { size: "Typo · 1-line fix · trivial refactor", parallel: "No", lane: 1 },
  { size: "Feature · multi-file refactor", parallel: "Yes (Claude agent)", lane: 2 },
  { size: "Verify clean build · risky experiment", parallel: "Either", lane: 2 },
  { size: "Done with a worktree", parallel: "—", lane: 3 },
];

const CONCEPTS = [
  {
    id: "switch-vs-checkout",
    h: "git checkout vs git switch vs git restore",
    body: [
      { p: "git checkout is overloaded — it does three different jobs:" },
      { code: "git checkout main               # \u2460 switch branches\ngit checkout abc123 -- foo.ts   # \u2461 restore file from a commit (destructive)\ngit checkout -- foo.ts          # \u2462 discard unstaged changes (destructive)" },
      { p: "Git 2.23 (2019) split that into narrower, safer commands:" },
      { table: [
        ["Old", "New", "Job"],
        ["git checkout <branch>", "git switch <branch>", "Switch branches"],
        ["git checkout -b <branch>", "git switch -c <branch>", "Create + switch"],
        ["git checkout -- <file>", "git restore <file>", "Discard local changes"],
        ["git checkout <commit> -- <file>", "git restore --source=<commit> <file>", "Restore file from a commit"],
      ]},
      { p: "Why it matters: git switch refuses to silently restore a file if you typo a branch name as a path. git checkout will happily wipe your edits." },
    ],
  },
  {
    id: "locked-worktrees",
    h: "What \"locked\" means for worktrees",
    body: [
      { p: "A flag stored as a plain text file at .git/worktrees/<name>/locked whose contents are the lock reason. It does only two things:" },
      { list: [
        "Blocks git worktree prune from removing the admin entry.",
        "Blocks git worktree remove unless you pass --force.",
      ]},
      { p: "It does not lock files, prevent commits, or affect the branch. It exists so housekeeping doesn't yank a worktree out from under a running tool. Claude Code locks each agent's worktree with the agent's PID as the reason — and leaves a stale lock behind if the agent crashes." },
    ],
  },
  {
    id: "pnpm-worktrees",
    h: "Why pnpm makes worktrees viable for Expo",
    body: [
      { list: [
        "npm / yarn: each worktree gets its own full node_modules copy → 1–2 GB, multi-minute install.",
        "pnpm: global content-addressed store + hard-links into each project → near-zero extra disk, ~10s install.",
      ]},
      { p: "Expo + EAS support pnpm out of the box. The one Expo-specific quirk: add shamefully-hoist=true to .npmrc if a native module breaks under strict isolation." },
    ],
  },
  {
    id: "ln-s-node-modules",
    h: "ln -s node_modules — when to symlink, when NOT to",
    body: [
      { p: "ln -s creates a symbolic link — a pointer that makes one folder look like it exists in a different location. No copy, no disk cost." },
      { p: "Reading the command piece by piece:" },
      { code: "ln -s ../../../node_modules node_modules" },
      { list: [
        "ln  — the link command",
        "-s  — symbolic (a pointer, not a copy)",
        "../../../node_modules  — the target (where the real folder lives)",
        "node_modules  — the link name (where the pointer appears)",
      ]},
      { p: "In plain English: \"create something called node_modules in my current directory, but it's really a pointer to the folder three levels up.\" From a worktree at <repo>/.claude/worktrees/<name>/, three .. takes you up out of <name> → out of worktrees → out of .claude → back to <repo>. So the link resolves to <repo>/node_modules." },
      { p: "When this is the right call (Lane 2 · Mode B):" },
      { list: [
        "Short-lived verification of a Claude Code agent's worktree.",
        "Lockfile is unchanged — git diff dev..claude/<name> -- package-lock.json prints nothing.",
        "Pure-JS or unchanged-deps PR; no new native modules.",
        "You'll remove the worktree (Lane 3) shortly anyway.",
      ]},
      { p: "When it isn't (Lane 2 · Mode A):" },
      { list: [
        "Long-lived worktree where you're actively editing deps.",
        "Different package-lock.json between worktree and main → npm resolves modules from a node_modules tree that doesn't match the lockfile → silent module-resolution bugs that look like \"works on my machine\" weirdness.",
        "If you need parallel worktrees with independent deps, move to pnpm instead — see the entry above.",
      ]},
      { p: "Two practical notes:" },
      { list: [
        "Adjust the depth. Count the .. from your worktree to the main repo. From .claude/worktrees/<name>/ that's three. From a sibling dir like ../verify/ it's one (ln -s ../<main>/node_modules node_modules).",
        "It's a relative link. If you move the worktree later, the link breaks — but you were going to remove it via Lane 3 anyway.",
      ]},
    ],
  },
  {
    id: "diagnostics",
    h: "Diagnostics — what to run when confused",
    body: [
      { code: "git worktree list                  # all worktrees + lock state\ngit worktree list --porcelain      # machine-readable, shows lock reason\ncat .git/worktrees/<name>/locked   # the lock-reason text\nls .git/worktrees/                 # admin entries on disk\ngit cherry <base> <branch>         # which commits are already on <base>\nps -p <pid>                        # is the locking process still alive?" },
    ],
  },
];

const ANTI = [
  { h: "rm -rf .claude/worktrees/<name>", b: "to \"clean up\" — leaves git's admin entry orphaned." },
  { h: "Create a PR for every typo fix", b: "burns time, pollutes the PR list." },
  { h: "Run git worktree remove from inside the worktree", b: "you're trying to remove. Always run from the main repo." },
  { h: "Symlink one worktree's node_modules into another", b: "breaks the moment lockfiles diverge." },
  { h: "git branch -D a branch still attached to a worktree", b: "git will refuse, and rightly so." },
];

window.CHEAT = { LANES, MATRIX, CONCEPTS, ANTI };
