Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
79816da
kit-46: spec
hhff Nov 14, 2026
c600510
kit-46: plan
hhff Nov 14, 2026
2ce8e94
kit-46: spec review 1 and its fixes
hhff Nov 14, 2026
76be351
kit-46: plan follows the reviewed spec
hhff Nov 14, 2026
36f1b24
kit-46: spec review 2 (approve), plan refined
hhff Nov 14, 2026
013ae42
kit-46: plan review 1 and its fixes; box baseline; rulings
hhff Nov 14, 2026
4ba60a4
kit-46: plan review 2 and its fixes; clean box baseline
hhff Nov 14, 2026
e4c60cc
kit-46: plan review 3 (approve)
hhff Nov 14, 2026
55b51e7
kit-46 T1: devteam.mjs hub, the list's kit, the hub's log
hhff Nov 14, 2026
0d9fb7c
kit-46 T2: dashboards leave the hub to the image
hhff Nov 14, 2026
f57715b
kit-46 T3: the press checks, against the standalone hub
hhff Nov 14, 2026
04f7dfb
kit-46 T4: machine status names the hub's kit
hhff Nov 14, 2026
c54471a
kit-46 T5: README, the image's hub
hhff Nov 14, 2026
e95e26b
kit-46: task 2 review (approve)
hhff Nov 14, 2026
ffbb787
kit-46: hub line has one 3 s deadline; a slow non-hub is logged once …
hhff Nov 14, 2026
bda6057
kit-46: task reviews 1, 3, 4, 5 and dispositions
hhff Nov 14, 2026
4f348d1
kit-46: task reviews 1 and 4, pass 2 (approve)
hhff Nov 14, 2026
d3c5f40
kit-46: verification at 4f348d1 (redone after the merge with main)
hhff Nov 14, 2026
31b98c8
Merge remote-tracking branch 'origin/main' into ticket/kit-46-standal…
hhff Nov 14, 2026
a8c9b2f
kit-46: the hub is exempt from kit-201's DEVTEAM_RUN check, as the ma…
hhff Nov 14, 2026
4513dc5
kit-46: verification at a8c9b2f (main merged after kit-201)
hhff Nov 14, 2026
f218627
kit-46: a dashboard's hub names its kit only when the kit is the proj…
hhff Nov 15, 2026
efd8ea9
kit-46: final reviews 1 (approve) and 1b (changes, F1 fixed), disposi…
hhff Nov 15, 2026
bbed0e9
kit-46: final review 2 (approve efd8ea9)
hhff Nov 15, 2026
51d61a2
kit-46: final review 2b (track b cold confirm, approve)
hhff Nov 15, 2026
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
2 changes: 1 addition & 1 deletion 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. 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 commits on main (in every repo the project spans), laid out like GitHub's contribution graph. 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 does nothing in any project: 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. The Overview also has the dashboard's **Threads** dock, and there every thread is with the machine's **operator** (`process/operator.md`), the session that looks after the machine and not a repo: start one from the dock, or hold Shift and click a card, its name, a stat, a line that needs you or the headline (**Pin** does the same for a finger), and a numbered pin stays on what it is about. This is the one thing the hub's server keeps and the one thing it does: the threads are in `~/.devteam/operator/threads.jsonl` (on a box, `~/operator/threads.jsonl`), each of your turns is typed into the operator's session as one line marked as yours by way of the hub, and the operator answers with `operator-thread reply`, which the open card shows as text. On a laptop there is no operator until you write to one: your first message, or **Start the operator** in the dock, starts it in a tmux session of its own (`devteam-home`, window `operator`; `tmux attach -t devteam-home`), with this project's `agent_command` (or `DEVTEAM_OPERATOR_COMMAND`), `DEVTEAM_ROLE=operator` and `operator.md` as its prompt, and never a second one. The first time, Claude Code asks whether to trust that folder, and nothing answers that for you: the reply to your first message says so, the dock says the operator is at a dialog, and you answer it in that session. The session is the operator's whatever its window is called, and when tmux doesn't answer the hub starts nothing. A thread is talk with the operator, never an answer to a question: decisions stay in each project's Inbox. 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, and the hub takes a thread turn only from its own page (the browser's `Origin` and `Sec-Fetch-Site` must both say so). That keeps other sites out, not other programs: on a laptop anything running as you can post to the hub or to a dashboard, or type into a tmux pane, your own fleets' sessions included, so the operator reads a thread line as it reads its terminal and confirms with you anything only you may do. On a box you are never on the machine, and every agent runs as the hub's own user, so the hub takes a thread turn there only when the box's gateway signed it, with a key only the gateway's own user can read; anything sent from the box itself is refused. The operator checks each turn with `box-thread show`, which says which ones the gateway signed, and still confirms with you, in its own window, anything only you may do: an agent there can still type into its terminal. The same goes the other way: any agent there can write a reply as the operator's, so read the operator's replies as a report, as you read an alert.
**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. On a laptop the hub is whichever dashboard took the port first; `devteam.mjs hub` serves it alone, retrying while anything else holds the port (its log is `~/.devteam/hub-4320.log`). On a box whose image sets `DEVTEAM_IMAGE_HUB`, the image's own kit serves the hub and dashboards don't: a dashboard on this release or later leaves the port alone, and says so in `run/dashboard.log`. The gap left is a project still on an older kit, which can take the port in the moment the image's hub restarts, until that project adopts the release. 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 commits on main (in every repo the project spans), laid out like GitHub's contribution graph. 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 does nothing in any project: 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. The Overview also has the dashboard's **Threads** dock, and there every thread is with the machine's **operator** (`process/operator.md`), the session that looks after the machine and not a repo: start one from the dock, or hold Shift and click a card, its name, a stat, a line that needs you or the headline (**Pin** does the same for a finger), and a numbered pin stays on what it is about. This is the one thing the hub's server keeps and the one thing it does: the threads are in `~/.devteam/operator/threads.jsonl` (on a box, `~/operator/threads.jsonl`), each of your turns is typed into the operator's session as one line marked as yours by way of the hub, and the operator answers with `operator-thread reply`, which the open card shows as text. On a laptop there is no operator until you write to one: your first message, or **Start the operator** in the dock, starts it in a tmux session of its own (`devteam-home`, window `operator`; `tmux attach -t devteam-home`), with this project's `agent_command` (or `DEVTEAM_OPERATOR_COMMAND`), `DEVTEAM_ROLE=operator` and `operator.md` as its prompt, and never a second one. The first time, Claude Code asks whether to trust that folder, and nothing answers that for you: the reply to your first message says so, the dock says the operator is at a dialog, and you answer it in that session. The session is the operator's whatever its window is called, and when tmux doesn't answer the hub starts nothing. A thread is talk with the operator, never an answer to a question: decisions stay in each project's Inbox. 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, and the hub takes a thread turn only from its own page (the browser's `Origin` and `Sec-Fetch-Site` must both say so). That keeps other sites out, not other programs: on a laptop anything running as you can post to the hub or to a dashboard, or type into a tmux pane, your own fleets' sessions included, so the operator reads a thread line as it reads its terminal and confirms with you anything only you may do. On a box you are never on the machine, and every agent runs as the hub's own user, so the hub takes a thread turn there only when the box's gateway signed it, with a key only the gateway's own user can read; anything sent from the box itself is refused. The operator checks each turn with `box-thread show`, which says which ones the gateway signed, and still confirms with you, in its own window, anything only you may do: an agent there can still type into its terminal. The same goes the other way: any agent there can write a reply as the operator's, so read the operator's replies as a report, as you read an alert.

**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
23 changes: 23 additions & 0 deletions docs/devteam-dev/tickets/kit-46-standalone-hub/baseline-box.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Baseline on the cloud box at main 5c2a77b (no code change yet), 2026-11-14, run with the session's devteam env removed (m-20261114-214804017-supervisor):
env -u DEVTEAM_RUN -u DEVTEAM_HOME -u DEVTEAM_ROLE -u DEVTEAM_TICKET -u DEVTEAM_FEATURE -u DEVTEAM_SUPERVISOR -u TMUX -u TMUX_PANE TMUX_TMPDIR=$(mktemp -d) node --test test/hub-overview.test.mjs test/machine.test.mjs test/operator-threads.test.mjs
not ok 20 - the store: a thread, its turns, closed and opened again, kept beside the operator in no repo
not ok 21 - a box keeps them in ~/operator, on the disk that outlives a deploy
not ok 22 - who may write what: the owner starts, says and closes; only the operator replies; no session in a fleet reads or writes
not ok 23 - what is kept is checked: a turn's size, a title of one line, a subject of the Overview's own shape
not ok 24 - an owner turn becomes one line in the operator's pane, and only at its prompt
not ok 25 - on a box the operator is window 0 of home: typed into when it is there, never started when it is not
not ok 26 - on a box the owner's verbs take only a press the box's gateway signed, and show says which turns it signed
not ok 27 - a laptop's operator is started once: by the first words or by start, in its own session, with operator.md as its prompt
not ok 28 - start-once holds under a C locale, with the window renamed, and when tmux gives no answer
not ok 29 - settings that can't be read fail the start before its lock is taken
not ok 30 - inside tmux, list, show and reply are the operator's only from its own pane
not ok 31 - turns sent at the same moment each arrive whole: text then Enter, never text, text, Enter, Enter
not ok 33 - the hub takes the owner's turns only from its own page, within its caps, and starts the laptop's operator on the first
not ok 34 - on a box the hub takes a press only with the gateway's signature: a local program with the page's own headers is refused
not ok 36 - a start's note from a kit that did not name its tmux server is never taken for another session's pane
# tests 36
# pass 19
# fail 15
# skipped 2

[exited with code 0]
Loading
Loading