0.74.0
A box’s mains show their kind on the ally card and its tower runs read as runs; every agent kind has its own icon on the page and its own glyph in Herdr’s sidebar; and a box’s tower opens the project’s integrator itself when a car slip arrives, an ally included.
Kinds and tower runs on the ally view
Section titled “Kinds and tower runs on the ally view”Todo #150, from the boss’s question on 11/10 about a box that runs a coordinator, an integrator and trains: the page’s ally view drew none of them by kind, and the tower’s runs read as agents that opened and closed by themselves.
- A box’s mains show their kind on the ally card. Each row of a unit’s Mains fold now says coordinator, integrator, standing, or train between the name and the state, in the words the local page uses (the same in every theme; the icons follow below). A main whose pane names no kind draws no word.
- On the wire. An entry of the
mainsin a box’sunit-statusgains an optional seventh field,kind, at most 16 bytes. The box writes it only to a controller whose freshcapslist the newunit-status.kindfeature besideunit-status.mains; every other controller gets the six-field entries of before, so an older device reads what it read. The list is still the mains the controller may stop, not a wider one. The spec’s “the ally board carries no role and no kind” is amended (agent kinds §12). - A tower’s run is not a unit. A tower wakes a short run for each message and stands by after it. A tower unit in its
runningorwakingstate now reads run in progress, with a tooltip that says it stands by between runs; running stays for a plain unit’sworking. On a box’s operate base the finished runs (an ally run, an event run, the tower’s own) are a closed fold, Tower runs, beside the agents’ Earlier runs, and never rows among the agents.
Icons by kind
Section titled “Icons by kind”Todo #152, from the boss’s 11/11 order that the different kinds of agent be told apart by their icons, on the page and in Herdr’s plugin. It reverses agent kinds D9’s “icons stay by position”, recorded as D16.
- The page. Every place that drew a unit’s role icon now draws its kind’s: a coordinator a ship’s wheel, an integrator two arrows joining, a standing main a lighthouse, a train the locomotive it already had, and a launched worker, scout, planner, reviewer or watchdog a wrench, binoculars, a pencil and ruler, a magnifier or a hound. That is the map’s cards and tokens, the zoomed base, the drawer, the dossier, an alert’s face, the chat avatar, and the row of a box’s main on the ally card (from the kind the box sends). The tower and the head tower keep their icons, and a unit with no known kind keeps its position’s icon (main, member, subagent). The symbols come from the same game-icons.net set and credit as the others (
gui/ICONS.md); Raven Keep and Geek draw them too (Raven has no twin for a kind, as for the train). - Where a launched subagent’s kind comes from.
Unit.dutynow also carries the kind of a subagent a pi-herdr-agents launch started, read from the agent role its launch script names (PI_SUBAGENT_AGENT;visual-testerandadversarial-reviewercount asreviewer). A role sb does not know, or a launch script that is gone, leaves it a plain subagent. Its card now says the kind word (worker, scout, planner, reviewer, watchdog) where it said subagent, as the spec’sroleWordalready did for a unit that has a kind. The operate sanitizer lets the worker kinds through. - Herdr’s sidebar and the tower’s dashboard.
sb_markis now one Nerd Font glyph per kind, checked against Nerd Fonts 3.5.1’s glyph names: coordinator nf-md-ship_wheel U+F0833, integrator nf-fa-code_fork U+F126 (as before), standing nf-md-lighthouse U+F09FF, train nf-fa-train U+F238 (as before), worker nf-md-wrench U+F05B7, scout nf-md-binoculars U+F00A5, planner nf-md-pencil_ruler U+F1353, reviewer nf-md-magnify U+F0349, watchdog nf-md-dog U+F0A43. A coordinator’s star is gone: a main whose kind is unknown (an older sb) keeps it, a member keeps the person, and[ally]stays. A launched subagent’s mark is its kind’s glyph alone; one with no kind keeps[sub] <launcher>. The dashboard shows the same glyph before each row’s name. - Your sidebar rows. If your
[ui.sidebar.agents]rows bold the marks, add rules for the two new main glyphs (\U000F0833,\U000F09FF) to the snippet in Sidebar net view; a subagent’s kind glyph now shows where[sub]was hidden. - The demo (
npm run gui:demo,gui:dev) gives its units kinds, and?kindsadds a base with a unit of every kind.
The tower opens the integrator
Section titled “The tower opens the integrator”Todo #151 (the boss’s choice of 2026-11-10: the box’s tower opens the integrator itself, and an ally sends only the car slip). On a shared box, an ally’s main had no integrator to own its trains: the ally run’s boss_open_main for one was refused, since a sender gets one opened main. That limit is not widened. The tower now opens the project’s integrator itself, on a car slip.
- One door:
boss_open_mainwithrole: "integrator", in a run of the sb server, with the slip as the request and its sender. The server finds the slip itself, among thecall_towermessages (why=shared) the run took from that sender, by thehead:the request carries (a run is shown no event id, so theforward: <event id>of 0.73 could not be followed by itself). It finds the project’s integrator or opens it, and forwards the slip as its sender wrote it, stampedfwdandfwd-from; nothing the run typed is forwarded. - The tower’s own main. The integrator’s pane is marked
sb_opened_by=integ:<project>and its tab and session are namedinteg <project>, so the new pi is of kind integrator before it is handed anything. It counts toward neither the workspace’s two opened mains nor its sender’s one. A tower opens at most 2 integrators in its workspace. - The project is the checkout’s. The opener reads it from the workspace’s checkout:
integration.projectat the top ofapps.json, else the folder name of the repository (every worktree of it reads the same). Another word from the run is refused, so a slip cannot be sent to another project’s integrator, one app cannot get two integrators under two names, and made-up names cannot use up the workspace’s two places. An integrator that works in another repository takes no slip, whatever its name; an ally run’s open stays inside the project its team binds here. - Never two for a project. An app whose trains a ledger already keeps under another project (
trains/<project>/<app>.jsonin the state folder) is refused with that project’s name. A live integrator of the project anywhere on the computer takes the slip (one a tower marked, or a main of kind integrator namedinteg <project>). A main opened for a teammate’s device is never one, whatever name and kind it carries: its pane token keeps it out, and the route table refuses a slip for it. A marked pane whose pi has not joined, a marked main of another kind, and two mains that both answer as the integrator each refuse the open and say which. The opens of one project wait for each other across every tower, so two workspaces that take a slip at the same moment open one. The open is kept in no ledger: after an idle integrator closed, the next slip opens it again. - Integrators you already run stay. The one-per-project rule is for the integrators the door opens. Where a project already runs integrators under names of their own (one per app, such as
integ FE2andinteg v3in one checkout: live mains of kind integrator in the tower’s workspace or working in the checkout, unmarked by a tower and not opened for a teammate’s device), the door opens nothing, counts none of them, and picks none. It answers with their names and the slip’s event id, and the tower forwards the slip as in 0.73:tower_replyto the integrator that runs that app’s trains, withforwardnaming that id, so the code stamps its sender. This is also the first time that forward can name its event: in 0.73 a run had no way to read the id. An ally run is given no id and forwards no slip there. The kept integrators are looked for before the ledger rule, so a ledger kept under one of their own names (trains/FE2/<app>.json) refuses nothing. The forward sends the event’s own text, not the message the tower typed, and only to an integrator the door named for that slip; a slip of a main opened for a teammate’s device is not forwarded. - Where a kept integrator sits. It leaves no durable sign, so the door sees it only in the tower’s workspace or working in the checkout. When the project’s ledger exists and no integrator of it is seen, the door looks once more after 2 s, and then opens one only when no unmarked main of kind integrator is alive anywhere on the computer; otherwise it refuses and names that main, so no second integrator opens on a ledger while the first restarts or sits elsewhere.
- An ally’s car slip. The
call_towerof a main an ally run opened reaches the ally runs of its device, never a local run. There the same call is the ally run’s one door to an integrator: the server takes it only for a slip this run took from a main opened for its own device, and refuses everything else before the gate. Belowdispatchit needs the boss’s confirm, and the request has to be the slip exactly as it came, so the confirm shows the text that is forwarded; a call the opener would refuse (a project that runs its own integrators, a checkout outside the team’s project) is refused before the boss is asked. When such a main’s slip reaches a local run instead (it named another workspace’s tower, or its route was gone), the door refuses it by the main’s pane token or any workspace’s route table. The integrator gets no ally route, so its reports and trains go to the local runs; the forward also carriesorigin=remote:<device id>. - Refused. A request with no slip, a slip of an app with no
integrationin the workspace’sapps.json, a project word that is not the checkout’s, a teammate’s device as the sender, the merge train off, androle: "integrator"outside a run of the sb server. - Prompts.
tower-run.mdsends a car slip through the door, forwards it withtower_replyand the door’s event id where the project runs integrators of its own, and no longer tells the tower to open an integrator as a main and have it callboss_duty;ally-run.mdsends a slip of a main it opened the same way and answers a request to open an integrator with that.
To move: nothing. Integrators you opened yourself keep their slips: several under names of their own (one per app) get them by the tower’s forward, as before, and a single main named integ <project> (or one that took a broken integrator over) gets them from the door when it works in the project’s repository. Only when you want the door to open the integrator of a project whose ledger is kept under another name than the repository’s folder, add "integration": { "project": "<that name>" } at the top of that project’s apps.json; until then that open is refused and names the ledger’s project.