Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ docs/devteam/

**What matters most.** The supervisor judges each goal's impact against the project's strategy and rates every question it asks (how much rides on the answer, how soon, and which other questions an answer would settle). The kit counts the rest (tickets held up, a default) and orders the Inbox from it, with the next best step on top and the reason on every card; `devteam.mjs next` prints the same. The order is computed on every read and stored nowhere. The owner corrects a goal's impact with their own value, and it stands.

**Many projects, one machine.** Each project has its own tmux session (`devteam-<project>-<6 hex of its path>`, saved in `run/tmux-session`) and its own dashboard port: the next free one in 4321–4345, saved in `run/dashboard-port` and reused. Any running dashboard also serves the hub at http://127.0.0.1:4320: a tab per instance on the machine (its name, with a badge when its Inbox has something), that instance's own dashboard under the selected tab, and one "Notify me" for new Inbox items across all of them. The hub keeps no state and takes no action: each dashboard, served by its own repo's kit, stays on its own address inside the hub's frame, and may be framed only by itself and the hub. A dashboard answers only on the address it serves (`127.0.0.1` and `localhost`, or the `--host` it was started with), and the hub only on its own: a page elsewhere gets nothing, not even a read.
**Many projects, one machine.** Each project has its own tmux session (`devteam-<project>-<6 hex of its path>`, saved in `run/tmux-session`) and its own dashboard port: the next free one in 4321–4345, saved in `run/dashboard-port` and reused. Any running dashboard also serves the hub at http://127.0.0.1:4320: a tab per instance on the machine (its name, with a badge when its Inbox has something), that instance's own dashboard under the selected tab, and one "Notify me" for new Inbox items across all of them. It opens on its **Overview** tab: a card per project saying whether it is building, moving, fully blocked on your answers (or partly: some tickets wait, the rest can move), stalled, idle or parked, with its kit version (amber when behind the newest one running here), Inbox count, workers, the goal it is on, tickets merged of planned, what needs you, and twelve weeks of activity. Each card has **Run**, **Drain** and **Park** for that project's fleet, and the page has **Park all** and **Resume all**; Park asks twice. The hub's server keeps no state and takes no action: for Run, Drain and Park the page asks that project's dashboard, in its frame, and the dashboard does it as a click there would, for its parent page only. Each dashboard, served by its own repo's kit, stays on its own address inside the hub's frame, and may be framed only by itself and the hub, so whatever is served at the hub's address can steer every fleet on the machine: an owner who finds something else on port 4320 should not open it (a dashboard notes it in `run/dashboard.log`). A dashboard answers only on the address it serves (`127.0.0.1` and `localhost`, or the `--host` it was started with), and the hub only on its own: a page elsewhere gets nothing, not even a read.

**Artifacts.** Any session can draw an artifact, a page that shows the owner an idea instead of describing it: a UI mock, a flow, a mock docs site, a 3D model. It lives in `project/artifacts/<slug>/` and lands with the PR it was drawn in; the dashboard's **Library** lists landed ones and live drafts from every worktree beside the link registry, and opens each in a centered viewer or its own tab. An artifact loads its own files and pinned libraries from cdnjs, jsdelivr and unpkg, runs sandboxed with no other network and no reach into the dashboard, and sends nothing back: a decision on one is a question that links it, by name and inline (`[[name]]`). The owner can ask for one ("draw me a mock docs site"), and planners draw them for any goal with UI or a flow. `devteam.mjs artifact new|check`, `artifacts`. On a cloud box the owner can also publish a landed artifact to anyone, at `public.box.example.com/<project>/<slug>/` (below).

Expand Down Expand Up @@ -163,7 +163,7 @@ Install [claude-swap](https://github.com/realiti4/claude-swap) and add your acco
Setting up a fleet needs none of this, and a project never copies it: `cloud/` sits outside `docs/devteam/`. It is for an owner who wants their dev box in the cloud: one container (Render, fly.io or any Docker host with a disk) holding the repos, the tmux server where every supervisor and worker runs, and the hub, reached at a URL of the owner's (say `box.example.com`, plus `*.box.example.com`, since each dashboard keeps an origin of its own on a subdomain, and `public.box.example.com` serves what the owner publishes). `cloud/Dockerfile` says how to deploy it.

- **Claiming it.** On first boot the log prints a claim link, good for a day. Opening it makes a passkey (1Password or the browser saves it), and from then on only a passkey registered on the box opens it; more can be added from the hub's Passkeys link. Signing in is at `box.example.com/auth`, and passkeys belong to `box.example.com`: pick the box's name before claiming, since changing it invalidates them. Lost every one: `devteam-reclaim` in a shell on the box prints a new link. A box claimed before sign-in moved from `auth.box.example.com` to `/auth` needs that once too: its passkeys belong to the old name (the gateway's log says so).
- **The hub** is the box's home page, with a **Terminal** tab first (`term.box.example.com`): a terminal on the box's tmux server, where the owner logs in to `claude` and `gh`, clones a repo and starts its supervisor (the notes it prints say how), and switches between every session (`C-b s`). Each repo's dashboard is a tab, as on a laptop. The image carries its own copy of the hub page and of the terminal's page, because every agent can change a checkout or take a port: pages on the box's name and the `term.` name are only ever the gateway's, and an agent's page can exist only on an instance's name.
- **The hub** is the box's home page, with a **Terminal** tab first (`term.box.example.com`): a terminal on the box's tmux server, where the owner logs in to `claude` and `gh`, clones a repo and starts its supervisor (the notes it prints say how), and switches between every session (`C-b s`). The **Overview** tab comes next (every project on one page, with Run, Drain and Park for each fleet and for all of them), then each repo's dashboard as a tab, as on a laptop. The image carries its own copy of the hub page and of the terminal's page, because every agent can change a checkout or take a port: pages on the box's name and the `term.` name are only ever the gateway's, and an agent's page can exist only on an instance's name.
- **Notifications.** The gateway watches every Inbox itself and sends the hub's notifications by Web Push, so they arrive with no page open. Turn them on with the hub's "Notify me of new Inbox items", once per browser; tap that text afterwards to send a test notification to that device. On an iPhone (iOS 16.4 or later), open the hub in Safari, Share > Add to Home Screen, open it from there and sign in (it doesn't share Safari's sign-in), then turn them on. They reach the Lock Screen. Removing a passkey or running `devteam-reclaim` ends every device's notifications.
- **Dropping files on the terminal.** Drag files from your desktop onto the Terminal tab and each is uploaded to `/data/uploads/<date>-<random>/<name>`, then its path is pasted at the focused pane, as a native terminal does, so the operator or supervisor there picks it up; press Enter when you've said what to do with it. An image arrives as an attachment (Safari sends a HEIC photo as a JPEG; elsewhere it arrives as a path). If the terminal has lost its connection, the path stays on screen and pastes when it's back. Up to 512 MB a file and 4 GB in all, kept for 7 days. The name is reduced to letters, digits, `.`, `-` and `_`. Only the gateway writes there; agents can read it.
- **It's a general-purpose dev machine,** with no container per project. Everything outside `/data` resets on a deploy, so `$HOME` is the machine: languages and tools install per project with `mise` (it reads `.tool-versions`, and `.ruby-version`, `.python-version` and `.nvmrc` too; `asdf` is an alias for it), and global tools are in `~/.config/mise/config.toml`. The image holds only what needs root: build libraries, `ffmpeg`, Chromium, and Postgres 14, 16 and 17 with `pgvector`. `box-pg 16` starts another Postgres (17 is on the default port, the others on 5400 + major); `~/.box/boot.sh` is what starts at boot, the box's `brew services`. There is no Docker on the box. Expo works without it: `npx expo start --tunnel` for your phone, `eas build` for builds. The Heroku CLI is a global tool too, the standalone build pinned by version and checksum, with its own Node and only `heroku` on `PATH` (the npm one runs on whatever Node a repo pins, and refuses one older than 20). Log in once with `heroku login -i`, giving a token from `heroku authorizations:create --expires-in <seconds> --scope <scopes>` on your laptop as the password: the browser login fails, since the browser isn't on the box's IP. The token lands in `~/.netrc`, on the disk, readable by every agent on the box, so give it the narrowest scope and shortest life that does the job.
Expand Down
Loading
Loading