My/AI/Agent/Fleet
Properties
- see-also
- My/AI/Agent/Chief of Staff, tmux, herdr, Terminal/Multiplexer
Fleet
- The set of AI agent seats Person/codekiln runs at once, and the harness that holds them. A seat is a standing role — a chief over one repository, a director over a group of them — and it outlives whichever session is sitting in it, the way My/AI/Agent/Chief of Staff describes for Hayward.
- Since 2026/08/24 the harness is tmux. herdr is no longer the primary multiplexer here — its server was stopped that morning and its ~24 orphaned sessions killed — and it is not retired.
What tmux gives that herdr did not
- What herdr offered was agent-state detection on the pane border — blocked, working, done — read off screen patterns. What it could not offer was depth. A workspace held panes, and the agent name carried the whole address in 32 characters, so
<workspace-label>-<tab-label>had to encode both the repo and the work item inside that budget. - tmux gives five nested levels instead of two, each one named separately and none of them competing for the same 32 characters. The hierarchy is the address, so no single name has to carry it.
- One keystroke reads the whole thing back.
C-b wopens tmux/Command/choose-tree in tmux/Mode/Tree — see tmux/session/Picker for what the picker's own keys do.
- What herdr offered was agent-state detection on the pane border — blocked, working, done — read off screen patterns. What it could not offer was depth. A workspace held panes, and the agent name carried the whole address in 32 characters, so
The five levels
- tmux/session — one seat. Named
<agent>-<REPO>-YYYY-MM-DD-<day>-HHMM, and the tmux session name and theclaudesession name are meant to be the same string. The repository code says which checkout, soC-b wanswers what a seat is working on without opening it:LEGis this garden,MKthe work knowledge vault,GLthe langserve repo,DFMy/Dotfiles. Sessions are what tmux/Option/status-left draws, which is why tmux/Option/status-left-length matters here.- The scheme is volatile, so read it rather than quoting it. Five forms inside one week, three of them on 2026-08-24 Mon alone. A page that quotes a current session name is wrong within days, and a successor cannot tell a stale literal from a live one.
tmux display-message -p '#{session_name}'names the session the running pane is in;tmux list-sessionsnames every session on the server, which is a different question. A name captured at session start — the oneListAgentsreports — ages silently through a rename. - A rename leaves window and pane ids alone —
@0,%0and thewindow.paneindices are unchanged across one — so anything addressed by id keeps working. The old session name stops resolving the instant the rename lands, withhas-sessionreturningcan't find session, so it is only name-addressed targets that break, and they break immediately rather than drifting. That is the argument for reading the name live instead of caching it. - A
claudesession's own name cannot be changed while it runs, so after a seat's tmux session is renamed the two names disagree until the next My/AI/Agent/Fleet/Bed Down respawn, which derives the new one from the tmux name. - No dots in a session name. A
-ttarget is parsed assession:window.pane, so a dot anywhere in the name is read as a separator and every bare target fails against a window or pane that does not exist —has-sessionincluded, which is what a script would use to check.DFrather than.Ffor the dotfiles code for this reason: tmux/Q/Why does a session name containing a dot break every bare -t target?.
- The scheme is volatile, so read it rather than quoting it. Five forms inside one week, three of them on 2026-08-24 Mon alone. A page that quotes a current session name is wrong within days, and a successor cannot tell a stale literal from a live one.
- tmux/Window — one contractor's job. Named for the job, never for the agent doing it. The window list is the seat's docket, and
tmux list-windowsreads it back. A window closes when its item is done; a window left open reports work in flight that is not. - tmux/Pane — chat left, viewer right. The right pane is
nvimon the document being edited, orgh dashon a review queue. Created at 45% width:- sh
tmux split-window -h -p 45 -t '=<session>:<window>' 'nvim <file>' - Quote every
-targument. A bare=wordis a zsh expansion, not a tmux exact-match target — see tmux/Q/Why does a tmux -t =session target fail in zsh?.
- The named
claudesession — one context window.claude -n '<name>'records the name in the session's own transcript, which is how a session is addressed for messaging and how its context size is read without spending model tokens. See My/AI/Agent/Fleet/Bed Down. - The branch and git/worktree — where the work lands. The bottom level is what makes two windows able to edit the same repository at once.
git stashis the exception that ignores the boundary: the stash is repo-global across every worktree, so it is never used in a shared checkout.
- tmux/session — one seat. Named
Hiring a contractor
- A contractor is a session engaged against one job that can be finished. It takes a window of its own, is named after the job rather than after itself, and is gone when the job is done. A seat is the other thing: a standing office, named after the office rather than the work, which survives its occupants by handing off from one context window to the next. Hayward, Reeve and George are seats; the session that wrote four keyboard-shortcut pages one afternoon was a contractor. Calling both of them workers implied a seat was only a long-running contractor, which is backwards.
- Two things follow from the word rather than from any rule added on top of it. If you cannot say what finishing looks like, the work is not a contractor's job — it belongs to a seat. And you do not close out a contractor without taking delivery, which is why the pane gets read before the window is killed.
- One contractor is one window in the hiring seat's own session, and it reads its brief from a file:
- What a contractor is hired under is settled and does not need asking. Person/codekiln on 2026-08-25 Tue: "assume that if I ask any knowledge garden agent manager to hire a contractor, it should use claude permission mode auto, use the claude model most appropriate for the task, it should commit work when done but not push (and not bug me about pushing), and close the worker."
--permission-mode autois already in the command above. The model is the hiring seat's call, matched to the job rather than defaulted upward.- A commit is where the work stops. The contractor's output is committed and the push waits for Person/codekiln to say so in their own pane. An unpushed stack is not an item: it earns no display line, no question and no paragraph in a report. Say what is committed and stop.
- The window closes when the job is done. A window left open reports work in flight that is not. What holds a window open is Person/codekiln's own unsent keystrokes in its composer, which is why a pane is read before it is killed, as a separate step.
- The brief goes by path, never interpolated into the command. Content inlined into a double-quoted shell string has its backticked terms executed and silently replaced, which deletes exactly the measurements a brief exists to carry.
What the fleet expects of a seat
- Bed down at 200k tokens rather than letting a session balloon — My/AI/Agent/Fleet/Bed Down.
- Browser-shaped work takes
claude --chromefirst and Playwright only when that is unavailable, one browser task at a time across the fleet — My/AI/Agent/Fleet/Browser. - Absorb rather than relay. A question answerable by reading something is not an escalation; it reaches the human only when a person outside the fleet is waiting on it, or when it needs authority only the human has.
- Announce a permission request before Person/codekiln is asked for it. A dialog or a login prompt must never be the first they hear of it — on screen it names none of what they need, so a seat sends four facts up: who is asking, what permission, what it serves, and what happens if they decline. Where a wall is foreseeable — an SSO login, an expired credential, a push wanting a credential the seat does not hold — send them before hitting it. Where a dialog fires unforeseen, send them the moment it fires. The standard is not that every dialog is predicted; it is that nothing stays quiet once it has happened.
- The dotfiles seat keeps the fleet's pending-request list as the top section of its own display, above everything else, on the grounds that a permission request is the one class of item where the human is the only possible actor. An empty section is stated rather than deleted, so that its absence cannot read as an oversight.
- Prefer the answer that needs no grant, and never take a standing grant to serve one narrow lookup. A grant is not scoped to the query that motivated it: control of System Events given to the terminal application is given to every process that ever runs inside it, which on one machine is every seat in the fleet, for as long as the grant lasts. A read-only question about which applications are running is not worth that. Nor does a seat request a grant on another seat's behalf.
- Durable opinions go in the garden, not hardcoded into a repository, which is why this page is here and not in My/Dotfiles.
- A seat's Heads Up Display says the minute it was last swept, with the offset. A time a reader cannot place is worse than no time, and the fleet has already lost a document to times read off a UTC clock and narrated as local. In this garden that is
current-as-of::in the page's frontmatter, and it is the authoritative time; the body keeps a link to the day's journal, which seat swept, and from which context window, because a stamp answers none of those. - A convention is the requirement, not the spelling. "A display says the minute with an offset" travels between repositories; the name of the key carrying it belongs to whichever system stores it. A display in a repository of YAML records spells the key
current_as_of; this graph has some five thousand kebab-case properties, no underscore ones, and a rule in.claude/rules/logseq-core.mdtelling every agent to use kebab-case, so here the same convention iscurrent-as-of::. Neither is an exception to the other.- The error this guards against showed up twice in one morning on 2026-08-25 Tue, in different clothing each time. A Logseq namespace's on-disk
Book/<title>shape was relayed to another garden as a filing convention, when it is what this one system's namespaces look like on disk. Then a YAML key's underscores were relayed as the field name. Both times a property of one system's mechanism was passed on as though someone had decided it. Ask which part of a relayed rule someone chose and which part the storage chose.
- The error this guards against showed up twice in one morning on 2026-08-25 Tue, in different clothing each time. A Logseq namespace's on-disk
- Write so that Person/codekiln never has to resolve a reference — name the thing again rather than pointing back at it, and never let a count stand in for the facts it counts: My/AI/Rule/How to Communicate Effectively With Me/Never make the reader resolve a reference.
- Deliverables go on tracked paths, and whether
tmp/is one of them is a fact about the repository rather than about the fleet. Check it per checkout withgit ls-files tmp/, which lists what is actually tracked; do not carry the answer between repos.git check-ignoredoes not answer this — it reports nothing for a tracked file, with exit1, which reads the same as no rule matching at all, so--no-indexis needed to audit a pattern: git/Q/Why does git check-ignore report nothing for a tracked file?.- Where
tmp/is ignored, a secret scan that skips ignored paths is bypassed entirely bygit add -fon atmp/file. - Where it is not, there is no bypass and no privacy either: in this garden
.gitignorecarries notmprule, three notes undertmp/are tracked, and anything written there is published. Scan a working note for credential-shaped strings before committing it.
- Where
- Fetch before trusting an ahead-behind count against a repository another seat also pushes to — git/Q/Does an ahead-behind count against origin reflect the remote's current state?.