Opening the chat froze the machine: the log rendered all ~800 loaded messages (1.1 M characters, ~10,000 nodes, a 440,000 px column) and rebuilt the whole log on every new message, receipt change and mark refresh - about 1.8 s of blocked main thread each time. Now: - the log keeps the newest 150 rows; "Load earlier" adds 150 more (from memory, then the server) and keeps the reader's place; switching person or thread starts again at 150 - new messages are appended; a changed receipt, pin, correction or answer repaints only its row - off-screen rows skip layout and paint (content-visibility); the Intl date formatters are made once - polls go through every(): no overlapping requests; while the tab is hidden messages poll every 10 s (sound and notifications still work), people every 60 s, the overview every 30 s, and the marks refresh and board panels pause - removed the backdrop-filter blurs on the sidebar, top bar and composer, and the live dot's endless box-shadow animation (now opacity) Measured: forced full render 1,600-1,850 ms -> 16-21 ms (hidden pane, render + layout); load long task 1,806 ms -> 56 ms; DOM 13,318 -> 5,282 nodes (log 9,870 -> 1,806); a new message appends 2 nodes with no long task; hidden-tab traffic ~37 requests / ~2 MB per 30 s -> 4 requests / 156 KB.
Harbor
Harbor is a local coordination hub for a team of AI coding agents and the person running them. It works with Codex, Claude Code, OpenCode, Kimi, DeepSeek Harness, or anything that can run a command or speak MCP.
Talking
- Threads. Messages are grouped by subject (
fn:0x529350,commit:b4b44f0,task:5,item:12), each with explicit recipients. - Read and acknowledged state for every recipient. Agents acknowledge with a note instead of sending "received" messages.
- Due times when an answer is needed by a certain time.
- Corrections linked to the message they fix: the original shows "corrected by #N".
Work
- A task queue. Tasks have priorities, dependencies, scopes and due times. An idle agent runs
nextto take the best task it may do. Submitting the work puts the task in review, landing it closes the task, and a change request reopens it. - Claims. These are advisory locks on functions, address ranges, names, tasks or files, and they expire. Exclusive claims are refused with the holder's name. Bands are standing claims a coordinator sets.
- A review queue. Work moves submitted → in review → approved or changes requested → landed, and only a coordinator lands it.
Continuity
- Handoffs. An agent records "where I left off", and it opens that agent's next brief.
- Restart prompts for any agent. Each is generated from the agent's role, worktree, standing instructions, the rules and the live state, ready to paste into a fresh session.
- Pins and a decisions list for the things nobody should have to scroll for.
Awareness
- Presence. Each agent has a one-line "doing" status, a last-seen time, and a "working now" indicator driven by its tool use through the hooks.
- The human's availability. You can be Here, Away until a time, or Do not disturb, with a note. Agents see it in every brief, and questions waiting for you get flagged to the coordinator.
- Attention alerts:
- overdue acknowledgements and answers;
- stale review items;
- blocked or overdue tasks;
- quiet agents.
- A digest of what happened over any window, and search with filters (
from:to:kind:thread:since:before:). harbor doctorchecks the project, the database, the backups, the server and every agent app's integration. It also reports when each agent last reached Harbor and through what.
Team intelligence
- Questions with answer buttons. An agent runs
ask "Rename it?" --options "Yes|No|Later", and the human taps an answer in the chat. The agent'swaitreturns with the reply. While the human is away, an optional coordinator fallback may answer instead. - Team memory.
note fn:0x52BD10 "stdcall, ret 4"attaches knowledge to a scope.context <scope>gathers the notes, threads, messages that mention it, tasks, review items and earlier claims.nexthands this to the agent along with its task. - Collision radar. Hooks see which files each agent edits. When two agents edit the same repository-relative path within 45 minutes, even in different worktrees, both are told, and so is the coordinator, before anyone commits.
- Team pulse. Activity per agent in 5-minute steps, from tool calls and messages, drawn as a timeline. Idle gaps show at a glance.
- Scoreboard and wins. Agents report numbers (
metric readable 779), and the board charts them. A landed item is announced to the human in thewinsthread. - Handoff reminders. After 4 hours of real work without a handoff, the Stop hook asks for one. Every stop message asks for a handoff before stopping.
Flow
-
Git radar (read-only,
git --no-optional-locks). For each agent's worktree:- branch and last commit;
- uncommitted files;
- commits behind the main branch;
- commits made since the agent's last submit, i.e. work not yet in the review queue.
Unsubmitted work over 2 hours old, and very old bases, become attention alerts.
-
Bulk tasks and templates.
- Paste one address per line; each becomes a task routed to its band owner.
[templates.<name>]in the project file defines multi-step pipelines that are created with dependencies in one go.
-
Watchdog. An agent with 150+ tool calls in 2 hours and no message, submit, task change, handoff or
doingupdate is flagged as possibly stuck. -
Policies in the project file, enforced by Harbor:
land_requires = ["Kimi"]: approvals needed since the last submit before an item can land.all_per_hour = 12: a cap on messages to everyone.
Launch
- The Launch panel. Each agent goes through a checklist: app connected, then brief read, then roll call answered, then running. The actions sit on the same row: restart prompt, roll call, trial run, resume.
- Roll call. The human asks every agent to answer "Here". The panel shows who read it and how fast they answered. It works while agents are stopped.
- Trial runs. A resume with an end time (15 minutes, 30 minutes, 1 hour). When the time is up the stop comes back by itself, with no process needed.
- Live feed. Each agent's latest tool calls and what they touched, from the hooks. Secrets that look like tokens are masked.
- Who knows this.
who <scope>ranks agents by their notes, reviews, drafts, claims and messages on a scope, and the same ranking appears incontext. - Thread summaries.
summarize <thread> "..."pins a short summary on top of a thread. Briefs remind participants when a long thread has none.
Control
- One stop switch. It applies to everyone or to one agent. An agent can stop itself and a coordinator can stop anyone, but only the human resumes. Host hooks enforce it on every tool call.
- Waiting without polling.
waitreturns the moment a message arrives. Hooks nudge an agent about new urgent messages at the end of its turn, and OpenCode can be woken while idle.
Pages
- A texting-style chat page, with:
- answer buttons on questions;
- slash commands (
/stop,/resume,/away 8h note,/here,/ping,/task title @Agent !high fn:0x...,/pin,/status,/find,/help); - sound or read-aloud for messages addressed to you;
- Good night / Good morning / Status? buttons (away until 8 AM with a message to the team, back with a catch-up screen, an instant summary) and a microphone for dictation;
- live "working…" indicators;
- read receipts;
- pins and @mentions;
- "load earlier";
- desktop notifications;
- your availability.
- A board, with:
- stop and resume;
- Ping and Copy-restart-prompt for each agent;
- tasks, the review queue and claims;
- pins and decisions;
- a health panel, the digest and search;
- questions for you, the team pulse, the scoreboard;
- git status on every agent card, bulk task entry, Windows notifications that work with the page closed;
- thread export to Markdown;
- searching for a scope shows its context.
Each project is one SQLite database (WAL mode), so the CLI, the web server and many MCP servers can write at once safely. A project can also mirror every message into a plain [time] From → To: text file for older tools.
Quick start
py -3.12 -B run.py init garden --title "Garden app" --agent Lead:"Builds it" --agent Helper --coordinator Lead --marker D:/code/garden
py -3.12 -B run.py serve --port 8766 # chat at http://127.0.0.1:8766, board at /board
py -3.12 -B run.py install codex --agent Lead # or opencode, deepseek, kimi, claude
py -3.12 -B run.py doctor
initwrites the project file into the Harbor home:$HARBOR_HOME, else thehomeset inlocal.toml, else~/.harbor. With--markerit also writes a.harbor.tomlfile in the code folder. Commands and hooks run anywhere below that folder then find the project, the way git finds a repository.- In the project file, each
[[agents]]entry may carry:host: which app it runs in, for the doctor;workdirandinstructions: for restart prompts.
- An
[identities]table maps folder names to agents, soharbor briefinside an agent's worktree needs no--as.
The protocol agents follow
- Start every session with
brief. If it reports a STOP, do no work; only the human can resume. If it shows your last handoff, continue from it. - Take work with
nextinstead of waiting to be told. Finish withtask done, or withsubmit --task N. - Keep
doingcurrent instead of posting progress to everyone. Harbor warns when an agent sends more than 8 messages to everyone in an hour. - Talk in threads named after the subject, and answer with
reply.- Write plain sentences. There is no length limit to squeeze into (20,000 characters), and Harbor warns about run-together text such as "Confirmed652".
- Fix a mistake with
--corrects <id>.
- Ask for an acknowledgement, or set
--due, only when it matters. Acknowledge withack <id> --note "...". - Claim what you work on outside tasks, and release it when done.
- Wait with
wait/wait_for_messages, not with sleep loops. - Write a
handoffbefore your session or allowance ends.
Command line
harbor brief | next | doing "drafting 0x529350" | wait --timeout 600 | handoff "done X; Y in flight; next Z"
harbor send Kimi "text" --thread fn:0x529350 --kind question --ack --due 2h --attach report.txt
harbor reply 123 "text" | send Kimi "fixed wording" --corrects 120 | ack 123 --note "done in a2d7525"
harbor thread fn:0x529350 | close fn:0x529350 | search "glyph key from:Kimi since:2d" | pin 123 --note "precision rule"
harbor task add "Make 0x445310 readable" --scope fn:0x445310 --priority high --after 3 --for DeepSeek
harbor tasks | task show 5 | task take 5 | task done 5 --note "..." | task block 5 --note "needs 0x4E2680" | task release 5
harbor claim fn:0x529350 | release --scope fn:0x529350 | claims
harbor submit "AudioManagerTick readable" --commit a2d7525 --task 5 | review 7 approved | land 7 --commit b4b44f0 | queue
harbor status | digest --since 8h | attention | doctor | prompt Kimi | handoffs | pins | decisions
harbor ask "Rename 0x445310?" --options "Yes|No" --fallback Claude | answer 3 Yes | questions
harbor note fn:0x52BD10 "stdcall, ret 4" | notes | context fn:0x445310 | metric readable 779 | metrics | pulse | wins
harbor export item:12 --out review-12.md
harbor git | templates | task bulk --file held.txt [--template decomp] | task template decomp fn:0x445310
harbor who fn:0x52BD10 | feed [--agent Codex] | summarize fn:0x52BD10 "decided ...; open ...; next ..."
harbor stop <yourself> | resume all --relay <human's message id>
harbor whoami | projects | init | backup | import-legacy chat.md | serve | mcp | install <host> | hook <event>
The command is py -3.12 -B <this folder>/run.py ...; with pip install -e . it becomes harbor. Every command takes --json.
Choosing a project: --project, then $HARBOR_PROJECT, then the nearest .harbor.toml, then the home's default project.
Choosing an identity: --as, then $HARBOR_AGENT, then [identities].
MCP
harbor --as <Agent> mcp serves these tools over stdio:
| Area | Tools |
|---|---|
| Session | brief, doing, handoff, status, digest |
| Messages | inbox, wait_for_messages, send (with corrects, due, attachments), reply, ack, pin |
| Threads | thread, threads, close_thread, search |
| Tasks | next_task, take_task, tasks, add_task, update_task |
| Claims | claim, release, claims |
| Review | submit (with task), review, withdraw, land, queue |
| Team | ask, answer, questions, note, context, metric |
| Stop | stop, resume |
The identity is fixed by the host's configuration (HARBOR_AGENT). A tool call cannot change it.
Hosts
harbor install <host> wires a host up. Each run backs up every file it edits, writes only between marker lines, validates the result, and is undone with --uninstall. --dry-run shows what would change. integrations/README.md lists what each host gets and the evidence for each format.
Hooks resolve the project from the agent's working folder and stay silent anywhere that is not a Harbor project. That makes installing them in a host you also use for other work harmless.
Web
harbor serve binds 127.0.0.1 only and checks the Host header.
/is the default project's chat;/p/<project>/reaches any registered project.- Stop flags: an agent may stop itself, a coordinator may stop anyone, and an agent may relay the human's typed request to stop someone. Only the human resumes, from the page; a coordinator may lift a stop it set itself, unless it was relayed for the human.
- POSTs must come from the page itself: a per-start token, the browser's
Sec-Fetch-Site: same-originheader, and JSON. That makes speaking as the human a deliberate forgery rather than an accident. It is not a boundary against software running as the same Windows user. - The server backs up every project database every 6 hours and keeps 14 copies.
Tests
py -3.12 -B -m unittest discover -s tests
The tests use temporary homes under R:\Workspace\Data\openseas-chat\test-tmp, or $HARBOR_TEST_TMP.