option-berthGuide

Several worktrees at once

A run belongs to the checkout that started it. Two worktrees can run the same repository at once without sharing a port, a database or a stop.

A run belongs to its checkout

Different worktrees of one repository publish as <project> and <project>@<worktree>, so a group-scoped stop on one does not reach the other. status --json publishes the checkout's identity, and that identity is what down --identity uses after a directory is gone.

see the identities this machine has seen
oberth status --json
oberth worktrees

Ports are allocated, not guessed

A service with port: auto gets its address from the run, and the engine injects it through PORT. A service's own env: PORT: 3000 overrides the injected value and makes up --wait wait on the wrong port; use ${port} instead. If a framework quietly switches to another port when one is taken, prefer its strict-port option so a conflict becomes a readable failure fact.

Data can follow the worktree

${worktree} is a stable identity derived from the canonicalized checkout root. It is the same on every start for one worktree and different for the main checkout and each linked worktree, which makes it suitable for database names and relative data directories.

one identity, many uses
DB_PATH: .berth/${worktree}/app.db
DATABASE_URL: postgres://localhost/app_${worktree}

Moving the checkout directory yields a new identity; the old data is not moved with it. That is deliberate: the engine reports the correspondence between identities and paths, and a person decides what to keep.

When a checkout disappears

worktrees --gone keeps only identities whose checkout no longer exists. down --identity still stops the runs and listeners belonging to one and returns its port: auto reservations. The engine only reports and stops; it never deletes a database, a data directory or a ledger row.

Follow changes without polling

oberth events is the read-only subscription for parallel work. It takes a worktree, repository or explicit workspace filter and writes NDJSON: the first line is a state.snapshot, later lines are state.changed summaries.

  • The first record is a snapshot; later records are changes. A reconnect after a drop passes the last applied seq as after_seq.
  • Only runtime fact summaries cross the stream — no logs, commands, working directories or environment variables.
  • When the queue window has expired or the state chain is discontinuous, the stream returns a full snapshot instead of pretending the gap did not happen.
  • --once reads the snapshot only and registers no workspace membership; a human running it is mainly troubleshooting.

Edit this page

Type to search every chapter and section.