Skip to main content
The exact rules for dev runs and dependencies, for looking things up. What a single command does is in the CLI reference, and timers and box sizes are in Configuration. The Services and Connect a frontend to a backend pages explain the ideas.

Dev runs

When tesser dev restarts the server

tesser dev reuses the box for this worktree and service. On a box whose server is already running, it only syncs, and prints when the server started and that its inputs are unchanged. It starts the server again when:
  • the server is not running, or its app crashed under a file watcher
  • the manifest or the env changed
  • a file setup or dev depends on changed:
    • the env files the manifest lists
    • any package.json, lockfile, or toolchain pin such as .nvmrc
    • a Dockerfile or compose file
    • any file the setup command names
    • any shell script the dev command names (a .sh, .bash or .zsh file, or one with no extension)
    • every file in the manifest’s directory except .md files and the other services’ manifests
A file a command names counts only when it is not gitignored, so a build output such as node_modules/.bin/... never forces a restart. A source file the dev command names, such as src/server.ts in bun --hot src/server.ts, does not count either, because the server reloads it.

While tesser dev waits

  • It prints the last line of the server’s log every 30 seconds. When the health check keeps returning the same 5xx status, the line says for how long.
  • When setup prints nothing new for 5 minutes, it prints a warning with the last line and keeps waiting. Raise [health] timeout only when that line still shows progress.

A service name instead of a box id

tesser status, tesser logs, tesser sync, and tesser wait take a service name for this worktree’s box of that service.
  • When the worktree has no box for it, status, logs, and wait use the service’s shared instance, and status and logs say so on stderr.
  • sync never falls back to the shared instance. It fails and suggests tesser dev <service>.
  • For a script, tesser status <service> --json | jq -r .boxId prints the box id.

Dependencies and shared instances

A dependency with nothing behind it

  • dev, make <service>, and make --ensure-running still start the box, and print one line per such port on stderr:
  • When this worktree has a manifest for api, the line first names tesser dev api in this worktree, which backs the dependency even when it starts after this box (see Your own box from the same worktree).
  • When a make api --ensure-running has started a new shared instance but not switched over yet, that instance is staged, and the line names it:
    The dependency reaches the new instance as soon as that command finishes, with no wire. If the command is no longer running, running it again with the same commit finishes the switch.
  • dev prints these lines when it returns: after the service is serving, or just before the error when the run fails, so they come at the end of its output. With --detach, it prints them when the box starts. tesser wait, and tesser sync --restart without --detach, print them when they return. Each check reads the box’s current wiring, so a port wired in the meantime is not reported.
  • On the box, every connection to that port is reset. While the service runs, the box notes the reset in the dev log (tesser logs), at most once a minute per port. On the laptop, the request gets a 503 naming the tesser wire command that fixes it.

Your own box from the same worktree

  • A dependency with no wire and no shared instance reaches your own dev box of that service from the same worktree, if one exists. The worktree is the folder that tesser dev ran in.
  • Only boxes you started with tesser dev from your own login count. A teammate’s box, a box from another worktree, and a box started with a service-account or CI token never do.
  • With several such boxes, the newest one wins. Removing it moves the dependency to the next one, or to nothing.
  • A wire or a shared instance always takes precedence over this fallback. tesser wire <box_id> api --shared with no shared instance running says the dependency routes to the same-worktree box instead.
  • When a shared instance takes precedence over your own box of that service from this worktree, dev says so and prints the wire that routes the dependency to your box:
    If the box that depends on it started first, dev api prints the same advice when it returns:
    Both lines appear only when both boxes are yours.
  • dev prints the fallback on stderr when it returns:
  • tesser wire <box_id> lists such a dependency as api -> <box_id> (same worktree).
  • If another box from the worktree was already running when this one began backing its dependency, dev prints:
  • Handing a box to another member clears its worktree, so it stops backing your other boxes.

Waking dependencies

  • dev starts only the requested services. It wakes the dependencies you own and the shared instances it depends on, and once its own server is ready it waits for them to serve, printing which ones it is waiting on.
  • If a dependency is removed, its server exits, it is still asleep after 15 seconds, or it does not come up in time, dev reports that it is not serving and finishes anyway. tesser status <box_id> shows where that box is.
  • dev never creates missing dependencies. make --ensure-running creates shared instances.

make --ensure-running

  • It creates a box, checks out the commit, runs setup and dev, waits for the health check, and makes the box the org’s shared instance.
  • With the same commit again, it does nothing and prints the same box id, unless the shared instance is unhealthy, in which case it runs setup and dev again on the same box. With a new commit, it starts a new box, waits until it is healthy, switches over, and removes the old box.
  • From a worktree that already has its own box of the service, when no shared instance runs that commit yet, that box becomes the shared instance and keeps its warm build. This applies only when the box is the size the manifest sets, is not kept, and has no --sleep-after of its own; otherwise a new box is created. The CLI resets the box to the commit, deletes files git neither tracks nor ignores (installs and build output stay), runs setup and dev again with the org’s env values, waits for the health check, and switches over. The box then belongs to the org, and the worktree’s next tesser dev of the service starts a new box.
  • --restart runs setup and dev again on the same box even while the health check passes, for a server that is stuck but still answers it. On a staged or draining instance it fails and changes nothing, and on the current one it does not switch the org over again.
  • A shared instance belongs to the org, so it cannot be synced to. Its env comes from tesser env set and the committed files in the manifest’s env.load.

During a switch

  • Both boxes run dev: the new one from when make starts it, the old one, if awake, until it is removed 60 seconds after the switch.
  • Both see TESSER_SLOT=pinned, so a queue keyed on TESSER_SLOT has workers on both commits, and the old commit can take jobs meant for the new one. Removal kills any job still running on the old box. Make jobs safe to retry and to run on either commit.
  • If a job’s producer and workers run on the same box, key the queue on TESSER_BOX_ID instead. Jobs still in the old box’s queue when it is removed never run.

tesser service show

Prints one service’s repo and, for each shared instance, the full commit sha, the box id, and a tesser ssh command that prints the manifest it runs. That reads the file on the box, which can differ from the copy committed at that sha. --json prints the same as JSON.

tesser wire

  • It refuses a target that is not a box in your org, has been removed, or runs a different service than the dependency names.
  • After saving, it waits until this laptop’s daemon routes the change. If the daemon has not loaded the wire after 70 seconds, the command fails, but the wire stays saved. tesser daemon stop then tesser use restarts the daemon.
  • A wire changes where new connections go but restarts nothing. If the box’s server ran while the dependency had nothing behind it, its client may have given up, and requests can hang even after the wire. Then wire prints:
  • A wire names a box and stays put. If that box is removed, the dependency fails loud instead of falling back to the shared instance. Re-wire it or return it to --shared.
  • A wire connects boxes, whichever worktree or repository they came from.
  • The panel marks a branch custom wiring when a laptop port reaches a different box than the box’s own dependency on that port, and names any route that is failing. Laptop focus can override subtrees without changing wires.
  • tesser dev reuses the box for a worktree and service, so each simultaneous frontend-plus-backend test needs its own frontend worktree. In a monorepo, run the frontend from the backend change’s worktree.
  • When both a dashboard and its backend depend on a third service, wire both to the copy meant for the test. Focusing the dashboard brings every reachable service to localhost, but does not fix inconsistent wiring on the boxes.

tesser exec --deps-of

The workbench gets that service’s dependency ports for as long as the command runs. They resolve exactly as for that service’s box in this worktree, wires included. --in <service> on its own only changes the working directory to the service’s root.