Skip to content

Set Up Your Operator Repo

Your identity, fleet registry (machines.json) and project manifests (provisions/*.json) belong in an operator repo, a git repo of your own that is separate from the BoB source checkout. This page takes a machine from “no operator declared” to “operator repo holds the fleet, with history”. For what the operator repo is and why it exists, see Operators.

Terminal only. These commands edit git repos and $BOB_HOME outside any session.

  • A clean BoB source checkout (git -C ~/projects/bigbrain status shows nothing).

  • Know where the operator repo should live. ~/projects/bob-operator is the checked default, so a repo there is found with no pointer at all.

  • Check where you stand:

    Terminal window
    bob operator show # exit 3 = no operator declared yet

bob operator init <dir> --link does four things, and commits nothing:

Writes What it is
operator.json Your identity: id, label, forge, people[], routes, bob_upstream, secrets[] (names and sources only).
machines.json A placeholder fleet with one example machine, example-laptop — only when no fleet exists yet. While the BoB checkout still holds machines.json, init leaves it out ([=] machines.json (not written — the fleet at … still resolves …)), so this host keeps resolving until migrate moves the real fleet in.
provisions/README.md, README.md, .gitignore, .github/workflows/validate.yml The rest of the scaffold, plus the CI workflow that runs BoB’s validators.
$BOB_HOME/.bob-operator (only with --link) The pointer that makes BoB resolve this repo.

It also runs git init in <dir>, and prints the operator id it used — when you leave out --id, the id is derived from the directory name, so check it.

init exits 1 and writes nothing when <dir> is:

  • inside the BoB source checkout, $BOB_HOME, or any git worktree of the BoB repo — operator data never lives in the product or the deploy target, and no flag overrides this;
  • a directory that already holds operator.json (--force rewrites the scaffold files);
  • inside an existing git repo with tracked files (--into-existing scaffolds into it anyway).
  1. Scaffold and link.

    Terminal window
    bob operator init ~/projects/bob-operator --id <your-slug> --label "<Your Name>" --link

    Pass --forge-owner <account> if your repos live under a different account than the id.

  2. Make both trees clean. migrate refuses unless the BoB checkout and the operator repo have no uncommitted changes, and the scaffold is uncommitted. Edit operator.json first (at least people[] and forge), then commit the scaffold:

    Terminal window
    cd ~/projects/bob-operator
    git add -A && git commit -m "chore: scaffold operator repo"
    git -C ~/projects/bigbrain status --short # must print nothing
  3. Dry-run the move. This prints what moves (with each path’s commit count), what stays in the source, and what happens to the placeholder. It changes nothing.

    Terminal window
    bob operator migrate

    Expect machines.json and every provisions/<project>.json in “would move with history”. provisions/_bob-home.json and provisions/_default.json belong to the product and stay in the source.

  4. Move it.

    Terminal window
    bob operator migrate --apply

    Add --force only if the dry run named a file already at a migrated path in the operator repo. The command:

    • replays every commit that touched those paths into the operator repo (git format-patch → git am --3way), so history comes along;
    • removes the paths from the BoB checkout on a new branch, chore/2270-move-operator-data.
  5. Publish both sides.

    Terminal window
    cd ~/projects/bob-operator && git push -u origin HEAD # after creating the remote
    cd ~/projects/bigbrain && git push -u origin chore/2270-move-operator-data
    gh pr create --fill # merge it, then deploy
Terminal window
bob operator show # exit 0: resolved and valid, names the rung
node ~/.claude/scripts/fleet/machine.js whoami # prints THIS machine's entry
bob doctor # the operator line names your repo

whoami must print a real machine. If it still fails, the operator repo’s machines.json is not your fleet. Check it before doing anything else.

Recovery: the scaffold is shadowing a live fleet

Section titled “Recovery: the scaffold is shadowing a live fleet”

Only an operator repo scaffolded before #2627 can get here — init no longer writes the placeholder while a real fleet resolves. Symptom: you ran init --link but not migrate, and now machine: could not resolve current host, bob doctor and fleet snapshots all read example-laptop.

  1. Park the placeholder so the fallback to the BoB checkout’s machines.json takes over again. This takes effect immediately:

    Terminal window
    cd ~/projects/bob-operator
    git rm --cached -q machines.json 2>/dev/null; mv machines.json ~/machines.scaffold.json
    node ~/.claude/scripts/fleet/machine.js whoami # your real entry again (with a legacy-location warning)
  2. Commit so the operator tree is clean: git add -A && git commit -m "chore: park the scaffold machines.json".

  3. Migrate deliberately: run step 3 (dry run) and step 4 above. With the placeholder gone, migrate --apply needs --force only if the dry run names another file already in the operator repo.

Deleting $BOB_HOME/.bob-operator is not the fix. A repo at ~/projects/bob-operator is still found on the default rung, so the placeholder keeps shadowing. At any other location, unlinking hides operator.json as well, and every manifest write (bob provision add, --init) is refused.

The operator repo already exists, so do not run init again. It refuses a directory that holds operator.json, and --force would rewrite your files with the scaffold. Clone the repo to the checked default instead:

Terminal window
git clone <your operator repo URL> ~/projects/bob-operator
bob operator show # resolves on the "default" rung — no pointer needed

For any other location, export BOB_OPERATOR=<dir> in your shell profile.