Rochet / config
A worktree is a checkout of tracked files and nothing else.
.rochet/config.json is how a repository says what else it
needs.
No node_modules. No .env — that one is
gitignored, which is exactly why git does not bring it. No database, and every
workspace’s dev server reaching for port 3000 at the same time. Without
this file the answer to all of that is the terminal, which means the first
thing you do in a new workspace is run the install by hand, in every workspace,
every time.
So a repository gets to say four things, and Rochet does them. All four are optional, and a repository that says none of them behaves exactly as it did before this file existed.
{
"setup": "bun install",
"run": "./bin/dev",
"teardown": "docker compose down",
"carry": [".env", ".env.local"]
}
Anything else in the file is ignored in silence. This file is edited by hand
and will acquire typos, and a workspace that refused to be created because you
wrote "scripts" would be a worse answer than one that boots
without your script. A file that is not valid JSON is reported —
the repository sheet shows the parse error — and then stepped over, so a
bad file never leaves you unable to open a terminal and fix it.
The install, the codegen, the seed — whatever makes the checkout
runnable. It runs in the new worktree, after carry and before the
agent’s first turn, and the workspace shows its last line of output on
the activity line while it works.
It gets ten minutes, which is generous because a cold
bun install is a real minute and a Docker image pull is several.
It is bounded at all because a script that waits on input would otherwise hold
a workspace in setting up forever with no way to find out why.
If setup fails, the workspace still exists and so does the worktree — Rochet says why and declines to start the first turn, rather than unwinding a checkout you might have wanted. An agent handed a worktree with no dependencies in it spends its opening minutes discovering that, and the answer it comes back with is about the install rather than about your task.
Offered as a button rather than run automatically. A dev server is something you want sometimes, and one spawned in every workspace you open is six of them by lunchtime.
Run is a place rather than an action: one pinned terminal tab per workspace
that cannot multiply, so pressing the button twice hands you back the session
you already have instead of putting a second dead server on a port the first
one is holding. Your run command is expected to listen on
$ROCHET_PORT.
Runs on archive, before the worktree is removed, with sixty
seconds. Best-effort on purpose and never allowed to stop an archive:
the whole point of archiving is that the worktree goes, and a
docker compose down that fails must not leave you with a workspace
you cannot get rid of. The containers it was supposed to stop are the smaller
problem.
An array of paths, relative to the repository root, copied out of your project checkout into each new worktree before setup runs. It exists for one thing — the gitignored files a checkout needs to work, which by definition git will not bring — and should not grow into a general sync.
A file you named that is not there is not an error. A
repository listing .env.local for the people who have one is being
correct, not aspirational. Four things are refused, and each refusal is said
out loud on the workspace’s setup line:
..;A secret cannot be tracked, and a path on your laptop should not be. So there are two files:
The local file wins key by key, not wholesale. The
commonest real case is a tracked file that is right about three things and
wrong about one, and a local file that had to restate run and
teardown in order to change setup would drift out of
date the moment the tracked one moved.
Rochet says which file answered. A repository whose
setup comes from the local file boots on your machine and on
nobody else’s — “works here and nowhere else” is a fact
that has to be legible before it bites, not afterwards, so the repository sheet
names any key the local file decided.
Both files are read from your project checkout, never from a worktree, and they are read fresh every time rather than remembered. An edit takes effect at once, for every workspace, including the ones already open. Rochet keeps no copy: something else on your machine can change these files — you, in an editor, in the next window — and a copy in a database would start disagreeing with your checkout the first time it did, with nothing able to say which answer was true.
All three commands, and every terminal you open in a workspace, get the same variables. A line you type by hand and the same line behind the Run button cannot disagree.
1ROCHET_ROOT earns its place on real projects rather than in
theory. A Rust or Go build cache belongs to the checkout rather than to each
worktree, and a setup script writing
target-dir = "$ROCHET_ROOT/target" is how a repository says so
— one project’s src-tauri/target is 72 GB, and
three workspaces with their own would be 216.
Each workspace gets a port and the seven above it. A project needing an API, a client and a database needs three numbers, and handing out one would move the collision rather than fix it.
ROCHET_PORT = 7400 + slot × 8 slot 0 → 7400 .. 7407 slot 1 → 7408 .. 7415 slot 2 → 7416 .. 7423
Slots are per repository and the lowest free one is taken, so the numbers stay small and predictable. A slot is stored on the workspace rather than counted from its position in a list: archiving a workspace frees its slot, and a dev server whose port moved because you restarted the daemon would break every link you had open.
Through sh -c, deliberately. What people write in these fields
is a shell line — bun install && ./bin/seed —
and a format that took an argv array instead would be a format everybody gets
wrong once.
Input is closed. A package manager that stops to ask you to confirm something fails immediately and says so, rather than waiting on a terminal nobody is looking at until the timeout becomes the only thing that ever answers it. If a script needs to be told something, it needs a flag.
Every repository has a scratch: its own checkout, present in the rail
alongside the workspaces. None of this applies to it, and that is deliberate.
It runs no setup — installing dependencies and generating
files into somebody’s actual checkout is not Rochet’s to do —
it has no ROCHET_PORT, because an offset port would be Rochet
changing how your repository behaves outside itself, and so it is offered no
Run button. In your own checkout you already know how to start your own
project.
Rochet develops on itself. Its tracked file is three lines:
{
"setup": "bun install",
"run": "./bin/dev",
"teardown": "./bin/dev --clean"
}
bin/dev is a script in the repository rather than a command
inline, which is worth copying: it can read $ROCHET_PORT, give the
dev daemon a state directory of its own keyed to that port, and pair with a
--clean that removes exactly the one it made. A run
and a teardown that know about each other are much easier to write
in a file than in two JSON strings.